Saturday, August 27, 2011

JSON Parsing in Android

1. Android and JSON

1.1. Android and JSON

JSON is a very condense data exchange format. Android includes the json.org libraries which allow to work easily with JSON files.

1.2. Twitter

Twitter is a great source for JSON. You can just call a URI and retrieve JSON. Here are some examples:

Table 1. Twitter URI's
URIDescription
http://twitter.com/statuses/user_timeline/vogella.json Get the timeline of user vogella .
http://search.twitter.com/search.json?q=android Search for the term "android" on Twitter.
http://twitter.com/statuses/user_timeline/vogella.json Returns the user data of user vogella .

Please note that some URI's return a JSONObject object while others return a JSONArray. The following coding uses an URI which returns an JSONArray.

2. Reading JSON

Create a new Android project "de.vogella.android.twitter.json" with the package "de.vogella.android.twitter.json" and the activity "ParseJSON".
Create the following coding for the activity. This will download the twitter feed for the user http://twitter.com/ and write the number of entries and the text messages to the Android log file.

package de.vogella.android.twitter.json;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class ParseJSON extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		String readTwitterFeed = readTwitterFeed();
	try {
		JSONArray jsonArray = new JSONArray(readTwitterFeed);
		Log.i(ParseJSON.class.getName(),
			"Number of entries " + jsonArray.length());
		for (int i = 0; i < jsonArray.length(); i++) {
		JSONObject jsonObject = jsonArray.getJSONObject(i);
		Log.i(ParseJSON.class.getName(), jsonObject.getString("text"));
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

	public String readTwitterFeed() {
	StringBuilder builder = new StringBuilder();
	HttpClient client = new DefaultHttpClient();
	HttpGet httpGet = new HttpGet(
		"http://twitter.com/statuses/user_timeline/vogella.json");
	try {
		HttpResponse response = client.execute(httpGet);
		StatusLine statusLine = response.getStatusLine();
		int statusCode = statusLine.getStatusCode();
		if (statusCode == 200) {
			HttpEntity entity = response.getEntity();
			InputStream content = entity.getContent();
			BufferedReader reader = new BufferedReader(
					new InputStreamReader(content));
			String line;
			while ((line = reader.readLine()) != null) {
				builder.append(line);
			}
		} else {
		Log.e(ParseJSON.class.toString(), "Failed to download file");
	}
	} catch (ClientProtocolException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	return builder.toString();
}
} 
 
 
To run this example assign the uses-permission to your "AndroidManifest.xml" for "android.permission.INTERNET".

3. Write JSON

Writing JSON is very simple. Just create the JSONObject or JSONArray and use the toString() method.
 

public void writeJSON() {
	JSONObject object = new JSONObject();
	try {
		object.put("name", "Jack Hack");
		object.put("score", new Integer(200));
		object.put("current", new Double(152.32));
		object.put("nickname", "Hacker");
	} catch (JSONException e) {
		e.printStackTrace();
	}
	System.out.println(object);
}
	

How to create LIve Wallpaper in Android ?

1.1. Live Wallpapers

Live Wallpapers are animated, interactive backgrounds for the Android homescreen. A live wallpaper is similar to other Android applications and can use most of the same functionality.
To create a live wallpaper, you need to create a XML file which describes your wallpaper. This file should contain a description of the application and can contain a preview and a link to a preference activity which allow to customize the live wallpaper.
You also maintain a service which must extend the abstract class "WallpaperService". "WallpaperService"is the base class for all live wallpapers in the system. You must implement the method onCreateEngine() and return an object of type "Engine". This engine will handle the life cycle events, animations and drawings of the wallpaper. Here you find life cycle hooks, as for example onCreate(), onSurfaceCreated(), onVisibilityChanged(), onOffsetsChanged(), onTouchEvent() and onCommand() .
This service requires the permission "android.permission.BIND_WALLPAPER" and must be registered via an intent-filter to the action "android.service.wallpaper.WallpaperService".
The service returns in its method onCreateEngine() an instance "android.service.wallpaper.WallpaperService.Engine". This class is responsible for handling the Live Wallpaper.
You should also maintain in your "AndroidManifest.xml" that your application uses the feature "android.software.live_wallpaper" in "AndroidManifest.xml". This will prevent that your wallpaper can be installed on devices which do not support live wallpapers.

2. Android Wallpaper Example

Create a new project "de.vogella.android.wallpaper". Do not create an Activity.
Create the folder "/res/xml" and maintain the following file "mywallpaper.xml. This file contains a description of your wallpaper and a preview graphic. You can also maintain a link to an activity which allow to configure your wallpaper. This resource file will be linked to from the "AndroidManifest.xml". You could also include the "android:thumbnail attribute" which would point to a drawable which gives a smaller image of the running wallpaper.


<?xml version="1.0" encoding="UTF-8"?>
<wallpaper 
xmlns:android="http://schemas.android.com/apk/res/android"
android:thumbnail="@drawable/icon"
android:description="@string/wallpaper_description"  
android:settingsActivity="de.vogella.android.wallpaper.MyPreferencesActivity"/>
 
	
Change your "AndroidManifest.xml" to the following to define your service ""MyWallpaperService" and to maintain the uses-feature. 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="de.vogella.android.wallpaper" android:versionCode="1"
	android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">


<service android:name="MyWallpaperService" android:enabled="true"
android:permission="android.permission.BIND_WALLPAPER"
android:label="Wallpaper Example ">
	<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService"></action>
		</intent-filter>
	<meta-data android:name="android.service.wallpaper"
		android:resource="@xml/mywallpaper"></meta-data>
</service>
<activity android:label="@string/app_name" android:name=".MyPreferencesActivity"
	android:theme="@android:style/Theme.Light.WallpaperSettings"
	android:exported="true">
	</activity>
	</application>
	<uses-sdk android:minSdkVersion="10" />
	<uses-feature android:name="android.software.live_wallpaper"
		android:required="true"></uses-feature>

</manifest> 
 
We create the class "MyPoint" to save the elements we have drawn. 
 
				
package de.vogella.android.wallpaper;

public class MyPoint {
	String text;
	private int x;
	private int y;

	public MyPoint(String text, int x, int y) {
		this.text = text;
		this.x = x;
		this.y = y;
	}
}

			
		
Create a preference activity . Create the preference value files "prefs.xml" in the folder "res/xml".

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
	<CheckBoxPreference android:key="touch"
		android:title="Enable Touch"></CheckBoxPreference>
	<EditTextPreference android:key="numberOfCircles"
		android:title="Number of Circles"></EditTextPreference>
</PreferenceScreen>
 
 
Create the following coding for your preference activity. 
 
 
package de.vogella.android.wallpaper;

import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.widget.Toast;

public class MyPreferencesActivity extends PreferenceActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.xml.prefs);

	// We want to add a validator to the number of circles so that it only
	// accepts numbers
	Preference circlePreference = getPreferenceScreen().findPreference(
			"numberOfCircles");

		// Add the validator
	circlePreference.setOnPreferenceChangeListener(numberCheckListener);
	}

	/**
	 * Checks that a preference is a valid numerical value
	 */
	Preference.OnPreferenceChangeListener numberCheckListener = 
new OnPreferenceChangeListener() {

	@Override
	public boolean onPreferenceChange(Preference preference,
Object newValue) {
		// Check that the string is an integer
		if (newValue != null && newValue.toString().length() > 0
				&& newValue.toString().matches("\\d*")) {
			return true;
		}
		// If now create a message to the user
		Toast.makeText(MyPreferencesActivity.this, "Invalid Input",
				Toast.LENGTH_SHORT).show();
			return false;
	}
	};

} 
 
Create the following coding for the Wallpaper Service. 
 
package de.vogella.android.wallpaper;

import java.util.ArrayList;
import java.util.List;

import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class MyWallpaperService extends WallpaperService {

	@Override
	public Engine onCreateEngine() {
		return new MyWallpaperEngine();
	}

	private class MyWallpaperEngine extends Engine {
		private final Handler handler = new Handler();
		private final Runnable drawRunner = new Runnable() {
			@Override
			public void run() {
				draw();
			}

		};
		private List<MyPoint> circles;
		private Paint paint = new Paint();
		private int width;
		int height;
		private boolean visible = true;
		private int maxNumber;
		private boolean touchEnabled;

		public MyWallpaperEngine() {
	SharedPreferences prefs = PreferenceManager
		.getDefaultSharedPreferences(MyWallpaperService.this);
		maxNumber = Integer
			.valueOf(prefs.getString("numberOfCircles", "4"));
			touchEnabled = prefs.getBoolean("touch", false);
			circles = new ArrayList<MyPoint>();
			paint.setAntiAlias(true);
			paint.setColor(Color.WHITE);
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeJoin(Paint.Join.ROUND);
			paint.setStrokeWidth(10f);
			handler.post(drawRunner);
		}

		@Override
		public void onVisibilityChanged(boolean visible) {
			this.visible = visible;
			if (visible) {
				handler.post(drawRunner);
			} else {
				handler.removeCallbacks(drawRunner);
			}
		}

		

}
 
			@Override
		public void onSurfaceDestroyed(SurfaceHolder holder) {
			super.onSurfaceDestroyed(holder);
			this.visible = false;
			handler.removeCallbacks(drawRunner);
		}

		@Override
		public void onSurfaceChanged(SurfaceHolder holder, int format,
				int width, int height) {
			this.width = width;
			this.height = height;
			super.onSurfaceChanged(holder, format, width, height);
		}

		@Override
		public void onTouchEvent(MotionEvent event) {
			if (touchEnabled) {

				float x = event.getX();
				float y = event.getY();
				SurfaceHolder holder = getSurfaceHolder();
				Canvas canvas = null;
				try {
					canvas = holder.lockCanvas();
			if (canvas != null) {
				canvas.drawColor(Color.BLACK);
				circles.clear();
				circles.add(new MyPoint(
				String.valueOf(circles.size() + 1), x, y));
				drawCircles(canvas, circles);
				}
			} finally {
				if (canvas != null)
				holder.unlockCanvasAndPost(canvas);
			}
			super.onTouchEvent(event);
		}
	}
		private void draw() {
	SurfaceHolder holder = getSurfaceHolder();
	Canvas canvas = null;
		try {
			canvas = holder.lockCanvas();
			if (canvas != null) {
				if (circles.size() >= maxNumber) {
					circles.clear();
				}
			int x = (int) (width * Math.random());
			int y = (int) (height * Math.random());
	circles.add(new MyPoint(String.valueOf(circles.size() + 1),
						x, y));
					drawCircles(canvas, circles);
				}
			} finally {
				if (canvas != null)
					holder.unlockCanvasAndPost(canvas);
			}
			handler.removeCallbacks(drawRunner);
			if (visible) {
				handler.postDelayed(drawRunner, 5000);
			}
		}

		// Surface view requires that all elements are drawn completely
		private void drawCircles(Canvas canvas, List<MyPoint> circles) {
			canvas.drawColor(Color.BLACK);
			for (MyPoint point : circles) {
			canvas.drawCircle(point.x, point.y, 20.0f, paint);
			}
		}
	}
 
 
If you start your application and set this wallpaper as
background your emulator should similar to the following. If you have
Touch enabled via the preferences you can click on the screen to
remove the existing circles, also via the settings you can define the
number of circles which should be displayed.
	 
 
 


Sunday, August 14, 2011

Android Cloud to Device Messaging (C2DM)

1. Cloud to device messaging

1.1. Cloud to device messaging

As of Android 2.2 the Android platform allows to push information from the Internet to an Android phone. This service is called "Cloud to Device messaging" or short C2DM.
The standard mechanism for an application which requires data from the internet is to poll periodically for the data. This uses unnecessary network bandwidth and consumes the battery. Therefore pushing data over the internet to the phone might be a preferable solution.
C2DM is intented to notify the mobile phone that new data is available. It is not intended to send lots of data, as the size of the message is limited to 1024 characters. The push service would notify the mobile device and the Android application would fetch the data from somewhere else.
C2DM uses existing connections the Google servers which is highly optimize to minimize bandwidth and battery consumption.

1.2. Requirements

C2MD only works as of Android 2.2 and requires that Android Market application is installed on the device.
To use C2DM on the Android simulator you also need to use a Google device with API 8 or higher and to register with a Google account on the emulator via the Settings.
To use C2DM on your Android device you have to write an Android application. This application will register at a Google server to be able to receive push messages from the Google server. Your server registers with the Google server and authenticates himself. The server can then push messages to the Google server which in return pushes the information to the device. For this authentication you require a Google user.

1.3. Information flow

In a C2DM you have three involved parties. The server which wants to push messages to the Android device (application server), the Google C2DM servers and the Android application.
To enable C2DM for an Android application, this application must registers with the Google C2DM servers. After registration it receives a registration ID from the Google C2DM servers.
The Android application must then send the registration ID to its application server. The application server stores the registration ID. This registration ID is required to send the information later to this specific device.
When the application server needs to push a message to the Android applicatoin, it sends the message via HTTP GET to Google’s C2DM servers.
The C2DM servers route the message to the device once the device is available, and an Intent broadcast is sent to the Android application. The app is started and processes the message in its defined Intent Receiver.

1.4. More details

To register your application for the C2DM service your application needs to fire an registration intent "com.google.android.c2dm.intent.REGISTER" to the C2DM server. This intent includes a Google mail address and the application ID.
Upon successful registration the C2DM server broadcast a REGISTRATION Intent to the application which includes a registration ID. Each registration ID represents a particular device, e.g. every Android phone will receive its own registration code.
The C2DM may refresh this registration ID periodically but until a refreshment your application should store this ID for later use.
Google provides a small set of classes which makes the registration as smooth as possible.
After the Android application received its unique registration ID it has to send this information to the application server.The application server can use this registration ID to send messages to the Android application on the specific device.

1.5. Send messages

With your registration ID the application server can send messages to your device. This requires in addition an authorization token which needs to be generated for each per Android application and registration ID.
The authorization token is generated by sending a post HTTPS request to the C2DM servers.
To send a message to the Android application on a specific device the application server sends a HTTP GET request to the Google C2DM servers. This HTTP GET request contain the registration ID (to address a specific device) and well as the authentication token (to tell Google that this server is allowed to send messages).
Once your server sends the message to the C2DM server this server will queue the message until the device is available. The message will be send to the device as a broadcast. Your application needs to register for this broadcast event to receive the message.

1.6. Receiving a Message

If a message is received on a device via a broadcast event. The system extracts the raw key/value pairs from the message. It then passes the key/value pairs to the Android application in a "com.google.android.c2dm.intent.RECEIVE" Intent. The data can be received from the Intent via getExtras(). The available keys are "payload", "from", "collapse_key". THe actual data is included in "payload">. The Android application extracts this data and can react to it.

1.7. Permissions

To use C2DM in your application to have to register for the permission "com.google.android.c2dm.permission.RECEIVE" and in most cases also "android.permission.INTERNET" permission. Your application should also declare the permission "applicationPackage + ".permission.C2D_MESSAGE" with the "android:protectionLevel" of "signature" so that other applications cannot register and receive message for the application. android:protectionLevel="signature". ensures that applications with request a permission must be signed with same certificate as the application that declared the permission.
Your application must also register as an intent receiver for the two intents "com.google.android.c2dm.intent.RECEIVE" and "com.google.android.c2dm.intent.REGISTRATION". "com.google.android.c2dm.SEND"

2. Device and Registration

Currently C2DM is under beta testing. You need to ask for access. Here is the link to the signup form .
If you test the following example on the emulator make sure that you use Google API 8 or later. You also have to register a Google user on the virtual device under "Settings"-> "Accounts Sync".
If you test the following example on a real device make sure that the Android Market is installed.

3. Example

3.1. Project

Create the Android project "de.vogella.android.c2dm" with the activity "RegisterActivity". Create the following layout "main.xml" which will allow to enter your registered user id for C2DM and send a registration request to the C2DM servers.

				
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
<EditText android:id="@+id/editText1" android:layout_height="wrap_content"
android:layout_width="match_parent" android:hint="Enter C2DM registered User"
	></EditText>
	<LinearLayout android:layout_height="match_parent"
		android:layout_width="match_parent" android:id="@+id/linearLayout1"
		android:gravity="center">
	<Button android:id="@+id/button1" android:text="Register for Push
Notification"
	android:layout_height="wrap_content" android:onClick="register"
			android:layout_width="wrap_content"></Button>
	</LinearLayout>

</LinearLayout>

			

3.2. Create the Google utility classes

Create the following three classes from Google. Unfortunately I was not able to find a downloadable package of these classes at Google therefore I reproduce them.

				
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.c2dm;

import java.io.IOException;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;

/**
 * Base class for C2D message receiver. Includes constants for the strings used
 * in the protocol.
 */
public abstract class C2DMBaseReceiver extends IntentService {
	private static final String C2DM_RETRY =
"com.google.android.c2dm.intent.RETRY";

	public static final String REGISTRATION_CALLBACK_INTENT =
"com.google.android.c2dm.intent.REGISTRATION";
	private static final String C2DM_INTENT =
"com.google.android.c2dm.intent.RECEIVE";

	// Logging tag
	private static final String TAG = "C2DM";

	// Extras in the registration callback intents.
	public static final String EXTRA_UNREGISTERED = "unregistered";

	public static final String EXTRA_ERROR = "error";

	public static final String EXTRA_REGISTRATION_ID = "registration_id";

public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";

	// wakelock
	private static final String WAKELOCK_KEY = "C2DM_LIB";

	private static PowerManager.WakeLock mWakeLock;
	private final String senderId;

	/**
	 * The C2DMReceiver class must create a no-arg constructor and pass the
	 * sender id to be used for registration.
	 */
	public C2DMBaseReceiver(String senderId) {
		// senderId is used as base name for threads, etc.
		super(senderId);
		this.senderId = senderId;
	}

	/**
	 * Called when a cloud message has been received.
	 */
	protected abstract void onMessage(Context context, Intent intent);

	/**
	 * Called on registration error. Override to provide better error messages.
	 * 
	 * This is called in the context of a Service - no dialog or UI.
	 */
	public abstract void onError(Context context, String errorId);

	/**
	 * Called when a registration token has been received.
	 */
	public void onRegistered(Context context, String registrationId)
			throws IOException {
		// registrationId will also be saved
	}

	/**
	 * Called when the device has been unregistered.
	 */
	public void onUnregistered(Context context) {
	}

	@Override
	public final void onHandleIntent(Intent intent) {
	try {
		Context context = getApplicationContext();
		if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
			handleRegistration(context, intent);
		} else if (intent.getAction().equals(C2DM_INTENT)) {
			onMessage(context, intent);
		} else if (intent.getAction().equals(C2DM_RETRY)) {
			C2DMessaging.register(context, senderId);
			}
		} finally {
			// Release the power lock, so phone can get back to sleep.
			// The lock is reference counted by default, so multiple
			// messages are ok.

			// it should use it's own lock.
			mWakeLock.release();
		}
	}

	/**
	 * Called from the broadcast receiver. Will process the received intent,
	 * call handleMessage(), registered(), etc. in background threads, with a
	 * wake lock, while keeping the service alive.
	 */
	static void runIntentInService(Context context, Intent intent) {
		if (mWakeLock == null) {
			// This is called from BroadcastReceiver, there is no init.
			PowerManager pm = (PowerManager) context
					.getSystemService(Context.POWER_SERVICE);
			mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
					WAKELOCK_KEY);
		}
		mWakeLock.acquire();

	// Use a naming convention, similar with how permissions and intents are
	// used. Alternatives are introspection or an ugly use of statics.
		String receiver = context.getPackageName() + ".C2DMReceiver";
		intent.setClassName(context, receiver);

		context.startService(intent);

	}

	private void handleRegistration(final Context context, Intent intent) {
		final String registrationId = intent
				.getStringExtra(EXTRA_REGISTRATION_ID);
		String error = intent.getStringExtra(EXTRA_ERROR);
		String removed = intent.getStringExtra(EXTRA_UNREGISTERED);

		if (Log.isLoggable(TAG, Log.DEBUG)) {
		Log.d(TAG, "dmControl: registrationId = " + registrationId
			+ ", error = " + error + ", removed = " + removed);
		}

		if (removed != null) {
			// Remember we are unregistered
			C2DMessaging.clearRegistrationId(context);
			onUnregistered(context);
			return;
		} else if (error != null) {
			// we are not registered, can try again
			C2DMessaging.clearRegistrationId(context);
			// Registration failed
			Log.e(TAG, "Registration error " + error);
			onError(context, error);
		if ("SERVICE_NOT_AVAILABLE".equals(error)) {
			long backoffTimeMs = C2DMessaging.getBackoff(context);

			Log.d(TAG, "Scheduling registration retry, backoff = "
					+ backoffTimeMs);
				Intent retryIntent = new Intent(C2DM_RETRY);
			PendingIntent retryPIntent = PendingIntent
			.getBroadcast(context, 0 /* requestCode */, retryIntent,
					0 /* flags */);

	AlarmManager am = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
			am.set(AlarmManager.ELAPSED_REALTIME, backoffTimeMs,
						retryPIntent);

				// Next retry should wait longer.
				backoffTimeMs *= 2;
				C2DMessaging.setBackoff(context, backoffTimeMs);
			}
		} else {
			try {
			onRegistered(context, registrationId);
			C2DMessaging.setRegistrationId(context, registrationId);
			} catch (IOException ex) {
			Log.e(TAG, "Registration error " + ex.getMessage());
			}
		}
	}
}

			

				
package com.google.android.c2dm;
/*
 */


import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * Helper class to handle BroadcastReciver behavior.
 * - can only run for a limited amount of time - it must start a real service 
 * for longer activity
 * - must get the power lock, must make sure it's released when all done.
 * 
 */
public class C2DMBroadcastReceiver extends BroadcastReceiver {
    
    @Override
    public final void onReceive(Context context, Intent intent) {
        // To keep things in one place.
        C2DMBaseReceiver.runIntentInService(context, intent);
        setResult(Activity.RESULT_OK, null /* data */, null /* extra */);        
    }
}

			

				
package com.google.android.c2dm;
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * Utilities for device registration.
 *
 * Will keep track of the registration token in a private preference.
 */
public class C2DMessaging {
    public static final String EXTRA_SENDER = "sender";
    public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
    public static final String REQUEST_UNREGISTRATION_INTENT = 
"com.google.android.c2dm.intent.UNREGISTER";
    public static final String REQUEST_REGISTRATION_INTENT =
"com.google.android.c2dm.intent.REGISTER";
    public static final String LAST_REGISTRATION_CHANGE =
"last_registration_change";
    public static final String BACKOFF = "backoff";
    public static final String GSF_PACKAGE = "com.google.android.gsf";


    // package
    static final String PREFERENCE = "com.google.android.c2dm";
    
    private static final long DEFAULT_BACKOFF = 30000;

    /**
     * Initiate c2d messaging registration for the current application
     */
    public static void register(Context context,
            String senderId) {
        Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
        registrationIntent.setPackage(GSF_PACKAGE);
        registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
                PendingIntent.getBroadcast(context, 0, new Intent(), 0));
        registrationIntent.putExtra(EXTRA_SENDER, senderId);
        context.startService(registrationIntent);
        // TODO: if intent not found, notification on need to have GSF
    }

    /**
     * Unregister the application. New messages will be blocked by server.
     */
    public static void unregister(Context context) {
        Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
        regIntent.setPackage(GSF_PACKAGE);
        regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
PendingIntent.getBroadcast(context,
                0, new Intent(), 0));
        context.startService(regIntent);
    }

    /**
     * Return the current registration id.
     *
     * If result is empty, the registration has failed.
     *
     * @return registration id, or empty string if the registration is not complete.
     */
    public static String getRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        String registrationId = prefs.getString("dm_registration", "");
        return registrationId;
    }

    public static long getLastRegistrationChange(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
    }
    
    static long getBackoff(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
    }
    
    static void setBackoff(Context context, long backoff) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putLong(BACKOFF, backoff);
        editor.commit();

    }

    // package
    static void clearRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", "");
        editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
        editor.commit();

    }

    // package
    static void setRegistrationId(Context context, String registrationId) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", registrationId);
        editor.commit();

    }
}

			

3.3. Getting ready for registration

Create the following class "C2DMReceiver" which we will later register as a service.

				
package de.vogella.android.c2dm;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.google.android.c2dm.C2DMBaseReceiver;

public class C2DMReceiver extends C2DMBaseReceiver {
	public C2DMReceiver() {
		// Email address currently not used by the C2DM Messaging framework
		super("dummy@google.com");
	}

	@Override
	public void onRegistered(Context context, String registrationId)
			throws java.io.IOException {
		// The registrationId should be send to your applicatioin server.
		// We just log it to the LogCat view
		// We will copy it from there
		Log.e("C2DM", "Registration ID arrived: Fantastic!!!");
		Log.e("C2DM", registrationId);
	};

	@Override
	protected void onMessage(Context context, Intent intent) {
		Log.e("C2DM", "Message: Fantastic!!!");
		// Extract the payload from the message
		Bundle extras = intent.getExtras();
		if (extras != null) {
			System.out.println(extras.get("payload"));
			// Now do something smart based on the information
		}
	}

	@Override
	public void onError(Context context, String errorId) {
		Log.e("C2DM", "Error occured!!!");
	}

}

			
Create the following "AndroidManifest.xml". That will register the right receivers and requests the right permissions.

				
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="de.vogella.android.c2dm" android:versionCode="1"
	android:versionName="1.0">
<permission android:name="de.vogella.android.c2dm.permission.C2D_MESSAGE"
	android:protectionLevel="signature" />
<uses-permission android:name="de.vogella.android.c2dm.permission.C2D_MESSAGE" />
<!-- Permissions -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />


<application android:icon="@drawable/icon" android:label="@string/app_name">
	<activity android:name="RegisterActivity" android:label="@string/app_name">
		<intent-filter>
			<action android:name="android.intent.action.MAIN" /> 
		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>
	<service android:name=".C2DMReceiver" />
	<!-- Only C2DM servers can send messages for the app. If permission is 
		not set - any other app can generate it -->
	<receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
		android:permission="com.google.android.c2dm.permission.SEND">
		<!-- Receive the actual message -->
<intent-filter>
		<action android:name="com.google.android.c2dm.intent.RECEIVE" />
		<category android:name="de.vogella.android.c2dm" />
</intent-filter>
		<!-- Receive the registration id -->
<intent-filter>
	<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
	<category android:name="de.vogella.android.c2dm" />
</intent-filter>
	</receiver>
</application>
</manifest>
			
Change your Activity to the following. In this activity you can enter your registered Google C2DM user and ask for a registration key.

				
package de.vogella.android.c2dm;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.google.android.c2dm.C2DMessaging;

public class RegisterActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}

	public void register(View view) {
		Log.e("Super", "Starting registration");
		Toast.makeText(this, "Starting", Toast.LENGTH_LONG).show();
		EditText text = (EditText) findViewById(R.id.editText1);

		C2DMessaging.register(this, text.getText().toString());
	}
}
			

3.4. Register your application

Run your application, maintain your registered user and press the button. Check LogCat for the registration ID.
If you see " Unable to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gsf (has extras) }: not found" message make sure you are using a Google device and that you have a Google user registered on the device.

3.5. Using curl

If you run a Linux system you can easily test the service on teh command line. You can request your authentication key via curl on the command line. From the response get the part after "Auth=".

				
curl https://www.google.com/accounts/ClientLogin -d 
Email=your_user -d "Passwd=your_password" -d 
accountType=GOOGLE -d source=Google-cURL-Example -d service=ac2dm
			
This part and the registration code can be used to send a message to your device.

				
curl --header "Authorization: GoogleLogin auth=your_authenticationid" "
https://android.apis.google.com/c2dm/send"
-d registration_id=your_registration -d "data.payload=This data will be send
to your application as payload" -d collapse_key=0

			

3.6. Create your server ;-) application

As described earlier the application server needs to get an authentication key via HTTPS. Afterwards it can send messages to the device via HTTP by supplying the authentication key and the registration ID.
To avoid that you have to learn about server side Java programming we will "simulate" the server by another Android application. In general you could create a server side application for example with the Google App Engine .
Create a new Android application "de.vogella.android.c2dm.server" with the Activity "ServerSimulator". Assign the permission "android.permission.INTERNET" to this activity.
Change "main.xml" to the following.

				
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
<Button android:text="Get Authentication" android:id="@+id/button1"
	android:layout_width="wrap_content" android:layout_height="wrap_content"
	android:onClick="getAuthentification"></Button>
<Button android:text="Show Authentication" android:id="@+id/button2"
android:layout_width="wrap_content" android:layout_height="wrap_content"
	android:onClick="showAuthentification"></Button>
<EditText android:layout_width="match_parent" android:id="@+id/registrationId"
	android:hint="Registration Code" android:layout_height="wrap_content"></EditText>
<Button android:text="Send Message" android:id="@+id/button3"
	android:layout_width="wrap_content" android:layout_height="wrap_content"
	android:onClick="sendMessage"></Button>
</LinearLayout>

			
Change "ServerSimulator" to the following. Make your to use your registration key, your user and your password.

				
package de.vogella.android.c2dm.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class ServerSimulator extends Activity {
	private SharedPreferences prefManager;
	private final static String AUTH = "authentication";

	private static final String UPDATE_CLIENT_AUTH = "Update-Client-Auth";

	public static final String PARAM_REGISTRATION_ID = "registration_id";

	public static final String PARAM_DELAY_WHILE_IDLE = "delay_while_idle";

	public static final String PARAM_COLLAPSE_KEY = "collapse_key";

	private static final String UTF8 = "UTF-8";

	// Registration is currently hardcoded
	private final static String YOUR_REGISTRATION_STRING = "APA91bFQut1tqA-
nIL1ZaV0emnp4Rb0smwCkrMHcoYRXeYVtIebJgrzOHQj0h76qKRzd3bC_JO37uJ
0NgTcFO87HS9V7YC-yOP774pm0toppTHFO7Zc_PAw";

	private SharedPreferences prefs;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	prefManager = PreferenceManager.getDefaultSharedPreferences(this);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.mymenu, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.menuitem_user:
			Intent intent = new Intent(this, UserPreferences.class);
			startActivity(intent);
			break;
		default:
			break;
		}
		return false;
	}

	public void getAuthentification(View view) {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(this);

		HttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(
				"https://www.google.com/accounts/ClientLogin");

		try {

		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
		nameValuePairs.add(new BasicNameValuePair("Email", prefs.getString(
				"user", "undefined")));
		nameValuePairs.add(new BasicNameValuePair("Passwd", prefs
				.getString("password", "undefined")));
		nameValuePairs.add(new BasicNameValuePair("accountType", "GOOGLE"));
		nameValuePairs.add(new BasicNameValuePair("source",
				"Google-cURL-Example"));
		nameValuePairs.add(new BasicNameValuePair("service", "ac2dm"));
			post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
		HttpResponse response = client.execute(post);
		BufferedReader rd = new BufferedReader(new InputStreamReader(
				response.getEntity().getContent()));
			String line = "";
		while ((line = rd.readLine()) != null) {
			Log.e("HttpResponse", line);
			if (line.startsWith("Auth=")) {
				Editor edit = prefManager.edit();
				edit.putString(AUTH, line.substring(5));
				edit.commit();
				String s = prefManager.getString(AUTH, "n/a");
				Toast.makeText(this, s, Toast.LENGTH_LONG).show();
			}
			}
	} catch (IOException e) {
		e.printStackTrace();
	}
}
	public void showAuthentification(View view) {
	String s = prefManager.getString(AUTH, "n/a");
	Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}

public void sendMessage(View view) {
	try {
		Log.e("Tag", "Started");
		String auth_key = prefManager.getString(AUTH, "n/a");
		// Send a sync message to this Android device.
		StringBuilder postDataBuilder = new StringBuilder();
		postDataBuilder.append(PARAM_REGISTRATION_ID).append("=")
				.append(YOUR_REGISTRATION_STRING);
			// if (delayWhileIdle) {
		// postDataBuilder.append("&").append(PARAM_DELAY_WHILE_IDLE)
		// .append("=1");
			// }
		postDataBuilder.append("&").append(PARAM_COLLAPSE_KEY).append("=")
				.append("0");

	postDataBuilder.append("&").append("data.payload").append("=")
			.append(URLEncoder.encode("Lars war hier", UTF8));

		byte[] postData = postDataBuilder.toString().getBytes(UTF8);

			// Hit the dm URL.

		URL url = new URL("https://android.clients.google.com/c2dm/send");
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setDoOutput(true);
		conn.setUseCaches(false);
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Content-Type",
		"application/x-www-form-urlencoded;charset=UTF-8");
		conn.setRequestProperty("Content-Length",
				Integer.toString(postData.length));
		conn.setRequestProperty("Authorization", "GoogleLogin auth="
				+ auth_key);
		OutputStream out = conn.getOutputStream();
		out.write(postData);
		out.close();

		int responseCode = conn.getResponseCode();

		Log.e("Tag", String.valueOf(responseCode));
		// Validate the response code
			if (responseCode == 401 || responseCode == 403) {
		// The token is too old - return false to retry later, will
		// fetch the token
		// from DB. This happens if the password is changed or token
		// expires. Either admin
		// is updating the token, or Update-Client-Auth was received by
		// another server,
		// and next retry will get the good one from database.
			Log.e("C2DM", "Unauthorized - need token");
		}
			// Check for updated token header
		String updatedAuthToken = conn.getHeaderField(UPDATE_CLIENT_AUTH);
	if (updatedAuthToken != null && !auth_key.equals(updatedAuthToken)) {
		Log.i("C2DM",
		"Got updated auth token from datamessaging servers: "
						+ updatedAuthToken);
			Editor edit = prefManager.edit();
			edit.putString(AUTH, updatedAuthToken);
		}
		String responseLine = new BufferedReader(new InputStreamReader(
				conn.getInputStream())).readLine();
	// NOTE: You *MUST* use exponential backoff if you receive a 503
		// response code.
	// Since App Engine's task queue mechanism automatically does this
	// for tasks that
	// return non-success error codes, this is not explicitly
	// implemented here.
	// If we weren't using App Engine, we'd need to manually implement
	// this.
	if (responseLine == null || responseLine.equals("")) {
			Log.i("C2DM", "Got " + responseCode
					+ " response from Google AC2DM endpoint.");
			throw new IOException(
						"Got empty response from Google AC2DM endpoint.");
			}

			String[] responseParts = responseLine.split("=", 2);
			if (responseParts.length != 2) {
				Log.e("C2DM", "Invalid message from google: " + responseCode
						+ " " + responseLine);
				throw new IOException("Invalid response from Google "
						+ responseCode + " " + responseLine);
			}

			if (responseParts[0].equals("id")) {
				Log.i("Tag", "Successfully sent data message to device: "
						+ responseLine);
			}

			if (responseParts[0].equals("Error")) {
				String err = responseParts[1];
				Log.w("C2DM",
						"Got error response from Google datamessaging endpoint: "
								+ err);
				// No retry.
				throw new IOException(err);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}