Search code examples
androidgoogle-cloud-messaginggcmlistenerservice

Faking GCM messages in Android apps


Background:

I would like an easy way to fake receiving a push notification within my android app.

I use SNS and GCM to send push notifications, however I don't want to have to send a real notification each time I'm testing my receiver handling.

I have created a 'dev menu' in the main activity which contains items like 'clear cache', 'fake server error' and so on, so I added a 'fake push notification' item to this list. (This only appears on debug builds)

The idea being I would programatically call the onMessageReceived method on my implementation of GcmListenerService when the menu item is selected, therefor faking a push notification being received.

Problems

In order to access the onMessageReceived method, I have to have access to the GcmListenerService, which is a service defined in my manifest like so (as per the docs):

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

I have tried using my existing Dagger2 setup to inject an instance into the activity, but I don't know how to 'provide' the correct instance to dagger.

Newing up a new MyGcmListenerService obviously calls the onMessageReceived method, but has no application context so I get a NPE when creating the Intent:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.content.ContextWrapper.getPackageName(ContextWrapper.java:132)
at android.content.ComponentName.<init>(ComponentName.java:128)at android.content.Intent.<init>(Intent.java:4868)
at uk.co.blah.blah.service.MyGcmListenerService.onMessageReceived(MyGcmListenerService.java:30)
at uk.co.blah.blah.DashboardActivity.onOptionsItemSelected(DashboardActivity.java:297)
at android.app.Activity.onMenuItemSelected(Activity.java:3203)

Dagger Provider: (I don't expect this to work, as its not the correct GcmListenerService)

@Provides
@Singleton
MyGcmListenerService provideMyGcmListenerService() {
    return new MyGcmListenerService();
}

Menu item handler:

if (id == R.id.fake_notification) {
        Bundle bundle = new Bundle();
        bundle.putString("message", "Test notification");
        gcmListenerService.onMessageReceived("fakeNotification", bundle);
}

My implementation of GcmListenerService:

public class MyGcmListenerService extends GcmListenerService {

@Override
public void onMessageReceived(String from, Bundle data) {
    String message = data.getString("message");
    Timber.d("From: %s", from);
    Timber.d("Message: %s", message);

    // FIXME Fails here
    Intent notificationAction = new Intent(this, DashboardActivity.class); 

    ... do some other stuff to create the notification

}

}

I think I might be going about this all wrong, so am open to any other technique. However, please don't suggest options involving something external to the app (some fake server or something) as the whole point of this is to spoof a notification within the app, using only actions available in the app... Also useful to demo notifications to stakeholders etc.


Solution

  • It's probably easiest to have your MyGcmListenerService.onMessageReceived just call some method, and your test call the same method.

    But if you really want to instantiate your MyGcmListenerService, then you need to follow the usual steps to start and connect to an Android Service.