A Tour of Android and some of it s APIs Bryan Noll
Me professionally
A good starting point http://neidetcher.blogspot.com/2009/07/android-presentation-from-denver-open.html The OS The VM Basic Views Basic Layouts Basic Activities
Building Blocks Activities Services Broadcast Receivers Content Providers The AndroidManifest.xml file
Activities An activity presents a visual user interface for one focused endeavor the user can undertake. You ll have a WhateverItIsActivity.java class for each screen/ page/view in your UI, and this class will be coupled to a view template defined in xml. Programmatic APIs exist such that you can dynamically add to the view hierarchy. Think Swing or GWT or some such UI tool.
Services A service doesn't have a visual user interface, but rather runs in the background for an indefinite period of time. Like activities and the other components, services run in the main thread of the application process. So that they won't block other components or the user interface, they often spawn another thread for time-consuming tasks.
Broadcast Receivers A broadcast receiver is a component that does nothing but receive and react to broadcast announcements. Many broadcasts originate in system code - for example, announcements that the timezone has changed, that the battery is low, that a picture has been taken, or that the user changed a language preference. Applications can also initiate broadcasts - for example, to let other applications know that some data has been downloaded to the device and is available for them to use. There is no UI directly associated with these guys, but they can cause one to be shown. Think fancy version of the Observer pattern.
Content Providers A content provider makes a specific set of the application's data available to other applications. The data can be stored in the file system, in an SQLite database, or in any other manner that makes sense. You define a ContentProvider subclass to expose your data to others using the conventions expected by ContentResolver and Cursor objects. This means implementing 6 abstract methods: query(), insert(), update(), delete(), gettype() & oncreate
AndroidManfest.xml Every application must have an AndroidManifest.xml file (with precisely that name) in its root directory. The manifest presents essential information about the application to the Android system, information the system must have before it can run any of the application's code. Names the Java package for the app, describes the components (activities, services, receivers), declares required API permissions, declares Android API that the application will work with.
Detecting an incoming text message - the manifest When the app gets installed, it will need to request permission from it s host environment in order to become aware whenever the system receives an SMS message. We tell the app to request the permission that must be granted for it to work properly in the manifest. <uses-permission android:name="android.permission.receive_sms" />
Detecting an incoming text message - the receiver Implement a BroadcastReceiver. public class SmsReceiver extends BroadcastReceiver { Override onreceive @Override public void onreceive(context context, Intent intent) {
Detecting an incoming text message - getting it Get the SMS message (we re still in the BroadcastReceiver.onReceive implementation) Bundle bundle = intent.getextras(); SmsMessage[] msgs = null; if (bundle!= null) { Object[] pdus = (Object[]) bundle.get("pdus"); msgs = new SmsMessage[pdus.length]; String[] originatingaddresses = new String[msgs.length]; String[] textsreceived = new String[msgs.length]; for (int i=0; i<msgs.length; i++){ msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]); String originatingaddress = msgs[i].getoriginatingaddress(); originatingaddresses[i] = originatingaddress; textsreceived[i] = msgs[i].getmessagebody();
Detecting an incoming text message - wait what? How, exactly, can we count on intent.getextras() to return us a useful bundle that can then actually return to us some useful pdus when we say bundle.get( pdus ). Back to the manifest. <receiver android:name=".smsreceiver"> <intent-filter> <action android:name= "android.provider.telephony.sms_received" /> </intent-filter> </receiver>
We ve got the text message, now what? Start a service that will deal with this SMS message in whatever way we want. (Still in onreceive of our BroadcaseReceiver) Intent pretextserviceintent = new Intent(context, PreTextService.class); pretextserviceintent.putextra( C.ORIGINATING_ADDRESSES, originatingaddresses); pretextserviceintent.putextra( C.TEXTS_RECEIVED, textsreceived);
Dealing with the text message - the service in the manifest Configuring it in the manifest in our case is as simple as <service android:name=".pretextservice" />
Dealing with the text message - the service Implement a service public class PreTextService extends Service implements LocationListener Set up the objects you need (NotificationManager, LocationManager, DBAccessor, etc.) here: @Override public void oncreate() { Do the work here: @Override public int onstartcommand(final Intent intent, int flags, int startid) {
The service, get location change updates http://developer.android.com/reference/android/location/locationmanager.html private LocationManager locationmanager; this.locationmanager = (LocationManager) getsystemservice(context.location_service); this.locationmanager.requestlocationupdates( LocationManager.GPS_PROVIDER, 0, 0, this); this.locationmanager.requestlocationupdates( LocationManager.NETWORK_PROVIDER, 0, 0, this); The method defined by the LocationListener interface: public void onlocationchanged(location location) { When you re timer is done this.locationmanager.removeupdates(this);
Location updates in the manifest All of the previous code will only work if we update the manfiest like so: <uses-permission android:name="android.permission.access_fine_location" />
Location updates - getbestprovider is not your friend http://developer.android.com/reference/android/location/locationmanager.html Supposedly Returns the name of the provider that best meets the given criteria. Criteria criteria = new Criteria(); criteria.setaccuracy(criteria.accuracy_fine); public String getbestprovider (Criteria criteria, boolean enabledonly)
Notify your user - setup This is all happening in the Service subclass. private NotificationManager notificationmanager; this.notificationmanager = (NotificationManager) getsystemservice(context.notification_service);
Notify your user - declare your intent Long time = System.currentTimeMillis(); Intent newintent = new Intent(PreTextService.this, PreTextActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); newintent.putextra(c.notification_id, time.intvalue()); newintent.putextra(c.ice_id, iceid); PendingIntent pendingintent = PendingIntent.getActivity(PreTextService.this, 0, newintent, PendingIntent.FLAG_UPDATE_CURRENT);
Notify your user - actually notify them Notification n = new Notification( R.drawable.underwood_small_black_text_white_bg, "PreText", time); // time is System.currentTimeInMillis String notificationcontenttext = "A message was intercepted."; if (shouldautorespond) { PreTextService.this.autoRespond(originatingAddress); notificationcontenttext = "A message was responded to."; n.setlatesteventinfo(pretextservice.this, "New PreText", notificationcontenttext, pendingintent);
Notify your user - ditch the notification public class PreTextActivity extends Activity { private NotificationManager notificationmanager; @Override public void oncreate(bundle savedinstancestate) { this.notificationmanager = (NotificationManager) getsystemservice(context.notification_service); @Override protected void onstart() { Bundle extras = getintent().getextras(); if (extras!= null) { if (extras.containskey(c.notification_id)) { int notificationid = extras.getint(c.notification_id); if (this.notificationmanager == null) { this.notificationmanager = (NotificationManager) getsystemservice(context.notification_service); this.notificationmanager.cancel(c.pretext, notificationid); Thursday, November 11, 2010
The Map - show it in the most basic possible way // The z field specifies the zoom level. A zoom level of 1 shows the // whole Earth, centered at the given lat,lng. A zoom level of 2 shows // a quarter of the Earth, and so on. The highest zoom level is 23. // A larger zoom level will be clamped to 23. int zoom = 10; String url = "geo:" + this.latitude + "," + this.longitude + "?z=" + zoom; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); this.activity.startactivity(intent); The problem. No way to put a pin on the map. At least not that I could find.
The Map - show it in a slightly more painful way that works Intent intent = new Intent( this.activity, PreTextMapActivity.class); intent.putextra(c.latitude, this.latitude); intent.putextra(c.longitude, this.longitude); this.activity.startactivity(intent); OK. But now I had to implement a custom Activity.
The Map - the custom Activity public class PreTextMapActivity extends MapActivity { private MapView mapview; private MapController mapcontroller; private GeoPoint geopoint; @Override protected void oncreate(bundle savedinstancestate) { this.mapview = (MapView) findviewbyid(r.id.mapview); this.mapview.setbuiltinzoomcontrols(true); this.mapcontroller = this.mapview.getcontroller(); addlocationmarker();
The Map - adding the pin public class PreTextMapActivity extends MapActivity { private void addlocationmarker() { if (this.haslocationreading) { // add a location marker MapOverlay mapoverlay = new MapOverlay(); List<Overlay> overlays = mapview.getoverlays(); overlays.clear(); overlays.add(mapoverlay); What s this overlay thing?
The Map - the overlay private class MapOverlay extends Overlay { // An inner class of the map activity @Override public boolean draw(canvas canvas, MapView mapview, boolean shadow, long when) { super.draw(canvas, mapview, shadow); // convert the GeoPoint to screen pixels Point screenpoints = new Point(); mapview.getprojection().topixels(geopoint, screenpoints); // add the marker Bitmap bmp = BitmapFactory.decodeResource( getresources(), R.drawable.mappin_text); int todeductfromx = MAPPIN_PIXELS_WIDTH / 2; int todeductfromy = MAPPIN_PIXELS_HEIGHT; canvas.drawbitmap(bmp, screenpoints.x - todeductfromx, screenpoints.y - todeductfromy, null); return true;
The Map - the view <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.mapview android:id="@+id/mapview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:enabled="true" android:clickable="true" android:apikey="04fgs1xvbeihegmuwd4fujhjvwz7ba1qgm-8pbw" /> </RelativeLayout> Note the mapview id and the apikey.
The Map - sign up for a key
The Map - thanks for signing up for a key
The Map - and finally the manifest <uses-library android:name="com.google.android.maps" />
Contact API - the basics, looking up a name public static String getcontactdisplayname(string originatingaddress, Context context) { // context is a android.content.context (or this from Activity) Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(originatingAddress)); String[] projection = new String[] { PhoneLookup.DISPLAY_NAME ; String selection = null; String[] selectionargs = null; String sortorder = null; Cursor c = context.getcontentresolver().query(uri, projection, selection, selectionargs, sortorder); String displayname = null; if (c!= null && c.movetofirst()) { displayname = c.getstring(0); c.close(); if (StringUtils.isEmpty(displayName)) { return originatingaddress; else { return displayname;
Contact API - the manifest <uses-permission android:name= "android.permission.read_contacts" />
Dial this number for me public class MakePhoneDialClickListener extends BaseOnClickListener { private String phonenumber; public MakePhoneDialClickListener(String phonenumber, Activity activity) { super(activity); this.phonenumber = phonenumber; @Override public void onclick(view v) { vibratephone(); String url = "tel:" + phonenumber; Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url)); this.activity.startactivity(intent);
Speak to me private void speak(final String texttospeak) { this.texttospeech = new TextToSpeech(this, new OnInitListener() { ); @Override public void oninit(int status) { speakdo(texttospeak); private void speakdo(string texttospeak) { if (this.texttospeech!= null) { String val = texttospeak; if (StringUtils.isEmpty(textToSpeak)) { val = "empty message"; this.texttospeech.speak(val, TextToSpeech.QUEUE_FLUSH, null);
The Market Don t make the mistake of making it free if you ever want to not make it free. (package="com.csc.pretext.app") Don t count on a mechanism for a great feedback loop with your reviewers. Get a Google Merchant account, and don t count on a lot of clarity and support for nonobvious events.
That s It The End Where To Now?