Spiri
Spiri

Reputation: 1303

Android: How to use Parse as an alternative GCM push notification provider?

SEE EDIT#2 at the end of the question (Google updated the way push is implemented so it became easier to handle gcm and parse together)

I already use GCM inside a application and I would like to add parse as an alternative. This is what I have now (all the permissions are correctly declared):

<service 
   android:name="com.mypackagename.GCMIntentService" 
   android:enabled="true" />
<receiver 
   android:name="com.google.android.gcm.GCMBroadcastReceiver" 
   android:permission="com.google.android.c2dm.permission.SEND" >
   <intent-filter>
       <action android:name="com.google.android.c2dm.intent.RECEIVE" />
       <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
       <category android:name="com.mypackagename.GCMIntentService" />
   </intent-filter>
</receiver>

The 'GCMIntentService' (inherits from 'GCMBaseIntentService') class handles the server registration and receiving of messages - it works fine and all the push messages are received. The server sometimes sends custom data so I handle the messages myself and create the notifications programmatically (the intent used when the notification is clicked has some important extras sent from the server).

I would like to somehow make parse behave in the same way in order to be able to send channel pushes from the parse website and create my own notifications, but everything I tried failed (following the android push tutorial isn't really working for my problem). Is there anyone who tried a similar thing? I'm kind of out of ideas after spending a lot of time tweaking the push guides/tutorials - sometimes I don't receive any notifications; sometimes both parse and my receiver are called and I get double notifications. I also tried to register using parse REST apis and handle everything myself but found out it isn't possible on Android.

So, how could I handle both parse pushes and the traditional gcm pushes (using my server) in such a way that I have access to both notifications and I can build them from scratch (create my own pending notifications with the required extras)?

EDIT#1:

The first thing I tried was to use the parse service and have a single broadcast receiver to handle the GCM messages:

AndroidMaifest.xml:

<service 
    android:name="com.mypackagename.GCMIntentService" 
    android:enabled="true" />
<service android:name="com.parse.PushService"
    android:enabled="true"/>
<receiver 
    android:name="com.google.android.gcm.GCMBroadcastReceiver" 
    android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="com.mypackagename.GCMIntentService" />
    </intent-filter>
</receiver>

And the parse library requires the following initializations:

Parse.initialize(context, appId, apiKey);
PushService.setDefaultPushCallback(context, MainActivity.class);
// I'm subscribing to channel push because I send channel pushes from the
// parse console
PushService.subscribe(context, MDConstants.PARSE_PUSH_CHANNEL, MainActivity.class);
ParseInstallation.getCurrentInstallation().saveInBackground();

The problem is that I receive the notifications from my other provider but I don't receive anything from parse (all the permissions are declared) and I get the following error from the parse library (when receiving the error the parse registration is not properly done - I can't see my device in the parse console):

E/com.parse.ManifestInfo(11677): Cannot use GCM for push because the app manifest is missing some required declarations. Please make sure that these permissions are declared as children of the root <manifest> element:
E/com.parse.ManifestInfo(11677): 
E/com.parse.ManifestInfo(11677): <uses-permission android:name="android.permission.INTERNET" />
E/com.parse.ManifestInfo(11677): <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
E/com.parse.ManifestInfo(11677): <uses-permission android:name="android.permission.VIBRATE" />
E/com.parse.ManifestInfo(11677): <uses-permission android:name="android.permission.WAKE_LOCK" />
E/com.parse.ManifestInfo(11677): <uses-permission android:name="android.permission.GET_ACCOUNTS" />
E/com.parse.ManifestInfo(11677): <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
E/com.parse.ManifestInfo(11677): <permission android:name="com.mypackagename.permission.C2D_MESSAGE" android:protectionLevel="signature" />
E/com.parse.ManifestInfo(11677): <uses-permission android:name="com.mypackagename.permission.C2D_MESSAGE" />
E/com.parse.ManifestInfo(11677): 
E/com.parse.ManifestInfo(11677): Also, please make sure that these services and broadcast receivers are declared as children of the <application> element:
E/com.parse.ManifestInfo(11677): 
E/com.parse.ManifestInfo(11677): <service android:name="com.parse.PushService" />
E/com.parse.ManifestInfo(11677): <receiver android:name="com.parse.GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
E/com.parse.ManifestInfo(11677):   <intent-filter>
E/com.parse.ManifestInfo(11677):     <action android:name="com.google.android.c2dm.intent.RECEIVE" />
E/com.parse.ManifestInfo(11677):     <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
E/com.parse.ManifestInfo(11677):     <category android:name="com.mypackagename" />
E/com.parse.ManifestInfo(11677):   </intent-filter>
E/com.parse.ManifestInfo(11677): </receiver>
E/com.parse.PushService(11677): Tried to use push, but this app is not configured for push due to: Push is not configured for this app because the app manifest is missing required declarations. Please add the following declarations to your app manifest to support either GCM or PPNS for push (or both). To enable GCM support, please make sure that these permissions are declared as children of the root <manifest> element:
E/com.parse.PushService(11677): 
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.INTERNET" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.VIBRATE" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.WAKE_LOCK" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.GET_ACCOUNTS" />
E/com.parse.PushService(11677): <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
E/com.parse.PushService(11677): <permission android:name="com.mypackagename.permission.C2D_MESSAGE" android:protectionLevel="signature" />
E/com.parse.PushService(11677): <uses-permission android:name="com.mypackagename.permission.C2D_MESSAGE" />
E/com.parse.PushService(11677): 
E/com.parse.PushService(11677): Also, please make sure that these services and broadcast receivers are declared as children of the <application> element:
E/com.parse.PushService(11677): 
E/com.parse.PushService(11677): <service android:name="com.parse.PushService" />
E/com.parse.PushService(11677): <receiver android:name="com.parse.GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
E/com.parse.PushService(11677):   <intent-filter>
E/com.parse.PushService(11677):     <action android:name="com.google.android.c2dm.intent.RECEIVE" />
E/com.parse.PushService(11677):     <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
E/com.parse.PushService(11677):     <category android:name="com.mypackagename" />
E/com.parse.PushService(11677):   </intent-filter>
E/com.parse.PushService(11677): </receiver>
E/com.parse.PushService(11677): To enable PPNS support, please make sure that these permissions are declared as children of the root <manifest> element:
E/com.parse.PushService(11677): 
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.INTERNET" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.VIBRATE" />
E/com.parse.PushService(11677): <uses-permission android:name="android.permission.WAKE_LOCK" />
E/com.parse.PushService(11677): 
E/com.parse.PushService(11677): Also, please make sure that these services and broadcast receivers are declared as children of the <application> element:
E/com.parse.PushService(11677): 
E/com.parse.PushService(11677): <service android:name="com.parse.PushService" />
E/com.parse.PushService(11677): <receiver android:name="com.parse.ParseBroadcastReceiver">
E/com.parse.PushService(11677):   <intent-filter>
E/com.parse.PushService(11677):     <action android:name="android.intent.action.BOOT_COMPLETED" />
E/com.parse.PushService(11677):     <action android:name="android.intent.action.USER_PRESENT" />
E/com.parse.PushService(11677):   </intent-filter>
E/com.parse.PushService(11677): </receiver>

EDIT#2:

I updated the way gcm push was handled based on the google push notification developer guide. While implementing the class that extends 'GcmListenerService', you can now easily check if the 'from' arguments is the same as your google project id used to register for push.

public class MyGcmListenerService extends GcmListenerService {
    @Override
    public void onMessageReceived(String from, Bundle data) {
        // only handle gcm messages that come from the same project id used to register
        if (from.equals("Your google project id)) {
            // handle the gcm push
        }
    }
}

Also, parse updated their libraries (I'm using '1.9.4' right now) and you can subclass the 'ParsePushBroadcastReceiver' to handle the notifications as you'd like. See the guide here for a basic implementation.

Parse initialization in the 'onCreate' method of your 'Application' class:

Parse.initialize(this, "your parse app id", "your parse client key");

// subscribing to a channel
ParsePush.subscribeInBackground("your channel name", new SaveCallback() {
    @Override
    public void done(ParseException e) {
        if (e == null) {
            Log.d("com.parse.push", "successfully subscribed to the broadcast channel.");
        } else {
            Log.e("com.parse.push", "failed to subscribe for push");
        }
    }
});

The broadcast receiver implementation:

public class MyParsePushBroadcastReceiver extends ParsePushBroadcastReceiver {
    @Override
    protected void onPushReceive(Context context, Intent intent) {
        // handle the parse push notification
    }
}

The manifest declaration for both parse and gcm:

...

<!-- GCM listener service -->
<service
    android:name=".MyGcmListenerService"
    android:exported="false" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    </intent-filter>
</service>

...

<!-- Parse broadcast receiver -->
<receiver android:name=".MyParsePushBroadcastReceiver" android:exported="false">
    <intent-filter>
        <action android:name="com.parse.push.intent.RECEIVE" />
        <action android:name="com.parse.push.intent.DELETE" />
        <action android:name="com.parse.push.intent.OPEN" />
    </intent-filter>
</receiver>

...

I only added the service and the receiver, but you need to make sure you follow the GCM guide and the Parse push guides to have a full implementation (for example, google also added a way to handle the token refresh - a sample containing full code samples can be found here).

Upvotes: 2

Views: 6462

Answers (1)

Eran
Eran

Reputation: 393781

If I understand correctly, you want your code to handle all the incoming GCM messages, regardless of their source (which can be either your server or Parse website), which means you don't want the Parse code in your app do handle them.

You can achieve this by declaring only a single broadcast receiver handling com.google.android.c2dm.intent.RECEIVE action in your manifest. That would be your GCMBroadcastReceiver class, which would handle all the arriving GCM messages.

The behavior you are currently experiencing can happen when you declare two broadcast receivers that handle the same action.

Upvotes: 2

Related Questions