From 7f762982cf0d7aed1e5c42f1e6d9d4059b400d0d Mon Sep 17 00:00:00 2001 From: Josh Kasten Date: Wed, 4 May 2022 19:00:58 -0700 Subject: [PATCH 1/5] rm all the reverse bits from Activity Trampolining This commit focuses on removing all logic that setups up multiple Activities and flags to build a "Reverse Activity Trampoline". This is Part 1 of required changes, this commit results in the app not opening, resuming, or opening launch URLs, the behavior of the click being tracked is still working via the invisible NotificationOpenedReceiver Activity. This gives us a clean starting state for the next commit where we we will add logic to the notification open event to Activity Trampoline forward to the intended Activity. --- .../GenerateNotificationOpenIntent.kt | 41 ++++--------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt index 8fb46098bd..a61b15101e 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt @@ -51,17 +51,11 @@ class GenerateNotificationOpenIntent( context, notificationOpenedClassAndroid22AndOlder ) - - if (getIntentVisible() == null) { - // If we don't show a visible Activity put OneSignal's invisible click tracking - // Activity on it's own task so it doesn't resume an existing one once it closes. - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK or - Intent.FLAG_ACTIVITY_MULTIPLE_TASK or - Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET - ) - } - + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_MULTIPLE_TASK or + Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET + ) return intent } @@ -83,31 +77,10 @@ class GenerateNotificationOpenIntent( oneSignalIntent: Intent, ): PendingIntent? { val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - val launchIntent = getIntentVisible() - ?: - // Even though the default app open action is disabled we still need to attach OneSignal's - // invisible Activity to capture click event to report click counts and etc. - // You may be thinking why not use a BroadcastReceiver instead of an invisible - // Activity? This could be done in a 5.0.0 release but can't be changed now as it is - // unknown if the app developer will be starting there own Activity from their - // OSNotificationOpenedHandler and that would have side-effects. - return PendingIntent.getActivity( - context, - requestCode, - oneSignalIntent, - flags - ) - - - // This setups up a "Reverse Activity Trampoline" - // The first Activity to launch will be oneSignalIntent, which is an invisible - // Activity to track the click, fire OSNotificationOpenedHandler, etc. This Activity - // will finish quickly and the destination Activity, launchIntent, will be shown to the user - // since it is the next in the back stack. - return PendingIntent.getActivities( + return PendingIntent.getActivity( context, requestCode, - arrayOf(launchIntent, oneSignalIntent), + oneSignalIntent, flags ) } From 324f5e9922ea59df74ee152ec9c5178032ba2e4f Mon Sep 17 00:00:00 2001 From: Josh Kasten Date: Wed, 4 May 2022 19:50:11 -0700 Subject: [PATCH 2/5] add back forward activity trampolining Now everything uses the same forward activity trampolining behavior so everything works consistently!, including the behavior of HMS message type notifications. Removed the startLauncherActivity flag from handleNotificationOpen due this consistently as well. Renamed startOrResumeApp to openDestinationActivity as this now handles URL opens as well. There was a bug here where HMS notifications did not open launch URLs that has also been fixed here. Added logging if we don't open an Activity, to indicate the behavior was intended. --- .../GenerateNotificationOpenIntent.kt | 2 +- .../NotificationOpenedProcessor.java | 2 +- .../NotificationPayloadProcessorHMS.java | 1 - .../main/java/com/onesignal/OneSignal.java | 56 +++++++++---------- .../OneSignalPackagePrivateHelper.java | 4 +- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt index a61b15101e..c96731687f 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt @@ -86,7 +86,7 @@ class GenerateNotificationOpenIntent( } // Return the provide intent if one was set, otherwise default to opening the app. - private fun getIntentVisible(): Intent? { + fun getIntentVisible(): Intent? { if (intent != null) return intent return getIntentAppOpen() } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java index bfb6b90096..727ed2c4f9 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java @@ -114,7 +114,7 @@ static void processIntent(Context context, Intent intent) { if (!(context instanceof Activity)) OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "NotificationOpenedProcessor processIntent from an non Activity context: " + context); else OneSignal.handleNotificationOpen((Activity) context, intentExtras.getDataArray(), - false, OSNotificationFormatHelper.getOSNotificationIdFromJson(intentExtras.getJsonData())); + OSNotificationFormatHelper.getOSNotificationIdFromJson(intentExtras.getJsonData())); } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java index b437553c44..3dc9dffb7f 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java @@ -68,7 +68,6 @@ private static void handleProcessJsonOpenData(@NonNull Activity activity, @NonNu OneSignal.handleNotificationOpen( activity, new JSONArray().put(jsonData), - true, OSNotificationFormatHelper.getOSNotificationIdFromJson(jsonData) ); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index 6eb10313e0..b189909884 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -2394,7 +2394,7 @@ static void fireForegroundHandlers(OSNotificationController notificationControll /** * Method called when opening a notification */ - static void handleNotificationOpen(final Activity context, final JSONArray data, final boolean startLauncherActivity, @Nullable final String notificationId) { + static void handleNotificationOpen(final Activity context, final JSONArray data, @Nullable final String notificationId) { // Delay call until remote params are set if (taskRemoteController.shouldQueueTaskForInit(OSTaskRemoteController.HANDLE_NOTIFICATION_OPEN)) { logger.error("Waiting for remote params. " + @@ -2404,7 +2404,7 @@ static void handleNotificationOpen(final Activity context, final JSONArray data, public void run() { if (appContext != null) { logger.debug("Running " + OSTaskRemoteController.HANDLE_NOTIFICATION_OPEN + " operation from pending queue."); - handleNotificationOpen(context, data, startLauncherActivity, notificationId); + handleNotificationOpen(context, data, notificationId); } } }); @@ -2422,39 +2422,35 @@ public void run() { if (shouldInitDirectSessionFromNotificationOpen(context, data)) { applicationOpenedByNotification(notificationId); - - if (startLauncherActivity) { - // Start activity with an activity trampolining - startOrResumeApp(context); - } } + openDestinationActivity(context, data); + runNotificationOpenedCallback(data); } - // Reverse activity trampolining is used for most notifications. - // This method is only used if the push provider does support it. - // This opens the app in the same way an Android home screen launcher does. - // This means we expect the following behavior: - // 1. Starts the Activity defined in the app's AndroidManifest.xml as "android.intent.action.MAIN" - // 2. If the app is already running, instead the last activity will be resumed - // 3. If the app is not running (due to being push out of memory), the last activity will be resumed - // 4. If the app is no longer in the recent apps list, it is not resumed, same as #1 above. - // - App is removed from the recent app's list if it is swiped away or "clear all" is pressed. - static boolean startOrResumeApp(@NonNull Activity activity) { - Intent launchIntent = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName()); - logger.debug("startOrResumeApp from context: " + activity + " isRoot: " + activity.isTaskRoot() + " with launchIntent: " + launchIntent); - - // Not all apps have a launcher intent, such as one that only provides a homescreen widget - if (launchIntent == null) - return false; - // Removing package from the intent, this treats the app as if it was started externally. - // This gives us the resume app behavior noted above. - // Android 11 no longer requires nulling this out to get this behavior. - launchIntent.setPackage(null); - - activity.startActivity(launchIntent); - return true; + static private void openDestinationActivity( + @NonNull final Activity activity, + @NonNull final JSONArray pushPayloads + ) { + try { + // Always use the top most notification if user tapped on the summary notification + JSONObject firstPayloadItem = pushPayloads.getJSONObject(0); + GenerateNotificationOpenIntent intentGenerator = GenerateNotificationOpenIntentFromPushPayload.INSTANCE.create( + activity, + firstPayloadItem + ); + + Intent intent = intentGenerator.getIntentVisible(); + if (intent != null) { + activity.startActivity(intent); + } + else { + logger.info("SDK not showing an Activity automatically due to it's settings."); + } + } catch (JSONException e) { + e.printStackTrace(); + } } private static boolean shouldInitDirectSessionFromNotificationOpen(Activity context, final JSONArray data) { diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java index ff73b9b7de..906349b712 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java @@ -129,8 +129,8 @@ public static JSONObject bundleAsJSONObject(Bundle bundle) { return NotificationBundleProcessor.bundleAsJSONObject(bundle); } - public static void OneSignal_handleNotificationOpen(Activity context, final JSONArray data, final boolean fromHMSMessage, final String notificationId) { - OneSignal.handleNotificationOpen(context, data, fromHMSMessage, notificationId); + public static void OneSignal_handleNotificationOpen(Activity context, final JSONArray data, final String notificationId) { + OneSignal.handleNotificationOpen(context, data, notificationId); } public static BigInteger OneSignal_getAccentColor(JSONObject fcmJson) { From 4c01f3ed7ecddf447548cfa2e3979e1d68b0f67a Mon Sep 17 00:00:00 2001 From: Josh Kasten Date: Wed, 4 May 2022 20:25:10 -0700 Subject: [PATCH 3/5] Separate notification generate from open logic GenerateNotificationOpenIntent used to handle everything related to Intents for notifications since it had to create the full reverse Activity trampoline and attach it to the notification. Now the notification generation logic simply needs to always open the SDK's invisible click tracking Activity. When the notification is open the runtime logic will take the push payload into consideration to trampoline to the destination Activity. --- .../com/onesignal/GenerateNotification.java | 20 ++--- .../GenerateNotificationOpenIntent.kt | 82 ++----------------- ...entGeneratorForAttachingToNotifications.kt | 71 ++++++++++++++++ 3 files changed, 86 insertions(+), 87 deletions(-) create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/IntentGeneratorForAttachingToNotifications.kt diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java index e75b323575..0e6371cbe0 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java @@ -275,9 +275,8 @@ private static boolean showNotification(OSNotificationGenerationJob notification JSONObject fcmJson = notificationJob.getJsonPayload(); String group = fcmJson.optString("grp", null); - GenerateNotificationOpenIntent intentGenerator = GenerateNotificationOpenIntentFromPushPayload.INSTANCE.create( - currentContext, - fcmJson + IntentGeneratorForAttachingToNotifications intentGenerator = new IntentGeneratorForAttachingToNotifications( + currentContext ); ArrayList grouplessNotifs = new ArrayList<>(); @@ -368,7 +367,7 @@ private static boolean showNotification(OSNotificationGenerationJob notification private static Notification createGenericPendingIntentsForNotif( NotificationCompat.Builder notifBuilder, - GenerateNotificationOpenIntent intentGenerator, + IntentGeneratorForAttachingToNotifications intentGenerator, JSONObject gcmBundle, int notificationId ) { @@ -385,7 +384,7 @@ private static Notification createGenericPendingIntentsForNotif( private static void createGenericPendingIntentsForGroup( NotificationCompat.Builder notifBuilder, - GenerateNotificationOpenIntent intentGenerator, + IntentGeneratorForAttachingToNotifications intentGenerator, JSONObject gcmBundle, String group, int notificationId @@ -495,9 +494,8 @@ static void updateSummaryNotification(OSNotificationGenerationJob notificationJo private static void createSummaryNotification(OSNotificationGenerationJob notificationJob, OneSignalNotificationBuilder notifBuilder) { boolean updateSummary = notificationJob.isRestoring(); JSONObject fcmJson = notificationJob.getJsonPayload(); - GenerateNotificationOpenIntent intentGenerator = GenerateNotificationOpenIntentFromPushPayload.INSTANCE.create( - currentContext, - fcmJson + IntentGeneratorForAttachingToNotifications intentGenerator = new IntentGeneratorForAttachingToNotifications( + currentContext ); String group = fcmJson.optString("grp", null); @@ -706,7 +704,7 @@ private static void createSummaryNotification(OSNotificationGenerationJob notifi @RequiresApi(api = Build.VERSION_CODES.M) private static void createGrouplessSummaryNotification( OSNotificationGenerationJob notificationJob, - GenerateNotificationOpenIntent intentGenerator, + IntentGeneratorForAttachingToNotifications intentGenerator, int grouplessNotifCount ) { JSONObject fcmJson = notificationJob.getJsonPayload(); @@ -763,7 +761,7 @@ private static void createGrouplessSummaryNotification( private static Intent createBaseSummaryIntent( int summaryNotificationId, - GenerateNotificationOpenIntent intentGenerator, + IntentGeneratorForAttachingToNotifications intentGenerator, JSONObject fcmJson, String group ) { @@ -1034,7 +1032,7 @@ static BigInteger getAccentColor(JSONObject fcmJson) { private static void addNotificationActionButtons( JSONObject fcmJson, - GenerateNotificationOpenIntent intentGenerator, + IntentGeneratorForAttachingToNotifications intentGenerator, NotificationCompat.Builder mBuilder, int notificationId, String groupSummary diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt index c96731687f..fe575e46b9 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt @@ -1,101 +1,31 @@ package com.onesignal -import android.app.PendingIntent import android.content.Context import android.content.Intent -import androidx.annotation.RequiresApi class GenerateNotificationOpenIntent( private val context: Context, private val intent: Intent?, private val startApp: Boolean ) { - - private val notificationOpenedClassAndroid23Plus: Class<*> = NotificationOpenedReceiver::class.java - private val notificationOpenedClassAndroid22AndOlder: Class<*> = NotificationOpenedReceiverAndroid22AndOlder::class.java - - fun getNewBaseIntent( - notificationId: Int, - ): Intent { - val intent = - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) - getNewBaseIntentAndroidAPI23Plus() - else - getNewBaseIntentAndroidAPI22AndOlder() - - return intent - .putExtra( - GenerateNotification.BUNDLE_KEY_ANDROID_NOTIFICATION_ID, - notificationId - ) - // We use SINGLE_TOP and CLEAR_TOP as we don't want more than one OneSignal invisible click - // tracking Activity instance around. - .addFlags( - Intent.FLAG_ACTIVITY_SINGLE_TOP or - Intent.FLAG_ACTIVITY_CLEAR_TOP - ) - } - - @RequiresApi(android.os.Build.VERSION_CODES.M) - private fun getNewBaseIntentAndroidAPI23Plus(): Intent { - return Intent( - context, - notificationOpenedClassAndroid23Plus - ) - } - - // See NotificationOpenedReceiverAndroid22AndOlder.kt for details - @Deprecated("Use getNewBaseIntentAndroidAPI23Plus instead for Android 6+") - private fun getNewBaseIntentAndroidAPI22AndOlder(): Intent { - val intent = Intent( - context, - notificationOpenedClassAndroid22AndOlder - ) - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK or - Intent.FLAG_ACTIVITY_MULTIPLE_TASK or - Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET - ) - return intent + fun getIntentVisible(): Intent? { + if (intent != null) return intent + return getIntentAppOpen() } /** - * Creates a PendingIntent to attach to the notification click and it's action button(s). - * If the user interacts with the notification this normally starts the app or resumes it - * unless the app developer disables this via a OneSignal meta-data AndroidManifest.xml setting - * - * The default behavior is to open the app in the same way an Android homescreen launcher does. + * This opens the app in the same way an Android homescreen launcher does. * This means we expect the following behavior: * 1. Starts the Activity defined in the app's AndroidManifest.xml as "android.intent.action.MAIN" * 2. If the app is already running, instead the last activity will be resumed * 3. If the app is not running (due to being push out of memory), the last activity will be resumed * 4. If the app is no longer in the recent apps list, it is not resumed, same as #1 above. - * - App is removed from the recent app's list if it is swiped away or "clear all" is pressed. + * - App is removed from the recent app's list if it is swiped away or "clear all" is pressed. */ - fun getNewActionPendingIntent( - requestCode: Int, - oneSignalIntent: Intent, - ): PendingIntent? { - val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - return PendingIntent.getActivity( - context, - requestCode, - oneSignalIntent, - flags - ) - } - - // Return the provide intent if one was set, otherwise default to opening the app. - fun getIntentVisible(): Intent? { - if (intent != null) return intent - return getIntentAppOpen() - } - - // Provides the default launcher Activity, if the app has one. - // - This is almost always true, one of the few exceptions being an app that is only a widget. private fun getIntentAppOpen(): Intent? { if (!startApp) return null + // Is null for apps that only provide a widget for it's UI. val launchIntent = context.packageManager.getLaunchIntentForPackage( context.packageName diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/IntentGeneratorForAttachingToNotifications.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/IntentGeneratorForAttachingToNotifications.kt new file mode 100644 index 0000000000..3fd120293c --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/IntentGeneratorForAttachingToNotifications.kt @@ -0,0 +1,71 @@ +package com.onesignal + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.annotation.RequiresApi + +class IntentGeneratorForAttachingToNotifications( + val context: Context +) { + private val notificationOpenedClassAndroid23Plus: Class<*> = NotificationOpenedReceiver::class.java + private val notificationOpenedClassAndroid22AndOlder: Class<*> = NotificationOpenedReceiverAndroid22AndOlder::class.java + + fun getNewBaseIntent( + notificationId: Int, + ): Intent { + val intent = + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) + getNewBaseIntentAndroidAPI23Plus() + else + getNewBaseIntentAndroidAPI22AndOlder() + + return intent + .putExtra( + GenerateNotification.BUNDLE_KEY_ANDROID_NOTIFICATION_ID, + notificationId + ) + // We use SINGLE_TOP and CLEAR_TOP as we don't want more than one OneSignal invisible click + // tracking Activity instance around. + .addFlags( + Intent.FLAG_ACTIVITY_SINGLE_TOP or + Intent.FLAG_ACTIVITY_CLEAR_TOP + ) + } + + @RequiresApi(android.os.Build.VERSION_CODES.M) + private fun getNewBaseIntentAndroidAPI23Plus(): Intent { + return Intent( + context, + notificationOpenedClassAndroid23Plus + ) + } + + // See NotificationOpenedReceiverAndroid22AndOlder.kt for details + @Deprecated("Use getNewBaseIntentAndroidAPI23Plus instead for Android 6+") + private fun getNewBaseIntentAndroidAPI22AndOlder(): Intent { + val intent = Intent( + context, + notificationOpenedClassAndroid22AndOlder + ) + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_MULTIPLE_TASK or + Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET + ) + return intent + } + + fun getNewActionPendingIntent( + requestCode: Int, + oneSignalIntent: Intent, + ): PendingIntent? { + val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + return PendingIntent.getActivity( + context, + requestCode, + oneSignalIntent, + flags + ) + } +} From 6b069dbfd7ed9b633e22b4e0d8f5b6ee008df28b Mon Sep 17 00:00:00 2001 From: Josh Kasten Date: Thu, 5 May 2022 12:01:41 -0700 Subject: [PATCH 4/5] Reverting activity trampolining tests While this commit isn't a git revert most of the changes are undoing the following commit: 5f7275069814b64220eb9697bb3c8ae4b1301505 Additionally this fixes tests due to the dropped activity param from OneSignal_handleNotificationOpen. --- .../onesignal/GenerateNotificationRunner.java | 70 ++------- .../onesignal/MainOneSignalClassRunner.java | 137 +++++++++++++++++- ...enedActivityHMSIntegrationTestsRunner.java | 9 ++ .../OutcomeEventIntegrationTests.java | 27 ++-- 4 files changed, 164 insertions(+), 79 deletions(-) diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java index 218daf4bb3..2a3620f00c 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java @@ -1142,49 +1142,25 @@ public void shouldUseCorrectActivityForAndroid23Plus() throws Exception { threadAndTaskWait(); Intent[] intents = lastNotificationIntents(); - assertEquals("android.intent.action.MAIN", intents[0].getAction()); assertEquals( com.onesignal.NotificationOpenedReceiver.class.getName(), - intents[1].getComponent().getClassName() + intents[0].getComponent().getClassName() ); - assertEquals(2, intents.length); - } - - @Test - @Config(shadows = { ShadowGenerateNotification.class }) - public void shouldSetContentIntentForLaunchURL() throws Exception { - generateNotificationWithLaunchURL(); - - Intent[] intents = lastNotificationIntents(); - assertEquals(2, intents.length); - Intent intentLaunchURL = intents[0]; - assertEquals("android.intent.action.VIEW", intentLaunchURL.getAction()); - assertEquals("https://google.com", intentLaunchURL.getData().toString()); - - assertNotificationOpenedReceiver(intents[1]); - } - - @Test - @Config(shadows = { ShadowGenerateNotification.class }) - public void shouldNotSetContentIntentForLaunchURLIfDefaultNotificationOpenIsDisabled() throws Exception { - OneSignalShadowPackageManager.addManifestMetaData("com.onesignal.NotificationOpened.DEFAULT", "DISABLE"); - generateNotificationWithLaunchURL(); - - Intent[] intents = lastNotificationIntents(); assertEquals(1, intents.length); - assertNotificationOpenedReceiver(intents[0]); } @Test - @Config(shadows = { ShadowGenerateNotification.class }) - public void shouldNotSetContentIntentForLaunchURLIfSuppress() throws Exception { - OneSignalShadowPackageManager.addManifestMetaData("com.onesignal.suppressLaunchURLs", true); - generateNotificationWithLaunchURL(); + @Config(sdk = 21, shadows = { ShadowGenerateNotification.class }) + public void shouldUseCorrectActivityForLessThanAndroid23() throws Exception { + NotificationBundleProcessor_ProcessFromFCMIntentService(blankActivity, getBaseNotifBundle()); + threadAndTaskWait(); Intent[] intents = lastNotificationIntents(); - assertEquals(2, intents.length); - assertOpenMainActivityIntent(intents[0]); - assertNotificationOpenedReceiver(intents[1]); + assertEquals( + com.onesignal.NotificationOpenedReceiverAndroid22AndOlder.class.getName(), + intents[0].getComponent().getClassName() + ); + assertEquals(1, intents.length); } private Intent[] lastNotificationIntents() { @@ -1193,21 +1169,6 @@ private Intent[] lastNotificationIntents() { return shadowOf(pendingIntent).getSavedIntents(); } - private void generateNotificationWithLaunchURL() throws Exception { - Bundle bundle = launchURLMockPayloadBundle(); - NotificationBundleProcessor_ProcessFromFCMIntentService(blankActivity, bundle); - threadAndTaskWait(); - } - - private void assertNotificationOpenedReceiver(@NonNull Intent intent) { - assertEquals(com.onesignal.NotificationOpenedReceiverAndroid22AndOlder.class.getName(), intent.getComponent().getClassName()); - } - - private void assertOpenMainActivityIntent(@NonNull Intent intent) { - assertEquals(Intent.ACTION_MAIN, intent.getAction()); - assertTrue(intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)); - } - @Test @Config(shadows = { ShadowGenerateNotification.class }) public void shouldSetAlertnessFieldsOnNormalPriority() { @@ -1312,17 +1273,6 @@ public void shouldSetExpireTimeCorrectlyWhenMissingFromPayload() throws Exceptio return bundle; } - @NonNull - private static Bundle launchURLMockPayloadBundle() throws JSONException { - Bundle bundle = new Bundle(); - bundle.putString("alert", "test"); - bundle.putString("custom", new JSONObject() {{ - put("i", "UUID"); - put("u", "https://google.com"); - }}.toString()); - return bundle; - } - @Test @Config(shadows = { ShadowOneSignalRestClient.class, ShadowOSWebView.class }) public void shouldShowInAppPreviewWhenInFocus() throws Exception { diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index 815666ba56..18af9205eb 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -39,6 +39,7 @@ import android.net.ConnectivityManager; import android.os.Bundle; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import com.onesignal.MockOSLog; @@ -557,7 +558,7 @@ public void testAppOnFocus_containsOutcomeData_withOutcomeEventFlagsEnabled() th time.advanceSystemTimeBy(31); // Click notification - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, "notification_id"); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), "notification_id"); threadAndTaskWait(); // Foreground app @@ -600,7 +601,7 @@ public void testAppOnFocus_wontContainOutcomeData_withOutcomeEventFlagsDisabled( time.advanceSystemTimeBy(31); // Click notification - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID + "1"); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID + "1"); threadAndTaskWait(); // Foreground app @@ -968,7 +969,7 @@ public void testAndroidParamsProjectNumberOverridesLocal() throws Exception { @Config(shadows = {ShadowOneSignal.class}) public void testOpenFromNotificationWhenAppIsDead() throws Exception { OneSignal.initWithContext(blankActivity); - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Robo test message\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Robo test message\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID); OneSignal.setAppId(ONESIGNAL_APP_ID); OneSignal.initWithContext(blankActivity); @@ -1024,7 +1025,7 @@ public void shouldCorrectlyRemoveOpenedHandlerAndFireMissedOnesWhenAddedBack() t threadAndTaskWait(); OneSignal.setNotificationOpenedHandler(null); - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Robo test message\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Robo test message\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID); assertNull(lastNotificationOpenedBody); OneSignalInit(); @@ -1057,6 +1058,132 @@ public void shouldNotFireNotificationOpenAgainAfterAppRestart() throws Exception assertEquals(null, lastNotificationOpenedBody); } + @Test + public void testOpeningLauncherActivity() throws Exception { + // First init run for appId to be saved + // At least OneSignal was init once for user to be subscribed + // If this doesn't' happen, notifications will not arrive + OneSignalInit(); + fastColdRestartApp(); + + AddLauncherIntentFilter(); + // From app launching normally + assertNotNull(shadowOf(blankActivity).getNextStartedActivity()); + // Will get appId saved + OneSignal.initWithContext(blankActivity.getApplicationContext()); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID); + + assertNotNull(shadowOf(blankActivity).getNextStartedActivity()); + assertNull(shadowOf(blankActivity).getNextStartedActivity()); + } + + @Test + public void testOpeningLaunchUrl() throws Exception { + // First init run for appId to be saved + // At least OneSignal was init once for user to be subscribed + // If this doesn't' happen, notifications will not arrive + OneSignalInit(); + fastColdRestartApp(); + OneSignal.initWithContext(blankActivity); + // Removes app launch + shadowOf(blankActivity).getNextStartedActivity(); + + // No OneSignal init here to test case where it is located in an Activity. + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\", \"u\": \"http://google.com\" } }]"), ONESIGNAL_NOTIFICATION_ID); + Intent intent = shadowOf(blankActivity).getNextStartedActivity(); + assertEquals("android.intent.action.VIEW", intent.getAction()); + assertEquals("http://google.com", intent.getData().toString()); + assertNull(shadowOf(blankActivity).getNextStartedActivity()); + } + + @Test + public void testOpeningLaunchUrlWithDisableDefault() throws Exception { + // Add the 'com.onesignal.NotificationOpened.DEFAULT' as 'DISABLE' meta-data tag + OneSignalShadowPackageManager.addManifestMetaData("com.onesignal.NotificationOpened.DEFAULT", "DISABLE"); + + // Removes app launch + shadowOf(blankActivity).getNextStartedActivity(); + + // No OneSignal init here to test case where it is located in an Activity. + + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\", \"u\": \"http://google.com\" } }]"), ONESIGNAL_NOTIFICATION_ID); + assertNull(shadowOf(blankActivity).getNextStartedActivity()); + } + + @Test + public void testDisableOpeningLauncherActivityOnNotificationOpen() throws Exception { + // Add the 'com.onesignal.NotificationOpened.DEFAULT' as 'DISABLE' meta-data tag + OneSignalShadowPackageManager.addManifestMetaData("com.onesignal.NotificationOpened.DEFAULT", "DISABLE"); + + // From app launching normally + assertNotNull(shadowOf(blankActivity).getNextStartedActivity()); + OneSignal.setAppId(ONESIGNAL_APP_ID); + OneSignal.initWithContext(blankActivity); + OneSignal.setNotificationOpenedHandler(getNotificationOpenedHandler()); + assertNull(lastNotificationOpenedBody); + + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID); + + assertNull(shadowOf(blankActivity).getNextStartedActivity()); + assertEquals("Test Msg", lastNotificationOpenedBody); + } + + @Test + public void testLaunchUrlSuppressTrue() throws Exception { + // Add the 'com.onesignal.suppressLaunchURLs' as 'true' meta-data tag + // First init run for appId to be saved + // At least OneSignal was init once for user to be subscribed + // If this doesn't' happen, notifications will not arrive + OneSignalInit(); + fastColdRestartApp(); + + // Add the 'com.onesignal.suppressLaunchURLs' as 'true' meta-data tag + OneSignalShadowPackageManager.addManifestMetaData("com.onesignal.suppressLaunchURLs", true); + + // Removes app launch + shadowOf(blankActivity).getNextStartedActivity(); + + // Init with context since this is call before calling OneSignal_handleNotificationOpen internally + OneSignal.initWithContext(blankActivity); + + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\", \"u\": \"http://google.com\" } }]"), ONESIGNAL_NOTIFICATION_ID); + threadAndTaskWait(); + + assertOpenMainActivityIntent(shadowOf(blankActivity).getNextStartedActivity()); + } + + private void assertOpenMainActivityIntent(@NonNull Intent intent) { + assertEquals(Intent.ACTION_MAIN, intent.getAction()); + assertTrue(intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)); + } + + @Test + public void testLaunchUrlSuppressFalse() throws Exception { + // Add the 'com.onesignal.suppressLaunchURLs' as 'true' meta-data tag + // First init run for appId to be saved + // At least OneSignal was init once for user to be subscribed + // If this doesn't' happen, notifications will not arrive + OneSignalInit(); + fastColdRestartApp(); + + OneSignalShadowPackageManager.addManifestMetaData("com.onesignal.suppressLaunchURLs", false); + OneSignal.initWithContext(blankActivity); + + // Removes app launch + shadowOf(blankActivity).getNextStartedActivity(); + + // Init with context since this is call before calling OneSignal_handleNotificationOpen internally + OneSignal.initWithContext(blankActivity); + + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\", \"u\": \"http://google.com\" } }]"), ONESIGNAL_NOTIFICATION_ID); + threadAndTaskWait(); + + Intent intent = shadowOf(blankActivity).getNextStartedActivity(); + assertEquals("android.intent.action.VIEW", intent.getAction()); + assertEquals("http://google.com", intent.getData().toString()); + assertNull(shadowOf(blankActivity).getNextStartedActivity()); + } + private static String notificationReceivedBody; private static int androidNotificationId; @@ -3812,7 +3939,7 @@ public void shouldSendFirebaseAnalyticsNotificationOpen() throws Exception { openPayload.put("title", "Test title"); openPayload.put("alert", "Test Msg"); openPayload.put("custom", new JSONObject("{ \"i\": \"UUID\" }")); - OneSignal_handleNotificationOpen(blankActivity, new JSONArray().put(openPayload), false, ONESIGNAL_NOTIFICATION_ID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray().put(openPayload), ONESIGNAL_NOTIFICATION_ID); assertEquals("os_notification_opened", ShadowFirebaseAnalytics.lastEventString); Bundle expectedBundle = new Bundle(); diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/NotificationOpenedActivityHMSIntegrationTestsRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/NotificationOpenedActivityHMSIntegrationTestsRunner.java index a961468d0c..3eb366a911 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/NotificationOpenedActivityHMSIntegrationTestsRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/NotificationOpenedActivityHMSIntegrationTestsRunner.java @@ -154,6 +154,15 @@ public void emptyIntent_doesNotThrow() { helper_startHMSOpenActivity(helper_baseHMSOpenIntent()); } + @Test + public void barebonesOSPayload_startsMainActivity() throws Exception { + helper_initSDKAndFireHMSNotificationBarebonesOSOpenIntent(); + + Intent startedActivity = shadowOf((Application) ApplicationProvider.getApplicationContext()).getNextStartedActivity(); + assertNotNull(startedActivity); + assertEquals(startedActivity.getComponent().getClassName(), BlankActivity.class.getName()); + } + @Test public void barebonesOSPayload_makesNotificationOpenRequest() throws Exception { helper_initSDKAndFireHMSNotificationBarebonesOSOpenIntent(); diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/OutcomeEventIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/OutcomeEventIntegrationTests.java index 4edd47953b..415de5c667 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/OutcomeEventIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/OutcomeEventIntegrationTests.java @@ -24,7 +24,6 @@ import com.onesignal.ShadowJobService; import com.onesignal.ShadowNotificationManagerCompat; import com.onesignal.ShadowOSUtils; -import com.onesignal.ShadowOneSignal; import com.onesignal.ShadowOneSignalRestClient; import com.onesignal.ShadowPushRegistratorFCM; import com.onesignal.ShadowTimeoutHandler; @@ -181,7 +180,7 @@ public void testAppSessions_beforeOnSessionCalls() throws Exception { pauseActivity(blankActivityController); // Click notification - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID + "2"); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID + "2"); threadAndTaskWait(); // Foreground app @@ -212,7 +211,7 @@ public void testAppSessions_afterOnSessionCalls() throws Exception { time.advanceSystemTimeBy(31); // Click notification - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID + "2"); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID + "2"); threadAndTaskWait(); // Foreground app @@ -531,7 +530,7 @@ public void notificationOpened(OSNotificationOpenedResult result) { pauseActivity(blankActivityController); // Receive and open a notification - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID); threadAndTaskWait(); // Foreground the application @@ -564,7 +563,7 @@ public void notificationOpened(OSNotificationOpenedResult result) { pauseActivity(blankActivityController); // Receive and open a notification - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID); threadAndTaskWait(); // Foreground the application @@ -587,7 +586,7 @@ public void testNoDirectSession_fromNotificationOpen_whenAppIsInForeground() thr assertNull(notificationOpenedMessage); // Click notification - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID); threadAndTaskWait(); // Check message String matches data sent in open handler @@ -632,7 +631,7 @@ public void testDirectSession_fromNotificationClick_OpenHandleByUser_whenAppIsIn sessionManager.onNotificationReceived(notificationID); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), false, notificationID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), notificationID); threadAndTaskWait(); assertNotNull(notificationOpenedResult); @@ -673,7 +672,7 @@ public void testDirectSession_fromNotificationClick_OpenHandleByUser_NewSession_ time.advanceSystemAndElapsedTimeBy(61); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), false, notificationID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), notificationID); threadAndTaskWait(); assertNotNull(notificationOpenedResult); @@ -713,7 +712,7 @@ public void testDirectSession_fromNotificationClick_NoOpened_whenAppIsInBackgrou ShadowTimeoutHandler.setMockDelayMillis(1); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), false, notificationID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), notificationID); threadAndTaskWait(); assertNotNull(notificationOpenedResult); @@ -752,7 +751,7 @@ public void testDirectSession_fromNotificationClick_NoOpened_NewSession_whenAppI time.advanceSystemAndElapsedTimeBy(61); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), false, notificationID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), notificationID); threadAndTaskWait(); assertNotNull(notificationOpenedResult); @@ -782,7 +781,7 @@ public void testDirectSession_fromNotificationClick_OpenHandleByUser_whenAppIsIn String notificationID = ONESIGNAL_NOTIFICATION_ID + "1"; sessionManager.onNotificationReceived(notificationID); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), false, notificationID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), notificationID); threadAndTaskWait(); assertNotNull(notificationOpenedResult); @@ -834,7 +833,7 @@ public void testDirectSession_willOverrideIndirectSession_whenAppIsInBackground( pauseActivity(blankActivityController); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID + "2"); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID + "2"); threadAndTaskWait(); // Foreground app @@ -862,7 +861,7 @@ public void testDirectSession_willOverrideDirectSession_whenAppIsInBackground() pauseActivity(blankActivityController); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), false, ONESIGNAL_NOTIFICATION_ID + "2"); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"UUID\" } }]"), ONESIGNAL_NOTIFICATION_ID + "2"); threadAndTaskWait(); // Check directNotificationId is set to clicked notification @@ -1212,7 +1211,7 @@ private void foregroundAppAfterClickingNotification() throws Exception { String notificationID = ONESIGNAL_NOTIFICATION_ID + "1"; sessionManager.onNotificationReceived(notificationID); // Click notification before new session - OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), false, notificationID); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray("[{ \"alert\": \"Test Msg\", \"custom\": { \"i\": \"" + notificationID + "\" } }]"), notificationID); threadAndTaskWait(); // App opened after clicking notification, but Robolectric needs this to simulate onAppFocus() code after a click From 1c3e69eca75373a6b9a3e35fb390b46b2975b6a4 Mon Sep 17 00:00:00 2001 From: Josh Kasten Date: Sat, 7 May 2022 16:18:35 -0700 Subject: [PATCH 5/5] Fix foregrounding app on IAM preview open Added extra foreground assert to existing test to ensure it fails. Added missing openDestinationActivity call for IAM notification handling and ensured test passed. Moved logic from NotificationOpenProcessor.handleIAMPreviewOpen into OSInAppMessagePreviewHandler.notificationOpened Remaned inAppMessagePreviewHandled to notificationReceived so it is more clear now that OSInAppMessagePreviewHandler handles both opens and received pushes. --- .../onesignal/NotificationBundleProcessor.java | 2 +- .../onesignal/NotificationOpenedProcessor.java | 11 +---------- .../NotificationPayloadProcessorHMS.java | 2 +- .../onesignal/OSInAppMessagePreviewHandler.kt | 17 +++++++++++++++-- .../src/main/java/com/onesignal/OneSignal.java | 3 ++- .../onesignal/GenerateNotificationRunner.java | 6 ++++++ 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java index b579451e3e..67d14f4f1b 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java @@ -372,7 +372,7 @@ static void processBundleFromReceiver(Context context, final Bundle bundle, fina bundleResult.setOneSignalPayload(true); maximizeButtonsFromBundle(bundle); - if (OSInAppMessagePreviewHandler.inAppMessagePreviewHandled(context, bundle)) { + if (OSInAppMessagePreviewHandler.notificationReceived(context, bundle)) { // Return early, we don't want the extender service or etc. to fire for IAM previews bundleResult.setInAppPreviewShown(true); bundleReceiverCallback.onBundleProcessed(bundleResult); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java index 727ed2c4f9..e57c0290d5 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java @@ -126,7 +126,7 @@ static OSNotificationIntentExtras processToOpenIntent(Context context, Intent in if (!(context instanceof Activity)) OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "NotificationOpenedProcessor processIntent from an non Activity context: " + context); - else if (handleIAMPreviewOpen((Activity) context, jsonData)) + else if (OSInAppMessagePreviewHandler.notificationOpened((Activity) context, jsonData)) return null; jsonData.put(BUNDLE_KEY_ANDROID_NOTIFICATION_ID, intent.getIntExtra(BUNDLE_KEY_ANDROID_NOTIFICATION_ID, 0)); @@ -143,15 +143,6 @@ else if (handleIAMPreviewOpen((Activity) context, jsonData)) return new OSNotificationIntentExtras(dataArray, jsonData); } - static boolean handleIAMPreviewOpen(@NonNull Activity context, @NonNull JSONObject jsonData) { - String previewUUID = OSInAppMessagePreviewHandler.inAppPreviewPushUUID(jsonData); - if (previewUUID == null) - return false; - - OneSignal.getInAppMessageController().displayPreviewMessage(previewUUID); - return true; - } - private static void addChildNotifications(JSONArray dataArray, String summaryGroup, OneSignalDbHelper writableDb) { String[] retColumn = { NotificationTable.COLUMN_NAME_FULL_DATA }; String[] whereArgs = { summaryGroup }; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java index 3dc9dffb7f..d474a23d51 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationPayloadProcessorHMS.java @@ -62,7 +62,7 @@ private static void reformatButtonClickAction(@NonNull JSONObject jsonData) { } private static void handleProcessJsonOpenData(@NonNull Activity activity, @NonNull JSONObject jsonData) { - if (NotificationOpenedProcessor.handleIAMPreviewOpen(activity, jsonData)) + if (OSInAppMessagePreviewHandler.notificationOpened(activity, jsonData)) return; OneSignal.handleNotificationOpen( diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSInAppMessagePreviewHandler.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSInAppMessagePreviewHandler.kt index 2cc0d170a6..25a7c81b67 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSInAppMessagePreviewHandler.kt +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSInAppMessagePreviewHandler.kt @@ -1,14 +1,17 @@ package com.onesignal +import android.app.Activity import android.content.Context import android.os.Build import android.os.Bundle +import androidx.annotation.ChecksSdkIntAtLeast +import org.json.JSONArray import org.json.JSONException import org.json.JSONObject internal object OSInAppMessagePreviewHandler { @JvmStatic - fun inAppMessagePreviewHandled(context: Context?, bundle: Bundle?): Boolean { + fun notificationReceived(context: Context?, bundle: Bundle?): Boolean { val pushPayloadJson = NotificationBundleProcessor.bundleAsJSONObject(bundle) // Show In-App message preview it is in the payload & the app is in focus val previewUUID = inAppPreviewPushUUID(pushPayloadJson) ?: return false @@ -23,6 +26,15 @@ internal object OSInAppMessagePreviewHandler { return true } + @JvmStatic + fun notificationOpened(activity: Activity, jsonData: JSONObject): Boolean { + val previewUUID = inAppPreviewPushUUID(jsonData) ?: return false + + OneSignal.openDestinationActivity(activity, JSONArray().put(jsonData)) + OneSignal.getInAppMessageController().displayPreviewMessage(previewUUID) + return true + } + @JvmStatic fun inAppPreviewPushUUID(payload: JSONObject): String? { val osCustom: JSONObject = try { @@ -43,5 +55,6 @@ internal object OSInAppMessagePreviewHandler { } // Validate that the current Android device is Android 4.4 or higher + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.KITKAT) private fun shouldDisplayNotification(): Boolean = Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 -} \ No newline at end of file +} diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index b189909884..bc8d5b03e3 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -2429,7 +2429,7 @@ public void run() { runNotificationOpenedCallback(data); } - static private void openDestinationActivity( + static void openDestinationActivity( @NonNull final Activity activity, @NonNull final JSONArray pushPayloads ) { @@ -2443,6 +2443,7 @@ static private void openDestinationActivity( Intent intent = intentGenerator.getIntentVisible(); if (intent != null) { + logger.info("SDK running startActivity with Intent: " + intent); activity.startActivity(intent); } else { diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java index 2a3620f00c..2a99ca2607 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java @@ -1308,11 +1308,17 @@ public void shouldShowInAppPreviewWhenOpeningPreviewNotification() throws Except }}); }}.toString()); + // Grab activity to remove it from the unit test's tracked list. + shadowOf(blankActivity).getNextStartedActivity(); + Intent notificationOpenIntent = createOpenIntent(2, inAppPreviewMockPayloadBundle()); NotificationOpenedProcessor_processFromContext(blankActivity, notificationOpenIntent); threadAndTaskWait(); threadAndTaskWait(); assertEquals("PGh0bWw+PC9odG1sPgoKPHNjcmlwdD4KICAgIHNldFBsYXllclRhZ3MobnVsbCk7Cjwvc2NyaXB0Pg==", ShadowOSWebView.lastData); + + // Ensure the app is foregrounded. + assertNotNull(shadowOf(blankActivity).getNextStartedActivity()); } @Test