Implementing
your C2DM Client :
C2DM Client(Android):
First we are going to implement our client side. It will receive the message from Google server and show a notification on the status bar Currently this code will show a notification on the status bar . In the C2DM client we need to provide the registered
eMail id to get the Registration key for the client .
Go to the link and register your e-mail id with your application Package name (Do remember the Package name its very important )
http://code.google.com/android/c2dm/signup.html
You will receive a mail from C2DM giving your C2DM account status.
The most important part is the AndroidManifest.xml be vary careful while making changes .
src/
C2DMRegistration.java
public class C2DMRegistration {
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";
public static final String TAG = "REGISTRATION FOR C2DM";
// package
static final String PREFERENCE = "com.google.android.c2dm";
public static long DEFAULT_BACKOFF = 3000;
/**
* Initiate c2d messaging registration for the current application
*/
public static void register(Context context
) {
String mailId=context.getString(R.string.email_id);
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, mailId);
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();
}
}
C2DMBaseReceiver.java
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
public static final String WAKELOCK_KEY = "C2DM_LIB";
public static PowerManager.WakeLock mWakeLock;
public 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.
*/
public 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)) {
C2DMRegistration.register(context);
}
} finally {
// Release the power lock, so phone can get back to sleep.
// The lock is reference counted by default, so multiple
// messages are ok.
// If the onMessage() needs to spawn a thread or do something else,
// 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 = "com.android.aspark.notification.C2DMReceiver";
String receiver = context.getPackageName() + ".C2DMReceiver";
intent.setClassName(context, receiver);
context.startService(intent);
}
public 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
C2DMRegistration.clearRegistrationId(context);
onUnregistered(context);
return;
} else if (error != null) {
// we are not registered, can try again
C2DMRegistration.clearRegistrationId(context);
// Registration failed
Log.e(TAG, "Registration error1 " + error);
onError(context, error);
if ("SERVICE_NOT_AVAILABLE".equals(error)) {
long backoffTimeMs = C2DMRegistration.getBackoff(context);
Log.d(TAG, "Scheduling registration retry, backoff = "
+ backoffTimeMs);
Log.i(TAG, "Registration retrying ");
Intent Intent = new Intent(C2DM_RETRY);
PendingIntent PIntent = PendingIntent
.getBroadcast(context, 0 /* requestCode */, Intent,
0 /* flags */);
//
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Log.i(TAG, "Starting Alarm ");
am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime()+backoffTimeMs,
PIntent);
Log.e(TAG," allerm set on "+ backoffTimeMs);
// // Next retry should wait longer.
backoffTimeMs*=2;
C2DMRegistration.setBackoff(context, backoffTimeMs);
}
} else {
try {
onRegistered(context, registrationId);
C2DMRegistration.setRegistrationId(context, registrationId);
} catch (IOException ex) {
Log.e(TAG, "Registration error2 " + ex.getMessage());
}
}
}
}
C2DMBroadcastReceiver.java
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 */);
}
}
C2DMReceiver.java
public class C2DMReceiver extends C2DMBaseReceiver {
public static String registrationID=null;
public C2DMReceiver() {
// Email address currently not used by the C2DM Messaging framework
super("xxx");
}
/* (non-Javadoc)
* @see com.android.aspark.notification.C2DMBaseReceiver#onRegistered(android.content.Context, java.lang.String)
*/
@Override
public void onRegistered(Context context, String registrationId)
throws java.io.IOException {
registrationID=registrationId;
Log.e("C2DM", "Registration ID arrived: Fantastic!!!");
Log.e("C2DM", registrationId);
//--------------------server call------------------------------------
String buildVersion = Build.VERSION.RELEASE;
Log.i("device type", deviceType );
};
/* (non-Javadoc)
* @see com.android.aspark.notification.C2DMBaseReceiver#onMessage(android.content.Context, android.content.Intent)
*/
@Override
public void onMessage(Context context, Intent intent) {
String action = intent.getAction();
// On Notification recived extract the Message
// "Payload"..............................
Log.w("C2DM", "Message Receiver called");
if ("com.google.android.c2dm.intent.RECEIVE".equals(action)) {
Log.w("C2DM", "Received message");
// extracting the payload data with key value ...........
final String payload = intent.getStringExtra("payload");
// expecting a unique notification id from server.......................
final int notificationID=Integer.parseInt(intent.getStringExtra("notificationid"));
}
Log.d("C2DM", "dmControl: payload = " + payload);
Log.d("C2DM", "dmControl: notificationID = " + notificationID);
//Auto incrementing for testing
// Note: Send this to my application server to get the real data
// Lets make something visible to show that we received the message
createNotification(context, payload,notificationID);
}
Log.e("C2DM", "Message Recived : Fantastic!!!");
}
@Override
public void onError(Context context, String errorId) {
Log.e("C2DM", "Error occured!!!");
}
/**
* ---------------Displaying Notification on status bar ---------------------
* @param context
* @param payload
*/
public void createNotification(Context context, String payload,int notificationID) {
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
int icon = R.drawable.notification_icon;
CharSequence tickerText = "New Message";
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
CharSequence contentTitle = "";
CharSequence contentText = payload;
Intent notificationIntent = new Intent(this,OnMessageReceiveActivity.class);
notificationIntent.putExtra("payload", payload);
notificationIntent.putExtra("notificationid", ""+notificationID);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
mNotificationManager.notify(notificationID, notification);
}
}
OnMessageReceiveActivity.java
public class OnMessageReceiveActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.onmessagereceiveactivity);
}
}
RegisterActivity.java
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();
C2DMRegistration.register(this);
}
}
res/
onmessagereceiveactivity.xml
<?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">
<TextView android:id="@+id/editText1" android:layout_height="wrap_content"
android:layout_width="match_parent" android:text="You are in new activity"
></TextView>
</LinearLayout>
main.xml
<?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:id="@+id/button1" android:text="Register for Push Notification"
android:layout_height="wrap_content" android:onClick="register"
android:layout_width="wrap_content"></Button>
</LinearLayout>
AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="YOUR_PACKAGE_NAME"
android:versionCode="1"
android:versionName="1.0" >
<permission
android:name="YOUR_PACKAGE_NAME.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="YOUR_PACKAGE_NAME.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.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_CORSE_LOCATION"/>
<application
android:icon="@drawable/ic_launcher"
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>
<activity android:name=".OnMessageReceiveActivity"/>
<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=".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="YOUR_PACKAGE_NAME" />
</intent-filter>
<!-- Receive the registration id -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="YOUR_PACKAGE_NAME" />
</intent-filter>
</receiver>
<receiver
android:name=".C2DMBroadcastReceiver"
>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RETRY"/>
<category android:name="YOUR_PACKAGE_NAME" />
</intent-filter>
</receiver>
</application>
</manifest>
You need to declare few more things
Value
->
String
:
<string
name="email_id"></string>
DRAWABLE:
1.notification_icon.png
Now you can run your Android Client , in response you will get the auth KEY( hash value ) . Check your LogCat .Do not change the permission and package name in manifest file.
*It will show the notifications on the status bar with a fixed message ,it will not show the message that is sent from the server side.
*for server side check my other post .C2DM Server.
Enjoy....
Do post your doubts, queries or suggestions in this blog.