Showing posts with label android. Show all posts
Showing posts with label android. Show all posts

Wednesday, April 10, 2013

Android Push Notification with GCM



Push notification is a mechanism to solve to common issues found in mobile application development. A common use is to inform the user that an event has occurred on the server which may require the user to interact with the application. Another use is to update local state or inform the application to request updated state from the server.


The Android emulator is capable of receiving push notifications through GCM, however, it does require an additional step of actually logging a valid Google account into the emulator when it is running. This will need to be done for each emulator that you wish to use with push notifications through GCM.



Google Cloud Messaging

Google provides a service called Google Cloud Messaging (GCM) to provide push notifications for the Android platform. The developer console provided by google is accessed with a google account. Project management can be delegated to additional users, so it is good form to create the project under the account which will also be used in conjunction with releasing the application and delegate functionality to developers.



1. Create Project

Log into the Google APIs console (https://code.google.com/apis/console) to create a project. The “Overview” will display the project number. This is how client applications will identify themselves to the GCM service, in this case the Android application that will be using the GCM library.



2. Add GCM Service

Select the “Services” menu item. A list of Google provided services is presented. Enable the “Google Cloud Messaging for Android” by toggling the switch to on. This enables the service for the project.



3. Grand API Access

Select the “API Access” menu item. There will already be an existing “Key for browser apps (with referers)” which can be ignored. Click the “Create new Server key...” button. Enter the IP or subnet which will be allowed to send push notifications to clients. The “API Key” will be used by the server to authenticate it is allowed to send push notifications to the mobile devices.



Android Application

Adding push notification to an existing application is a straight forward task of adding the GCM library and updating the manifest. Once push notification is added to the application the developer is free to implement management of the registration and registration ids and handling the actual push notifications.



1. Download GCM Package

Using the Android SDK Manager, select the “Google Cloud Messaging for Android Library”, and install. This will download the library into $ANDROID_SDK/extras/google/gcm. Copy $ANDROID_SDK/extras/google/gcm/gcm-client/dist/gcm.jar to your project's $PROJECT/libs directory. Refresh the libs directory within Eclipse and add the gcm.jar to the build path.



2. Update Android Manifest

Several additions need to be added to the AndroidManifest.xml file to enable push notifications through GCM.



A new permission PACKAGE.permission.C2D_MESSAGE, where PACKAGE is the project's package needs to be defined and added. Additionally the RECEIVE and WAKE_LOCK are dependencies from the GCM library.


<!-- gcm permissions -->
<permission
    android:name="PACKAGE.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="PACKAGE.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

Under the Application element add a service, which is created further along in the process. Replace PACKAGE with the project's package. Note, there appears to be a limitation that this service must reside in the root java package of the project. Attempts to change this caused GCM to stop functioning, probably based on some reflection aspects being used within the GCMBaseIntentService class.


<application...>
    <service android:name="PACKAGE.GCMIntentService"/>

Also under the Application element add the boilerplate for the a broadcast receiver which is used by the GCMIntentService which was defined above. Replace PACKAGE with the project's package.


<application...>
    <receiver
        android:name="com.google.android.gcm.GCMBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND">
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="PACKAGE" />
        </intent-filter>
    </receiver>

3. Hook GCM Registration

At some point the application will need to register itself with the server which is generating push notifications. In the case of applications which require login this is usually after the user has been authenticated. The logout process should always include mechanism to handle clearing the registration id from the server. Otherwise, applications which do not require the user to log in can register when convenient.



The GCM library provides a couple functions to verify the environment for the application is correctly setup. These methods will cause the application to log the error and exit. They can be left in for production, but are not necessary.


GCMRegistrar.checkDevice(context);
GCMRegistrar.checkManifest(context);

A registration id uniquely identifies the phone to the GCM servers for a project. Before requesting registration the application can check if there is a current registration id available. If the registration id exists the application can skip requesting a registration id. Otherwise a registration request is sent with the application's project id which will set into the motion a request to the GCM service for a registration id and completing with a call to the onRegistered method on the GCMIntentService.


String registrationId = GCMRegistrar.getRegistrationId(context);
if (registrationId.equals("")) {
    Log.d(getClass().getSimpleName(), "registering");
    GCMRegistrar.register(context, context.getString(R.string.project_id));
} else {
    Log.d(getClass().getSimpleName(), "registrationId: " + registrationId);
}

Unregistering an application from the GCM service will set into motion the handling of clearing the registration id from the GCM server and ending in the call to the onUnregister method on the GCMIntentService.

GCMRegistrar.unregister(context);



4. Extend GCMBaseIntentService

The interface to the GCM service is handled through implementing the abstract methods of the GCMBaseIntentService. There are registration methods, error handling methods, and receive message methods.



The registration id should be sent to the server which will be sending push notifications. That value will be used by the server to direct push notifications to the particular device. The only authoritative source for the registration id should are calls to GCMRegistrar.getRegistrationId and this value should not be stored locally to determine if the registration id is set. During the unregistration process the registration id should be cleared on the server.


@Override
protected void onRegistered(Context context, String content)
{
    Log.d(getClass().getSimpleName(), "onRegistered: " + content);
}

@Override
protected void onUnregistered(Context context, String content)
{
    Log.d(getClass().getSimpleName(), "onRegistered: " + content);
}

5. Push Notifications

The GCMIntentService is also the entry point for the push notification. The data will arrive within an Intent as an extra as a JSON encoded String. This string can be parsed and the data extracted.



@Override
protected void onMessage(Context context, Intent intent)
{
    String content = intent.getStringExtra(CONTENT);
    Log.i(getClass().getSimpleName(), "content: " + content);

    JSONObject json = new JSONObject(content);
    // ...
}

Since push notification data arrives asynchronously to the application UI thread, it is necessary to implement a mechanism to handle the data without a direct reference to an Activity. The application itself may not have any active running activities.



Example Application

The example application (https://github.com/crazydays/scratchpad/tree/master/dealer) uses a queueing message service which is bound by the GCMIntentService for when receiving data. The lifecycle of the GCMIntentService requires that the message queue service store messages when it stops and load them again when started. The application's activity binds and registers for notifications for when data arrives. Then the data is published to the web view if it has been initialized.


Wednesday, November 14, 2012

Android Free and Non-Free Builds

Anyone who has browsed around the iTunes store or Android marketplace is familiar that many of the applications have a free and non-free version.  While it would certainly be possible to maintain two complete branches of code, anyone who has done software development for more than trivial projects knows that duplication is a Bad Thing (tm).  The Android SDK provides a mechanism to build multiple projects based on a single library without duplicating all of the code.

See iOS Free and Non-Free Builds also.

Create Library Project

The odds are you already have a project you are planning on turning a free and non-free version.  It is probably safer to start with a new project to figure out any of the details before jumping in with your current project.  To just jump in with an existing project skip this step and go into the Android properties for the project and set the project as a library.

Android will have you create multiple projects to accomplish multiple build targets.  For organizational purposes create a root directory for the over all product, in this case 'A Example'.  Under that directory create the 'library' project first.

Follow through choosing the options for the project until the project is created.

Create Application

The next step is to create the application which will use the library project created in the previous step.  This project will need a different package, usually a subpackage of the library project.
On the next screen skip adding an activity.  The activities will be used from the library project.  After finishing this step project will be created.
In the project properties on the Android tab click the Add button.  Select the library created in the first step.
 

Create Free Application

Follow the same procedure to create the application for the free version, choosing another unique subpackage of the library project.  At this point there should be three projects: library, application, and free application.  It would be good practice at this point to create a test project, but it will be skipped for this post.  Be aware that the test project needs to be built for one of the applications rather than the library directly.
 
 

Update Manifests

Perhaps my biggest complaint for Android's mechanism for creating multiple releases is the necessity to duplicate manifest.  The manifest in the library project is unused, but I like to keep it updated anyway as it it were a full project.

One limitation to note about the manifests in the application and free application project is that
android:name attribute of the activity elements must be the full package and class name from the library project.  The shorthand .ActivityClassName does not work since it would look for the class in the current project's package.

Update Layout

To demonstrate separate targets add a label and button to the main activity layout.

Resources

All of the resources are loaded in a heirarchacal fashion, checking the application project directory first and then falling back to the library project.  This allows customization of the assets required for the application or using shared assets from the library project.  One limitation for the library project is that all resources it references must be setup in the library project, even if you plan on all of the application projects overriding it.

The library res/strings/values.xml has the greeting value set to "Hello, Library!" and the doit value set to "DOIT".  It can only be seen on the eclipse graphical layout the project is a library and not an application project.

The application res/strings/values.xml has the greeting value set to "Free stuff is overrated." and the doit value set to "Gloat".

The free application res/strings/values.xml has the greeting value set to "Free stuff is cool." and the doit value set to "Whine".

Functionality

Changing functionality for a free version can be done in a couple ways.  Each mechanism has advantages and disadvantages, and in all likelihood a combination of techniques will be used.

Resources

The free version of an application could remove widgets from application layouts which expose the pay only options.  Then the library activity classes would check for the existence of those widgets before setting up them up.  The non-free functionality would then not be exposed.  The downside to this having multiple layouts defined for an activity, depending on the application, with a risk of duplication.

Programmatically

Android creates an R class in the project's defined package.  A simple helper class can be used to attempt to load the R class to determine the application project.

Then the class can be referenced to determine which features need to be enabled programmatically.  This does add complexity to the code with if statements to enable or disable behaviors.

Resources






Friday, December 9, 2011

Google Maps API on Android

At the GR Mobile Dev group this month there was discussion on the topic of using mapping capabilities on the Android and iOS platforms. This document describes the steps necessary to setup an Android application, acquire a API key to access map tiles, and implement a simple mapping application. Below is the basic boilerplate to getting started with the Google Maps API. This doesn't look at any geo-location or overlays, but will get you to the point of being able to start exploring these topics.

Requirements

SDK
The Google Maps API is not part of the core Android SDK, however access to it is available through the Android tool chain. Running the “Android SDK Manager” and verify that the “SDK Platform” and “Google APIs by Google Inc” for the API version you are interested in developing an application are installed (recommend API version 7 [2.1] based on current usage statistics).

AVD (Emulator)
Once the SDK Platform and Google APIs are installed we will need to create an AVD which will support the Google Maps API. The target will need to be set to “Google APIs (Google Inc.) - API Level #” instead of “Android #.# - API Level #” for the Google APIs support. This will create an AVD which includes the normal Android and Google APIs.

Basic Application

Create a basic Android application, except on the step where you select the “Build Target” choose the “Google APIs” for the target API level instead of “Android #.#.” Otherwise the application creation process is the same as the any other project. In the instance where you are working on an already existing project, you can update the project by going to the properties for the project “Android” options and select “Google APIs” for the existing target API level.

Include Map

Manifest
The manifest has two updates required to enable using the Google Maps API. The application will need to know that it should use the Google Maps library. In addition the INTERNET permission is required to load the map tiles.

AndroidManifest.xml
    <manifest ...>
        ...
        <uses-sdk ...>
        <uses-permission android:name="android.permission.INTERNET">
        <application ...>
            <uses-library android:name="com.google.android.maps">
            <activity ...>
        </application>
    </manifest>

Map API Key
Google requires that an application has a map API key to load the map tiles from their servers. This map key is based on the signing key used to sign the application. Under development conditions there is a debug key used to sign the application by the development environment which can be found in ~/.android/debug.keystore, though you will also want to use the release signing key when submitting to the Android Market.

To generate the map API key you will need to use the keytool application which ships with your Java installation. On windows you will find it in the bin directory of your java installation, on Mac OS X or Linux it will probably already be in your path.

Terminal
    % keytool -list -keystore ~/.android/debug.keystore
    ... 
    Certificate fingerprint (MD5): 94:1E:43:49:87:73:BB:E6:A6:88:D7:20:F1:8E:B5:98

Browse to the Map API Key Signup page. Copy the MD5 into the form and submit the form. This will generate the map key for your the debug.keystore. Follow the same steps after you generate a release key for submitting to the Android Market.

Browser - http://code.google.com/android/maps-api-signup.html
    Your key is:
        0UUnupTxbpdGofOV-UQi2jQHV0z6xwGh_kDu3NA

It is good to store the map API key as a value in a strings file to make it easy to change out in case you need to generate a new debug map key for multiple developers working on the project. This file can then be set within the version control system as ignored. Also, a release key can be added and easily be switched between release and production by updating the map_key between @string/map_key_debug and @string/map_key_release.

res/values/map_keys.xml
    <resources>
        <string name="map_key_debug">0UUnupTxbpdGofOV-UQi2jQHV0z6xwGh_kDu3NA</string>
        <string name="map_key_release">0UUnupTxbpdHBqSgZFLLqTmFGezrtAJBEUCUWgw</string>
        <string name="map_key">@string/map_key_debug</string>
    </resources>

Application 

Layout
The layout can now have the map view included. In this example we are using the full screen to display the map view.

res/layout/main.xml
    <linearlayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        android:orientation="vertical">
        <com.google.android.maps.mapview
            android:id="@+id/map"
            android:apikey="@string/map_key"
            android:layout_height="fill_parent"
            android:layout_width="fill_parent">
        </com.google.android.maps.mapview>
    </linearlayout>

Activity
Maps require that you use a MapActivity. The MapActivity is an abstract class that requires the implementation of the isRouteDisplayed method, though with a simple example this method can be left as a stub returning false. The setupMapView method is setting the map to be clickable and enabling the built in zoom controls.

src/...package.../MainActivity.java
    package ...package...;

    import android.os.Bundle;
    import com.google.android.maps.MapActivity;
    import com.google.android.maps.MapView;

    public class MainActivity
        extends MapActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            setupMapView();
        }

        private void setupMapView() {
            MapView map = (MapView) findViewById(R.id.map);
            map.setBuiltInZoomControls(true);
            map.setClickable(true);
        }

        @Override
        protected boolean isRouteDisplayed() {
            return false;
        }
    }

Resources

Tuesday, September 13, 2011

Android, Ant, Emma, Robotium

We started a new android project at work today.  Since there isn't any legacy development we wanted to get it started right, with a continuous integration environment so we wanted to automate the build and testing as an ant task.  I was unable to find anything on the android developer site outlining the process to set this up, so I turned to the internet.  Nothing on the first couple pages of google results had a complete recipe so I figured I ought to share the solution we used.

Requirements
  • Eclipse
  • Android SDK
  • ADT
  • Ant
  • Robotium jar
Create Android Application
  • File -> New -> Other...
  • Open Android -> Select Android Project -> Click Next
  • Set Project name, Android <Version>, Package name (The rest should be filled out with reasonable defaults)
  • Click Finish
Add Ant Script
  • Open terminal to project directory
% $ANDROID_HOME/tools/android update project --path .
Updated local.properties
Added file ./build.xml
Updated file ./proguard.cfg

Create Android Test Application
  • File -> New -> Other...
  • Open Android -> Select Android Test Project -> Click Next
  • Set An existing Android project (The rest should be filled in with reasonable defaults)
Add Ant Script
  • Open terminal to test project directory
% $ANDROID_HOME/android update test-project -m ../BigMeanRobot --path .
Resolved location of main project to: /Users/daya/src/BigMeanRobot/BigMeanRobot
Updated default.properties
Updated local.properties
Added file ./build.xml
Updated file ./proguard.cfg
Updated build.properties

Test Coverage Report
  • Open terminal to test project directory
% ant coverage
...compile/install project...
...compile/install test project...
coverage:
        Running tests...
...cleanup...
  • Open <test project root>/coverage/coverage.html
Add Robotium
  • Download robotium-solo-<version>.jar to <test project root>/libs
  • Add to test project
  • File -> Properties
  • Select Java Build Path -> Libraries -> Click Add Jar...
  • Open Big Mean Robot -> libs
  • Select robotium-solo-<version>.jar -> Click OK
  • Click OK
Create Robotium Test