3

First of all please don't make this question as duplicate or anything else because all other don't cover my issue.

I have an issue with push notification. I have implemented push notification in my app using gcm and make a jar with its source code. Now I have distributed it with my res folder for integration. Its working fine if host app don't implement push notification its own. If host app implement push notification its own then my integrated app doesn't receive push.

I went through this post : Register GCM from a library project

I have used below addition in the app in which I have integrated my jar:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />        
<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Creates a custom permission so only this app can receive its messages. -->
<permission
    android:name="HOST_APP_PACKAGE.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="HOST_APP_PACKAGE.permission.C2D_MESSAGE" />
<!-- This app has permission to register and receive data message. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Network State Permissions to detect Internet status -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Permission to vibrate -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

And below is my receiver:

<receiver
      android:name="MY_JAR_APP_PACKAGE.PushLibraryBroadcastReceiver"
      android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
          <!-- Receives the actual messages. -->
          <action android:name="com.google.android.c2dm.intent.RECEIVE" />
          <!-- Receives the registration id. -->
          <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
          <category android:name="HOST_APP_PACKAGE" />
      </intent-filter>
  </receiver>

My PushLibraryBroadcastReceiver class code in jar:

public class PushLibraryBroadcastReceiver extends GCMBroadcastReceiver
{
    /**
     * Gets the class name of the intent service that will handle GCM messages.
     */
    @Override
    protected String getGCMIntentServiceClassName(Context context) {
        return "MY_JAR_APP_PACKAGE.GCMIntentService";
    }
}
Community
  • 1
  • 1
Harish Godara
  • 2,388
  • 1
  • 14
  • 28
  • what you mean when saying host app implement `push notificatin` its own ? – CompEng Apr 17 '14 at 07:35
  • Means the app which integrate my app – Harish Godara Apr 17 '14 at 07:39
  • No, its not giving any error but not sending any notification in integrated app. – Harish Godara Apr 17 '14 at 11:33
  • You should post some code - give an example of an app that uses your jar and implements its own GCM. Show us the manifest and the GCM related code. Show us how the app is using your jar. – Eran Apr 17 '14 at 15:27
  • Hi @Eran I have edited my question and added important implementation here, if you need anything else then please let me know I'll provide that also, but I need help here! – Harish Godara Apr 18 '14 at 06:41
  • What do you expect to happen when you have two GCM implementations in the same app? (one via your library and one direct implementation) Are you registering twice to GCM? Are you using two different project IDs (sender IDs) for the registrations? Do you send the registration IDs to different servers? And when a message arrives, which broadcast receiver should handle it? – Eran Apr 18 '14 at 14:26
  • I'm using two different Sender IDs, And in both project registering to GCM, My server is different for both (What if server is same for both), I want that my both app receive push. – Harish Godara Apr 21 '14 at 12:57
  • @HarishGodara Did you see my answer? Does it solve your problem? – Eran Apr 29 '14 at 15:48

1 Answers1

2

Based on your clarification above, you need each broadcast receiver (that of your library and that of the host app) to take care of its own messages and ignore messages intended for the other broadcast receiver.

Since you are using different sender IDs to register to GCM in your library and in your host app, you can use that to determine which message should be handled by which broadcast receiver.

First of all, I'd suggest that you stop extending the deprecated GCMBroadcastReceiver class. My solution relies on not using it (though you might be able to make it work with the old receiver by changing its code).

Then following receiver is based on the new version of the official GCM Demo App.

public class PushLibraryBroadcastReceiver extends WakefulBroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getExtras ().get("from").equals (SENDER_ID_OF_LIBRARY) {
          // Explicitly specify that GcmIntentService will handle the intent.
          ComponentName comp = new ComponentName(
            GcmIntentService.class.getPackage().getName(),
            GcmIntentService.class.getName());
          // Start the service, keeping the device awake while it is launching.
          startWakefulService(context, (intent.setComponent(comp)));
          setResultCode(Activity.RESULT_CANCEL);
        } else
          setResultCode(Activity.RESULT_OK);
        }
    }
}

I made two changes from the Demo's implementation :

  1. get the package name of the intent service explicitly (since using context.getPackageName() will return the main package of the host app, which is not what you need).
  2. compare the "from" field of the message to the sender ID of the library and handle the message only if it comes from that sender. Once the message is handled, the result is set to Activity.RESULT_CANCEL, to prevent the broadcast from being handled by the broadcast receiver of the host app.

If you stop using the old GCMBroadcastReceiver, you should change your intent service to something like this (again, this is taken from the demo) :

protected void onHandleIntent(Intent intent) {
    Bundle extras = intent.getExtras();
    GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
    // The getMessageType() intent parameter must be the intent you received
    // in your BroadcastReceiver.
    String messageType = gcm.getMessageType(intent);

    if (!extras.isEmpty()) {  // has effect of unparcelling Bundle
        /*
         * Filter messages based on message type. Since it is likely that GCM will be
         * extended in the future with new message types, just ignore any message types you're
         * not interested in, or that you don't recognize.
         */
        if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
            sendNotification("Send error: " + extras.toString());
        } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
            sendNotification("Deleted messages on server: " + extras.toString());
        // If it's a regular GCM message, do some work.
        } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
            // This loop represents the service doing some work.
            for (int i = 0; i < 5; i++) {
                Log.i(TAG, "Working... " + (i + 1)
                        + "/5 @ " + SystemClock.elapsedRealtime());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
            Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());
            // Post notification of received message.
            sendNotification("Received: " + extras.toString());
            Log.i(TAG, "Received: " + extras.toString());
        }
    }
    // Release the wake lock provided by the WakefulBroadcastReceiver.
    GcmBroadcastReceiver.completeWakefulIntent(intent);
}

I'm assuming your GCMIntentService class extends the deprecated GCMBaseIntentService. You should extend IntentService instead, and move the logic from onMessage to onHandleIntent.

You should also switch to the new way of registering to GCM, using GoogleCloudMessaging.register, which doesn't require any handling in the intent service class. All the handling will be done in the activity that performs the registration, as demonstrated here.

Finally, if the broadcast receiver of the host app doesn't behave similarly to your library's broadcast receiver (i.e. handling only the messages it is supposed to handle), you would still have a problem if the host app's broadcast receiver is triggered before your library's broadcast receiver. You can avoid that by adding the android:priority attribute to the intent-filter of both receivers, and giving your library's receiver a higher priority. That would ensure that the library's broadcast receiver is always triggered first.

I must say that I never tested an app with two broadcast receivers, so I can't guarantee that using the priority attribute works, but it should work based on the documentation I read :

Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • If we are uploading our app(in which we have integrated library project) on play store then which app_id should I associate for GCM? – Harish Godara Sep 29 '14 at 05:06
  • @Harish I've never tried it myself, but I'd say use the package of the app (not the library project) . – Eran Sep 29 '14 at 06:29
  • My scenario is that I made a library project and implemented everything about GCM into it, then I integrate this lib to my app and while developing I get notification into my app through lib, but when I uploaded it on play store, notifications are not working. What should I do, I need your help, you are the only one who can save me. – Harish Godara Sep 29 '14 at 07:33