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";
}
}
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 :
- 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).
- 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.