Skip to content

Commit 7493442

Browse files
authored
Merge pull request #221 from OneSignal/filtering_other_gcm_receivers
Added filterOtherGCMReceivers method
2 parents e04a9d6 + 15d24ac commit 7493442

File tree

9 files changed

+187
-36
lines changed

9 files changed

+187
-36
lines changed

OneSignalSDK/onesignal/src/main/java/com/onesignal/ADMMessageHandler.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ public ADMMessageHandler() {
4848
@Override
4949
protected void onMessage(Intent intent) {
5050
Bundle bundle = intent.getExtras();
51-
52-
if (NotificationBundleProcessor.processBundle(this, bundle))
51+
52+
NotificationBundleProcessor.ProcessedBundleResult processedResult = NotificationBundleProcessor.processBundle(this, bundle);
53+
54+
if (processedResult.processed())
5355
return;
5456

5557
NotificationGenerationJob notifJob = new NotificationGenerationJob(this);

OneSignalSDK/onesignal/src/main/java/com/onesignal/GcmBroadcastReceiver.java

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Modified MIT License
33
*
4-
* Copyright 2016 OneSignal
4+
* Copyright 2017 OneSignal
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -33,15 +33,17 @@
3333
import android.content.Intent;
3434
import android.os.Bundle;
3535
import android.support.v4.content.WakefulBroadcastReceiver;
36+
import com.onesignal.NotificationBundleProcessor.ProcessedBundleResult;
3637

3738
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
3839

39-
private static final String GCM_RECEIVE = "com.google.android.c2dm.intent.RECEIVE";
40+
private static final String GCM_RECEIVE_ACTION = "com.google.android.c2dm.intent.RECEIVE";
4041
private static final String GCM_TYPE = "gcm";
42+
private static final String MESSAGE_TYPE_EXTRA_KEY = "message_type";
4143

4244
private static boolean isGcmMessage(Intent intent) {
43-
if (GCM_RECEIVE.equals(intent.getAction())) {
44-
String messageType = intent.getStringExtra("message_type");
45+
if (GCM_RECEIVE_ACTION.equals(intent.getAction())) {
46+
String messageType = intent.getStringExtra(MESSAGE_TYPE_EXTRA_KEY);
4547
return (messageType == null || GCM_TYPE.equals(messageType));
4648
}
4749
return false;
@@ -55,25 +57,53 @@ public void onReceive(Context context, Intent intent) {
5557
Bundle bundle = intent.getExtras();
5658
if (bundle == null || "google.com/iid".equals(bundle.getString("from")))
5759
return;
58-
59-
processOrderBroadcast(context, intent, bundle);
60+
61+
ProcessedBundleResult processedResult = processOrderBroadcast(context, intent, bundle);
62+
63+
// Null means this isn't a GCM / FCM message.
64+
if (processedResult == null) {
65+
setResultCode(Activity.RESULT_OK);
66+
return;
67+
}
68+
69+
// Prevent other GCM receivers from firing if;
70+
// This is a duplicated GCM message
71+
// OR app developer setup a extender service to handle the notification.
72+
if (processedResult.isDup || processedResult.hasExtenderService) {
73+
// Abort to prevent other GCM receivers from process this Intent.
74+
abortBroadcast();
75+
return;
76+
}
77+
78+
// Prevent other GCM receivers from firing if;
79+
// This is a OneSignal payload
80+
// AND the setting is enabled to allow filtering in this case.
81+
if (processedResult.isOneSignalPayload &&
82+
OneSignal.getFilterOtherGCMReceivers(context)) {
83+
84+
abortBroadcast();
85+
return;
86+
}
6087

6188
setResultCode(Activity.RESULT_OK);
6289
}
63-
64-
private static void processOrderBroadcast(Context context, Intent intent, Bundle bundle) {
90+
91+
private static ProcessedBundleResult processOrderBroadcast(Context context, Intent intent, Bundle bundle) {
6592
if (!isGcmMessage(intent))
66-
return;
67-
93+
return null;
94+
95+
ProcessedBundleResult processedResult = NotificationBundleProcessor.processBundle(context, bundle);
96+
6897
// Return if the notification will NOT be handled by normal GcmIntentService display flow.
69-
if (NotificationBundleProcessor.processBundle(context, bundle))
70-
return;
98+
if (processedResult.processed())
99+
return processedResult;
71100

72101
Intent intentForService = new Intent();
73102
intentForService.putExtra("json_payload", NotificationBundleProcessor.bundleAsJSONObject(bundle).toString());
74103
intentForService.setComponent(new ComponentName(context.getPackageName(),
75-
GcmIntentService.class.getName()));
104+
GcmIntentService.class.getName()));
76105
startWakefulService(context, intentForService);
106+
107+
return processedResult;
77108
}
78-
79109
}

OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -354,27 +354,35 @@ private static void processCollapseKey(NotificationGenerationJob notifJob) {
354354
}
355355

356356
// Return true to count the payload as processed.
357-
static boolean processBundle(Context context, final Bundle bundle) {
357+
static ProcessedBundleResult processBundle(Context context, final Bundle bundle) {
358+
ProcessedBundleResult result = new ProcessedBundleResult();
359+
358360
// Not a OneSignal GCM message
359361
if (OneSignal.getNotificationIdFromGCMBundle(bundle) == null)
360-
return true;
362+
return result;
363+
result.isOneSignalPayload = true;
361364

362365
prepareBundle(bundle);
363-
366+
367+
// NotificationExtenderService still makes additional checks such as notValidOrDuplicated
364368
Intent overrideIntent = NotificationExtenderService.getIntent(context);
365369
if (overrideIntent != null) {
366370
overrideIntent.putExtra("json_payload", bundleAsJSONObject(bundle).toString());
367371
WakefulBroadcastReceiver.startWakefulService(context, overrideIntent);
368-
return true;
372+
result.hasExtenderService = true;
373+
return result;
369374
}
375+
376+
// We already ran a getNotificationIdFromGCMBundle == null check above so this will only be true for dups
377+
result.isDup = OneSignal.notValidOrDuplicated(context, bundleAsJSONObject(bundle));
378+
if (result.isDup)
379+
return result;
370380

371-
boolean display = shouldDisplay(bundle.getString("alert") != null
372-
&& !"".equals(bundle.getString("alert")));
381+
String alert = bundle.getString("alert");
382+
boolean display = shouldDisplay(alert != null && !"".equals(alert));
373383

374384
// Save as a opened notification to prevent duplicates.
375385
if (!display) {
376-
if (OneSignal.notValidOrDuplicated(context, bundleAsJSONObject(bundle)))
377-
return true;
378386
saveNotification(context, bundle, true, -1);
379387
// Current thread is meant to be short lived.
380388
// Make a new thread to do our OneSignal work on.
@@ -384,8 +392,8 @@ public void run() {
384392
}
385393
}, "OS_PROC_BUNDLE").start();
386394
}
387-
388-
return !display;
395+
396+
return result;
389397
}
390398

391399
static boolean shouldDisplay(boolean hasBody) {
@@ -402,4 +410,15 @@ static JSONArray newJsonArray(JSONObject jsonObject) {
402410
jsonArray.put(jsonObject);
403411
return jsonArray;
404412
}
413+
414+
415+
static class ProcessedBundleResult {
416+
boolean isOneSignalPayload;
417+
boolean hasExtenderService;
418+
boolean isDup;
419+
420+
boolean processed() {
421+
return !isOneSignalPayload || hasExtenderService || isDup;
422+
}
423+
}
405424
}

OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public static class Builder {
106106
boolean mDisableGmsMissingPrompt;
107107
// Default true in 4.0.0 release.
108108
boolean mUnsubscribeWhenNotificationsAreDisabled;
109+
boolean mFilterOtherGCMReceivers;
109110

110111
// Exists to make wrapper SDKs simpler so they don't need to store their own variable before
111112
// calling startInit().init()
@@ -154,6 +155,11 @@ public Builder unsubscribeWhenNotificationsAreDisabled(boolean set) {
154155
mUnsubscribeWhenNotificationsAreDisabled = set;
155156
return this;
156157
}
158+
159+
public Builder filterOtherGCMReceivers(boolean set) {
160+
mFilterOtherGCMReceivers = set;
161+
return this;
162+
}
157163

158164
public void init() {
159165
OneSignal.init(this);
@@ -359,6 +365,8 @@ public static void init(Context context, String googleProjectNumber, String oneS
359365
foreground = contextIsActivity;
360366
appId = oneSignalAppId;
361367
appContext = context.getApplicationContext();
368+
369+
saveFilterOtherGCMReceivers(mInitBuilder.mFilterOtherGCMReceivers);
362370

363371
if (contextIsActivity) {
364372
ActivityLifecycleHandler.curActivity = (Activity) context;
@@ -372,7 +380,7 @@ public static void init(Context context, String googleProjectNumber, String oneS
372380

373381
OneSignalStateSynchronizer.initUserState(appContext);
374382

375-
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2)
383+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2)
376384
((Application)appContext).registerActivityLifecycleCallbacks(new ActivityLifecycleListener());
377385
else
378386
ActivityLifecycleListenerCompat.startListener();
@@ -1250,7 +1258,21 @@ static void saveUserId(String inUserId) {
12501258
editor.putString("GT_PLAYER_ID", userId);
12511259
editor.commit();
12521260
}
1253-
1261+
1262+
static boolean getFilterOtherGCMReceivers(Context context) {
1263+
SharedPreferences prefs = getGcmPreferences(context);
1264+
return prefs.getBoolean("OS_FILTER_OTHER_GCM_RECEIVERS", false);
1265+
}
1266+
1267+
static void saveFilterOtherGCMReceivers(boolean set) {
1268+
if (appContext == null)
1269+
return;
1270+
final SharedPreferences prefs = getGcmPreferences(appContext);
1271+
SharedPreferences.Editor editor = prefs.edit();
1272+
editor.putBoolean("OS_FILTER_OTHER_GCM_RECEIVERS", set);
1273+
editor.commit();
1274+
}
1275+
12541276
static void updateUserIdDependents(String userId) {
12551277
saveUserId(userId);
12561278
fireIdsAvailableCallback();

OneSignalSDK/onesignal/src/release/AndroidManifest.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
<application>
88
<meta-data android:name="onesignal_app_id" android:value="${onesignal_app_id}" />
9+
<!-- Deprecated - Pulled from OneSignal dashboard. -->
910
<meta-data android:name="onesignal_google_project_number" android:value="str:${onesignal_google_project_number}" />
1011

1112
<receiver
1213
android:name="com.onesignal.GcmBroadcastReceiver"
1314
android:permission="com.google.android.c2dm.permission.SEND" >
14-
<intent-filter>
15+
<!-- High priority so OneSignal payloads can be filtered from other GCM receivers if filterOtherGCMReceivers is enabled. -->
16+
<intent-filter android:priority="999" >
1517
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
1618
<category android:name="${applicationId}" />
1719
</intent-filter>

OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public static void NotificationBundleProcessor_ProcessFromGCMIntentService_NoWra
7070
}
7171

7272
public static boolean GcmBroadcastReceiver_processBundle(Context context, Bundle bundle) {
73-
return NotificationBundleProcessor.processBundle(context, bundle);
73+
NotificationBundleProcessor.ProcessedBundleResult processedResult = NotificationBundleProcessor.processBundle(context, bundle);
74+
return processedResult.processed();
7475
}
7576

7677
public static int NotificationBundleProcessor_Process(Context context, boolean restoring, JSONObject jsonPayload, NotificationExtenderService.OverrideSettings overrideSettings) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2017 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
package com.onesignal;
29+
30+
import android.content.BroadcastReceiver;
31+
32+
import org.robolectric.annotation.Implements;
33+
34+
@Implements(BroadcastReceiver.class)
35+
public class ShadowGcmBroadcastReceiver {
36+
37+
public static boolean calledAbortBroadcast;
38+
public static Integer lastResultCode;
39+
40+
public static void resetStatics() {
41+
calledAbortBroadcast = false;
42+
lastResultCode = null;
43+
}
44+
45+
public void abortBroadcast() {
46+
calledAbortBroadcast = true;
47+
}
48+
49+
public void setResultCode(int code) {
50+
lastResultCode = code;
51+
}
52+
}

OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.onesignal.OneSignalDbHelper;
5151
import com.onesignal.OneSignalPackagePrivateHelper;
5252
import com.onesignal.ShadowBadgeCountUpdater;
53+
import com.onesignal.ShadowGcmBroadcastReceiver;
5354
import com.onesignal.ShadowNotificationManagerCompat;
5455
import com.onesignal.ShadowNotificationRestorer;
5556
import com.onesignal.ShadowOneSignal;
@@ -600,6 +601,7 @@ public void shouldCorrectlyDisplaySummaryWithMixedInAppAlertsAndNotifications()
600601

601602

602603
@Test
604+
@Config(shadows = {ShadowGcmBroadcastReceiver.class})
603605
public void shouldSetButtonsCorrectly() throws Exception {
604606
Intent intentGcm = new Intent();
605607
intentGcm.setAction("com.google.android.c2dm.intent.RECEIVE");
@@ -609,10 +611,7 @@ public void shouldSetButtonsCorrectly() throws Exception {
609611
intentGcm.putExtras(bundle);
610612

611613
GcmBroadcastReceiver gcmBroadcastReceiver = new GcmBroadcastReceiver();
612-
try {
613-
gcmBroadcastReceiver.onReceive(blankActivity, intentGcm);
614-
} // setResultCode throws this error due to onReceive not designed to be called manually.
615-
catch (java.lang.IllegalStateException e) {}
614+
gcmBroadcastReceiver.onReceive(blankActivity, intentGcm);
616615

617616
Intent intent = Shadows.shadowOf(blankActivity).getNextStartedService();
618617
Assert.assertEquals("com.onesignal.GcmIntentService", intent.getComponent().getClassName());
@@ -624,7 +623,29 @@ public void shouldSetButtonsCorrectly() throws Exception {
624623
JSONObject additionalData = new JSONObject((customJson.getString("a")));
625624
Assert.assertEquals("id1", additionalData.getJSONArray("actionButtons").getJSONObject(0).getString("id"));
626625
}
627-
626+
627+
@Test
628+
@Config(shadows = {ShadowGcmBroadcastReceiver.class})
629+
public void shouldPreventOtherGCMReceiversWhenSettingEnabled() throws Exception {
630+
OneSignal.setInFocusDisplaying(OneSignal.OSInFocusDisplayOption.InAppAlert);
631+
OneSignal.startInit(blankActivity).filterOtherGCMReceivers(true).init();
632+
threadAndTaskWait();
633+
634+
635+
Intent intentGcm = new Intent();
636+
intentGcm.setAction("com.google.android.c2dm.intent.RECEIVE");
637+
intentGcm.putExtra("message_type", "gcm");
638+
Bundle bundle = getBaseNotifBundle();
639+
bundle.putString("o", "[{\"n\": \"text1\", \"i\": \"id1\"}]");
640+
intentGcm.putExtras(bundle);
641+
642+
GcmBroadcastReceiver gcmBroadcastReceiver = new GcmBroadcastReceiver();
643+
gcmBroadcastReceiver.onReceive(blankActivity, intentGcm);
644+
645+
Assert.assertNull(ShadowGcmBroadcastReceiver.lastResultCode);
646+
Assert.assertTrue(ShadowGcmBroadcastReceiver.calledAbortBroadcast);
647+
}
648+
628649
private OSNotification lastNotificationReceived;
629650
@Test
630651
public void shouldStillFireReceivedHandlerWhenNotificationExtenderServiceIsUsed() throws Exception {
@@ -680,7 +701,7 @@ public void shouldFireNotificationExtenderService() throws Exception {
680701
RuntimeEnvironment.getRobolectricPackageManager().addResolveInfoForIntent(serviceIntent, resolveInfo);
681702

682703
boolean ret = OneSignalPackagePrivateHelper.GcmBroadcastReceiver_processBundle(blankActivity, bundle);
683-
Assert.assertEquals(true, ret);
704+
Assert.assertTrue(ret);
684705

685706
Intent intent = Shadows.shadowOf(blankActivity).getNextStartedService();
686707
Assert.assertEquals("com.onesignal.NotificationExtender", intent.getAction());

0 commit comments

Comments
 (0)