📜 ⬆️ ⬇️

Writing an Android application with Cloud to Device Messaging (C2DM) support



Hello, readers Habrahabr!

In this post I want to pay attention to the C2DM service from Google and try to tell you how to implement support for this service in your Android application. Recall that C2DM is a special service that provides an API for sending messages to applications installed on Android devices. Using this service is an indispensable way if you need to send a message to a user application that is registered in the system but is not currently active.
')
Although C2DM is one of the fundamental features of the Android platform, there is little information about it in runet. Attempting to change this situation is one of the tasks of this post.

Under the cut, I'll tell you how to write simple client and server applications, show some “pitfalls”, and also give links to code examples.

Cloud to Device Messaging


But for now a bit of theory. As I wrote above, C2DM is a message delivery service from user applications to Android applications, you can read more here . Those. This is an analogue of Push Notification, in terms of Apple. The general scheme of interaction is shown in this picture found on the Internet:
Three main parts are visible from the diagram:
  1. C2DM service. “Cloud” from Google, responsible for delivering messages. As well as registration / deregistration of devices and authentication.
  2. Client part. An Android application that accepts messages.
  3. Server part A client application (“3rd party server” in Google terms) that sends messages.
Details about the process of sending messages written on the link that I cited above, but if it is short, then it consists of the following:
  1. The Android application registers with C2DM, thereby announcing its readiness to receive messages, and receives a Registration ID . Registration ID is a unique identifier of the device, knowing that the backend can send a message to the recipient. When registering, you need to specify the name of the Google account, but I will write about this later when I consider the client part.
  2. The application sends the Registration ID of the server part so that it knows to whom it is possible to send messages.
  3. The server part is authenticated on the Google server, using the same account as the Android application when registering with C2DM, and gets Auth Token . Details about authorization \ authentication in Google’s services are written here .
  4. Knowing the Registration ID and Auth Token, the server part sends a message to the Android application.
Now you can go directly to the development. Let's start with the client part (Android application).

Client part


To use C2DM Android, the device must meet the following requirements:
  1. It must be a version of Android 2.2 or higher. On previous versions, C2DM is not supported!
  2. There must be any working Google account. If you are running the Android Market, then you have it.
If everything is done, you can proceed to create the application. For this we will use the Eclipse + Android SDK. You can create the simplest “Hello, World” on TextView. I will not describe the procedure for creating an application, so much has been written about it, for example, here or here . It should turn out something like:


Note: If you do not have Android 2.2, you can use the emulator, you only need to use Google APIs (API 8 or higher), not the standard SDK Platform. The standard SDK does not support Google accounts.

After you have created an application, you must register it on a special site where you need to specify the package name (package name), and, most importantly, your Google email account that will be used to forward messages. It is this mail that we will use in the client during registration and on the server during authentication. You must receive a letter with registration confirmation, after this letter the specified account can be used to work with C2DM.

Next, we proceed to the modification of our “Hello, World!” Application. We need to implement the code responsible for registering / unregistering, and, of course, receiving messages. It’s really hard to write it yourself the first time. It is difficult to take into account all the nuances, so we will use the code, which is kindly provided by Google itself as an example . Downloading a ready-made set of classes for working with C2DM on the client side from svn storage . And we add them to the project, to the directory " src \ com \ google \ android \ c2dm ". It should turn out like this:


Now you need to implement the methods of the abstract class C2DMBaseReceiver, for this we will write the class C2DMReceiver, while only logging calls, and put it next to the Main. Content Class C2DMReceiver:
  1. package com.home.c2dmtest ;
  2. import com.google.android.c2dm.C2DMBaseReceiver ;
  3. import android.app.Notification ;
  4. import android.app.NotificationManager ;
  5. import android.app.PendingIntent ;
  6. import android.content.Context ;
  7. import android.content.Intent ;
  8. import android.util.Log ;
  9. public class C2DMReceiver extends C2DMBaseReceiver {
  10. public C2DMReceiver ( ) {
  11. super ( "<your mail> @ gmail.com" ) ;
  12. }
  13. @ Override
  14. public void onRegistered ( Context context, String registrationId ) {
  15. Log w ( "onRegistered" , registrationId ) ;
  16. }
  17. @ Override
  18. public void onUnregistered ( Context context ) {
  19. Log w ( "onUnregistered" , "" ) ;
  20. }
  21. @ Override
  22. public void onError ( Context context, String errorId ) {
  23. Log w ( "onError" , errorId ) ;
  24. }
  25. @ Override
  26. protected void onMessage ( Context context, Intent intent ) {
  27. Log w ( "onMessage" , "" ) ;
  28. }
  29. }

But the application is not yet ready to work with C2DM, since Required rights are not set and BroadcastReceiver is not registered. To fix this, you need to modify AndroidManifest.xml, how to do this you can read here . For my example, the file looks like this:
  1. <? xml version = "1.0" encoding = "utf-8" ?>
  2. <manifest xmlns: android = " schemas.android.com/apk/res/android"
  3. android: versionCode = "1"
  4. android: versionName = "1.0" package = "com.home.c2dmtest" >
  5. <application
  6. android: debuggable = "true"
  7. android: label = "@ string / app_name" >
  8. <activity android: name = "Main"
  9. android: label = "@ string / app_name"
  10. android: theme = "@android: style / Theme.NoTitleBar" >
  11. <intent-filter >
  12. <action android: name = "android.intent.action.MAIN" />
  13. <category android: name = "android.intent.category.LAUNCHER" />
  14. </ intent-filter >
  15. </ activity >
  16. <service android: name = ".C2DMReceiver" />
  17. <receiver
  18. android: name = "com.google.android.c2dm.C2DMBroadcastReceiver"
  19. android: permission = "com.google.android.c2dm.permission.SEND" >
  20. <intent-filter >
  21. <action android: name = "com.google.android.c2dm.intent.RECEIVE" />
  22. <category android: name = "com.home.c2dmtest" />
  23. </ intent-filter >
  24. <intent-filter >
  25. <action android: name = "com.google.android.c2dm.intent.REGISTRATION" />
  26. <category android: name = "com.home.c2dmtest" />
  27. </ intent-filter >
  28. </ receiver >
  29. </ application >
  30. <uses-sdk android: minSdkVersion = "8" />
  31. <permission
  32. android: name = "com.home.c2dmtest.permission.C2D_MESSAGE"
  33. android: protectionLevel = "signature" />
  34. <uses-permission android: name = "com.home.c2dmtest.permission.C2D_MESSAGE" />
  35. <uses-permission android: name = "com.google.android.c2dm.permission.RECEIVE" />
  36. <uses-permission android: name = "android.permission.INTERNET" />
  37. <uses-permission android: name = "android.permission.WAKE_LOCK" />
  38. </ manifest >

Note: The right android.permission.WAKE_LOCK is not needed to use C2DM, but we will need it later, and in order not to bring this file twice, I decided to add it in advance.

Now you can register a device in C2DM, for this we call the following code in OnCreate:
C2DMessaging. register ( this , "<yourmail> @ gmail.com" ) ;

If everything went fine, then after a couple of seconds, the new Regestration ID will fall into the onRegistered method of the C2DMReceiver class. If this does not happen, you need to look at the LogCat log for errors.

You can check out by calling the method:
C2DMessaging. unregister ( this ) ;

Get current Registration ID:
String id = C2DMessaging. getRegistrationId ( this ) ;

Registration ID can change at any second, i.e. Google can send its new value, and, therefore, such a situation must be able to handle. In our example, everything is already done for this, the implementation of this mechanism is in the C2DMBaseReceiver class, the handleRegistration method.

The final project looks like this:


Now we need to make our project more visual, we will expand the processing of messages. Let Notification appear when a new message arrives, and when it is selected, our application will be launched with the text specified by the server.

Ok, for this we modify the onMessage code as follows:
  1. @ Override
  2. protected void onMessage ( Context context, Intent receiveIntent )
  3. {
  4. String data = receiveIntent. getStringExtra ( "message" ) ;
  5. if ( data ! = null )
  6. {
  7. Log w ( "C2DMReceiver" , data ) ;
  8. Intent intent = new Intent ( this , Main. Class ) ;
  9. intent. putExtra ( "message" , data ) ;
  10. NotificationManager mManager = ( NotificationManager )
  11. getSystemService ( Context . NOTIFICATION_SERVICE ) ;
  12. Notification notification = new Notification ( android. R. Drawable . Ic_dialog_info ,
  13. "My C2DM message" , System . currentTimeMillis ( ) ) ;
  14. notification. setLatestEventInfo ( context, "App Name" , "C2DM notification" ,
  15. PendingIntent. getActivity ( this . getBaseContext ( ) , 0 ,
  16. intent, pendingintent. FLAG_CANCEL_CURRENT ) ) ;
  17. mManager. notify ( 0 , notification ) ;
  18. }
  19. }

message is the message identifier that the server sends. We will add it to the new Intent so that we can get it in Main.

Modify Main to display the transmitted message:
  1. @ Override
  2. public void onCreate ( Bundle savedInstanceState ) {
  3. super . onCreate ( savedInstanceState ) ;
  4. TextView view = new TextView ( this ) ;
  5. String message = getIntent ( ) . getStringExtra ( "message" ) ;
  6. if ( message == null )
  7. view. setText ( "Hello, World !!!" ) ;
  8. else
  9. view. setText ( "Your message:" + message ) ;
  10. setContentView ( view ) ;
  11. String id = C2DMessaging. getRegistrationId ( this ) ;
  12. if ( id == "" )
  13. {
  14. C2DMessaging. register ( this , "<yourmail> @ gmail.com" ) ;
  15. }
  16. }

In real life, we certainly need to write a Registration ID transfer mechanism to the server and other trifles, but since For this example, we’ll stop here and the Registration ID will be hard-coded on the server.

Server part


First you need to get Auth Token. This is well written in this blog (There is also a lot of other useful information). In general, there is an example, everything is in Russian, so we will not repeat it and we will assume that we received it.

So we have a Registration ID and Auth Token. Things are easy, you need to send a message. For some reason, there is very little information about this part of the Internet, although there is nothing difficult here.

We need to establish a connection with the Google server, form a request in the correct format. And that's all. A simplified example of sending a message is given in the following code:
  1. public boolean sendData (
  2. String authToken
  3. String registrationId,
  4. String collapse
  5. String key
  6. String value )
  7. throws IOException {
  8. // Set Registration ID
  9. StringBuilder postDataBuilder = new StringBuilder ( ) ;
  10. postDataBuilder. append ( "registration_id" ) .
  11. append ( "=" ) . append ( registrationId ) ;
  12. // Set collapse_key - groups messages if the collapse key is the same, and
  13. // a device, for example, is turned off, then only one message will be sent,
  14. // not all at once.
  15. postDataBuilder. append ( "&" ) . append ( "collapse_key" ) . append ( "=" ) .
  16. append ( collapse ) ;
  17. // Add the transmitted date, in the format <data.> <Key> = <value>
  18. postDataBuilder. append ( "&" ) . append ( "data." + key ) . append ( "=" ) .
  19. append ( URLEncoder . encode ( value, "UTF-8" ) ) ;
  20. byte [ ] postData = postDataBuilder. toString ( ) . getBytes ( "UTF-8" ) ;
  21. URL url = new URL ( " android.clients.google.com/c2dm/send" ) ;
  22. // Establish connection
  23. // Proxy proxy = new Proxy (Proxy.Type.HTTP, new InetSocketAddress ("lazerboy.local", 8080));
  24. HttpsURLConnection conn = ( HttpsURLConnection ) url. openConnection ( / * proxy * / ) ;
  25. conn. setDoOutput ( true ) ;
  26. conn. setHostnameVerifier ( this . new MyHostnameVerifier ( ) ) ;
  27. conn. setRequestMethod ( "POST" ) ;
  28. conn. setRequestProperty ( "Content-Type" ,
  29. "application / x-www-form-urlencoded; charset = UTF-8" ) ;
  30. conn. setRequestProperty ( "Content-Length" , Integer . toString ( postData. length ) ) ;
  31. conn. setRequestProperty ( "Authorization" , "GoogleLogin auth =" + authToken ) ;
  32. OutputStream out = conn. getOutputStream ( ) ;
  33. out. write ( postData ) ;
  34. out. close ( ) ;
  35. // Get the response code.
  36. int responseCode = conn. getResponseCode ( ) ;
  37. if ( responseCode == HttpServletResponse. SC_UNAUTHORIZED ||
  38. responseCode == HttpServletResponse. SC_FORBIDDEN ) {
  39. System . out . printf ( "Unauthorized - need token" ) ;
  40. return false ;
  41. }
  42. if ( responseCode == HttpServletResponse. SC_OK )
  43. {
  44. System . out . printf ( "Data sent to device!" ) ;
  45. return true ;
  46. }
  47. System . out . printf ( "Something wrong, response message:" , conn. getResponseMessage ( ) ) ;
  48. return false ;
  49. }

One very important detail is missing here: the processing of messages from the server. For example, the situation when Auth Token becomes outdated is not processed in any way. But I wanted to write an example of a working application, but not ready to use library. Moreover, everything you need can be found in the source from Google, for example, here .

If the code runs successfully, the user on his device will see our notification in the notification area, and if he clicks on it, then our application will start with the text from the server. Hooray, this is exactly what we wanted.

Conclusion


I showed how it is possible to fasten C2DM support to an Android application without a special headache. What you need to do, both on the client side and on the server side. Of course, there were "white spots", but again, the main task was to bring ideas and show code examples to make it easy for people reading this post to start developing applications with C2DM support.

useful links

  1. Android Cloud to Device Messaging
  2. Place where you need to register an account to use C2DM
  3. Russian-language blog on C2DM and Android
  4. Google Sample
  5. Another blog on the topic
  6. C2DM usage example

Source: https://habr.com/ru/post/116106/


All Articles