From ed35d0611e2069f9be8e9c9cff56ec0e2e635979 Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Wed, 1 Dec 2021 09:14:18 -0800 Subject: [PATCH 1/6] [MOB - 3754] - Handling Trampoline Restriction 1. Introduce new Activity - `TrampolineActivity` which now replaces `IterablePushActionReceiver` as a starting point when user taps notification replacing. 2. Notification Builder instead of create PendingIntent from `getActivity` instead of `getBroadcast` to accomodate to above change 3. Data from notification is thus routed through `TrampolineActivity` 4. `TrampolineActivity` passes the data as is to `IterablePushActionReceiver` and dismisses itself --- iterableapi/src/main/AndroidManifest.xml | 5 ++ .../IterableNotificationBuilder.java | 6 +-- .../IterableNotificationHelper.java | 13 ++--- .../iterableapi/TrampolineActivity.java | 53 +++++++++++++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 iterableapi/src/main/java/com/iterable/iterableapi/TrampolineActivity.java diff --git a/iterableapi/src/main/AndroidManifest.xml b/iterableapi/src/main/AndroidManifest.xml index 3afe07bca..c5c819ee2 100644 --- a/iterableapi/src/main/AndroidManifest.xml +++ b/iterableapi/src/main/AndroidManifest.xml @@ -22,6 +22,11 @@ + diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java index 5670b18be..efd6f4f50 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java @@ -112,14 +112,14 @@ public Notification build() { */ public void createNotificationActionButton(Context context, IterableNotificationData.Button button, Bundle extras) { Intent buttonIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); - buttonIntent.setClass(context, IterablePushActionReceiver.class); + buttonIntent.setClass(context, TrampolineActivity.class); buttonIntent.putExtras(extras); buttonIntent.putExtra(IterableConstants.REQUEST_CODE, requestCode); buttonIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, button.identifier); buttonIntent.putExtra(IterableConstants.ACTION_IDENTIFIER, button.identifier); - PendingIntent pendingButtonIntent = PendingIntent.getBroadcast(context, buttonIntent.hashCode(), - buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingButtonIntent = PendingIntent.getActivity(context, buttonIntent.hashCode(), + buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action .Builder(NotificationCompat.BADGE_ICON_NONE, button.title, pendingButtonIntent); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java index 1a603ed5c..14ca1901c 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java @@ -177,10 +177,11 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex IterableLogger.d(IterableNotificationBuilder.TAG, "Request code = " + notificationBuilder.requestCode); } - Intent pushContentIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); - pushContentIntent.setClass(context, IterablePushActionReceiver.class); - pushContentIntent.putExtras(extras); - pushContentIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT); + //Create an intent for TrampolineActivity instead of BroadcastReceiver + Intent trampolineActivityIntent = new Intent(context, TrampolineActivity.class); + trampolineActivityIntent.putExtras(extras); + trampolineActivityIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT); + trampolineActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Action buttons if (notificationData.getActionButtons() != null) { @@ -192,8 +193,8 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex } } - PendingIntent notificationClickedIntent = PendingIntent.getBroadcast(context, notificationBuilder.requestCode, - pushContentIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent notificationClickedIntent = PendingIntent.getActivity(context, notificationBuilder.requestCode, + trampolineActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); notificationBuilder.setContentIntent(notificationClickedIntent); notificationBuilder.setIsGhostPush(isGhostPush(extras)); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/TrampolineActivity.java b/iterableapi/src/main/java/com/iterable/iterableapi/TrampolineActivity.java new file mode 100644 index 000000000..f9a986624 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/TrampolineActivity.java @@ -0,0 +1,53 @@ +package com.iterable.iterableapi; + +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +public class TrampolineActivity extends AppCompatActivity { + + private static final String TAG = "TrampolineActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + IterableLogger.v(TAG, "Notification Trampoline Activity created"); + } + + @Override + protected void onResume() { + super.onResume(); + IterableLogger.v(TAG, "Notification Trampoline Activity resumed"); + + Intent notificationIntent = getIntent(); + if (notificationIntent == null) { + finish(); + return; + } + String action = notificationIntent.getAction(); + if (action == null) { + IterableLogger.d(TAG, "Notification trampoline activity received intent with null action. Doing nothing."); + finish(); + return; + } + Intent pushContentIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); + pushContentIntent.setClass(this, IterablePushActionReceiver.class); + pushContentIntent.putExtras(notificationIntent.getExtras()); + this.sendBroadcast(pushContentIntent); + finish(); + } + + @Override + protected void onPause() { + super.onPause(); IterableLogger.v(TAG, "Notification Trampoline Activity on pause"); + + } + + @Override + protected void onDestroy() { + super.onDestroy(); + IterableLogger.v(TAG, "Notification Trampoline Activity destroyed"); + } +} \ No newline at end of file From d01042fbb82d54385c767150e266c0d2c82b9228 Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Wed, 1 Dec 2021 17:57:33 -0800 Subject: [PATCH 2/6] [MOB - 3772] - Make Trampoline Activity transparent 1. Removes the glitch like effect when loading the app through push notification 2. Also removes the Activity from recent app/ app switcher --- iterableapi/src/main/AndroidManifest.xml | 2 +- iterableapi/src/main/res/values/styles.xml | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 iterableapi/src/main/res/values/styles.xml diff --git a/iterableapi/src/main/AndroidManifest.xml b/iterableapi/src/main/AndroidManifest.xml index c5c819ee2..429fc8dbd 100644 --- a/iterableapi/src/main/AndroidManifest.xml +++ b/iterableapi/src/main/AndroidManifest.xml @@ -26,7 +26,7 @@ android:name=".TrampolineActivity" android:exported="false" android:launchMode="singleInstance" - android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar"/> + android:theme="@style/TrampolineActivity.Transparent"/> diff --git a/iterableapi/src/main/res/values/styles.xml b/iterableapi/src/main/res/values/styles.xml new file mode 100644 index 000000000..ebbd77a47 --- /dev/null +++ b/iterableapi/src/main/res/values/styles.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file From 29d67e3655c8ebb70d7e28dc2a07280b38119a6f Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Mon, 6 Dec 2021 13:08:16 -0800 Subject: [PATCH 3/6] Renaming TrampolineActivity to IterableTrampolineActivity --- iterableapi/src/main/AndroidManifest.xml | 2 +- .../iterable/iterableapi/IterableNotificationBuilder.java | 2 +- .../iterable/iterableapi/IterableNotificationHelper.java | 2 +- ...olineActivity.java => IterableTrampolineActivity.java} | 8 +++++--- 4 files changed, 8 insertions(+), 6 deletions(-) rename iterableapi/src/main/java/com/iterable/iterableapi/{TrampolineActivity.java => IterableTrampolineActivity.java} (89%) diff --git a/iterableapi/src/main/AndroidManifest.xml b/iterableapi/src/main/AndroidManifest.xml index 429fc8dbd..e8ec645a2 100644 --- a/iterableapi/src/main/AndroidManifest.xml +++ b/iterableapi/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java index efd6f4f50..f9cde9322 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java @@ -112,7 +112,7 @@ public Notification build() { */ public void createNotificationActionButton(Context context, IterableNotificationData.Button button, Bundle extras) { Intent buttonIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); - buttonIntent.setClass(context, TrampolineActivity.class); + buttonIntent.setClass(context, IterableTrampolineActivity.class); buttonIntent.putExtras(extras); buttonIntent.putExtra(IterableConstants.REQUEST_CODE, requestCode); buttonIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, button.identifier); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java index 14ca1901c..7b25a1223 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java @@ -178,7 +178,7 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex } //Create an intent for TrampolineActivity instead of BroadcastReceiver - Intent trampolineActivityIntent = new Intent(context, TrampolineActivity.class); + Intent trampolineActivityIntent = new Intent(context, IterableTrampolineActivity.class); trampolineActivityIntent.putExtras(extras); trampolineActivityIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT); trampolineActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/TrampolineActivity.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java similarity index 89% rename from iterableapi/src/main/java/com/iterable/iterableapi/TrampolineActivity.java rename to iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java index f9a986624..bfb001b6a 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/TrampolineActivity.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java @@ -4,9 +4,8 @@ import android.content.Intent; import android.os.Bundle; -import android.util.Log; -public class TrampolineActivity extends AppCompatActivity { +public class IterableTrampolineActivity extends AppCompatActivity { private static final String TAG = "TrampolineActivity"; @@ -32,6 +31,8 @@ protected void onResume() { finish(); return; } + + Intent pushContentIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); pushContentIntent.setClass(this, IterablePushActionReceiver.class); pushContentIntent.putExtras(notificationIntent.getExtras()); @@ -41,7 +42,8 @@ protected void onResume() { @Override protected void onPause() { - super.onPause(); IterableLogger.v(TAG, "Notification Trampoline Activity on pause"); + super.onPause(); + IterableLogger.v(TAG, "Notification Trampoline Activity on pause"); } From 059e840ddedb21a50e7f1b0c270f56bf28ebc95b Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Mon, 6 Dec 2021 16:27:34 -0800 Subject: [PATCH 4/6] Moving PushAction Receiver code to a common util function Trampoline Activity now performs all the actions previously carried out by PushActionReceiver using a util function where all the methods are migrated. Pending - PushActionReceiver to now handle only those conditions where the app knows if the Activity is not going to open. i.e CustomAction Handling with open App flag turned false --- .../com/iterable/iterableapi/IterableApi.java | 2 +- .../IterablePushActionReceiver.java | 135 ------------------ .../IterablePushNotificationUtil.java | 124 ++++++++++++++++ .../IterableTrampolineActivity.java | 32 ++++- 4 files changed, 150 insertions(+), 143 deletions(-) create mode 100644 iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java index 7b35883d4..435a187eb 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java @@ -303,7 +303,7 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey, } loadLastSavedConfiguration(context); - IterablePushActionReceiver.processPendingAction(context); + IterablePushNotificationUtil.processPendingAction(context); } public static void setContext(Context context) { diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java index 0403d4846..543637bdc 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java @@ -1,15 +1,8 @@ package com.iterable.iterableapi; -import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.Bundle; - -import androidx.core.app.RemoteInput; - -import org.json.JSONException; -import org.json.JSONObject; /** * Handles incoming push actions built by {@link IterableNotificationBuilder} @@ -17,137 +10,9 @@ */ public class IterablePushActionReceiver extends BroadcastReceiver { private static final String TAG = "IterablePushActionReceiver"; - // Used to hold intents until the SDK is initialized - private static PendingAction pendingAction = null; @Override public void onReceive(Context context, Intent intent) { - // Dismiss the notification - int requestCode = intent.getIntExtra(IterableConstants.REQUEST_CODE, 0); - NotificationManager mNotificationManager = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(requestCode); - - // Dismiss the notifications panel - try { - context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } catch (SecurityException e) { - IterableLogger.w(TAG, e.getLocalizedMessage()); - } - - String actionName = intent.getAction(); - if (IterableConstants.ACTION_PUSH_ACTION.equalsIgnoreCase(actionName)) { - handlePushAction(context, intent); - } - } - - static boolean processPendingAction(Context context) { - boolean handled = false; - if (pendingAction != null) { - handled = executeAction(context, pendingAction); - pendingAction = null; - } - return handled; - } - - private static void handlePushAction(Context context, Intent intent) { - if (intent.getExtras() == null) { - IterableLogger.e(TAG, "handlePushAction: extras == null, can't handle push action"); - return; - } - IterableNotificationData notificationData = new IterableNotificationData(intent.getExtras()); - String actionIdentifier = intent.getStringExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER); - IterableAction action = null; - JSONObject dataFields = new JSONObject(); - - boolean openApp = true; - - if (actionIdentifier != null) { - try { - if (actionIdentifier.equals(IterableConstants.ITERABLE_ACTION_DEFAULT)) { - // Default action (click on a push) - dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT); - action = notificationData.getDefaultAction(); - if (action == null) { - action = getLegacyDefaultActionFromPayload(intent.getExtras()); - } - } else { - dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, actionIdentifier); - IterableNotificationData.Button button = notificationData.getActionButton(actionIdentifier); - action = button.action; - openApp = button.openApp; - - if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) { - Bundle results = RemoteInput.getResultsFromIntent(intent); - if (results != null) { - String userInput = results.getString(IterableConstants.USER_INPUT); - if (userInput != null) { - dataFields.putOpt(IterableConstants.KEY_USER_TEXT, userInput); - action.userInput = userInput; - } - } - } - } - } catch (JSONException e) { - IterableLogger.e(TAG, "Encountered an exception while trying to handle the push action", e); - } - } - pendingAction = new PendingAction(intent, notificationData, action, openApp, dataFields); - - boolean handled = false; - if (IterableApi.getInstance().getMainActivityContext() != null) { - handled = processPendingAction(context); - } - - // Open the launcher activity if the action was not handled by anything, and openApp is true - if (openApp && !handled) { - Intent launcherIntent = IterableNotificationHelper.getMainActivityIntent(context); - launcherIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); - if (launcherIntent.resolveActivity(context.getPackageManager()) != null) { - context.startActivity(launcherIntent); - } - } - } - - private static boolean executeAction(Context context, PendingAction action) { - // Automatic tracking - IterableApi.sharedInstance.setPayloadData(action.intent); - IterableApi.sharedInstance.setNotificationData(action.notificationData); - IterableApi.sharedInstance.trackPushOpen(action.notificationData.getCampaignId(), action.notificationData.getTemplateId(), - action.notificationData.getMessageId(), action.dataFields); - - return IterableActionRunner.executeAction(context, action.iterableAction, IterableActionSource.PUSH); } - - private static IterableAction getLegacyDefaultActionFromPayload(Bundle extras) { - try { - if (extras.containsKey(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL)) { - JSONObject actionJson = new JSONObject(); - actionJson.put("type", IterableAction.ACTION_TYPE_OPEN_URL); - actionJson.put("data", extras.getString(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL)); - return IterableAction.from(actionJson); - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - private static class PendingAction { - Intent intent; - IterableNotificationData notificationData; - IterableAction iterableAction; - boolean openApp; - JSONObject dataFields; - - PendingAction(Intent intent, IterableNotificationData notificationData, IterableAction iterableAction, boolean openApp, JSONObject dataFields) { - this.intent = intent; - this.notificationData = notificationData; - this.iterableAction = iterableAction; - this.openApp = openApp; - this.dataFields = dataFields; - } - } - } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java new file mode 100644 index 000000000..4190e4a90 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java @@ -0,0 +1,124 @@ +package com.iterable.iterableapi; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import androidx.core.app.RemoteInput; + +import org.json.JSONException; +import org.json.JSONObject; + +public class IterablePushNotificationUtil { + private static PendingAction pendingAction = null; + private static final String TAG = "IterablePushNotificationUtil"; + + static boolean processPendingAction(Context context) { + boolean handled = false; + if (pendingAction != null) { + handled = executeAction(context, pendingAction); + pendingAction = null; + } + return handled; + } + + static boolean executeAction(Context context, PendingAction action) { + // Automatic tracking + IterableApi.sharedInstance.setPayloadData(action.intent); + IterableApi.sharedInstance.setNotificationData(action.notificationData); + IterableApi.sharedInstance.trackPushOpen(action.notificationData.getCampaignId(), action.notificationData.getTemplateId(), + action.notificationData.getMessageId(), action.dataFields); + + return IterableActionRunner.executeAction(context, action.iterableAction, IterableActionSource.PUSH); + } + + + static void handlePushAction(Context context, Intent intent) { + if (intent.getExtras() == null) { + IterableLogger.e(TAG, "handlePushAction: extras == null, can't handle push action"); + return; + } + IterableNotificationData notificationData = new IterableNotificationData(intent.getExtras()); + String actionIdentifier = intent.getStringExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER); + IterableAction action = null; + JSONObject dataFields = new JSONObject(); + + boolean openApp = true; + + if (actionIdentifier != null) { + try { + if (actionIdentifier.equals(IterableConstants.ITERABLE_ACTION_DEFAULT)) { + // Default action (click on a push) + dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT); + action = notificationData.getDefaultAction(); + if (action == null) { + action = getLegacyDefaultActionFromPayload(intent.getExtras()); + } + } else { + dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, actionIdentifier); + IterableNotificationData.Button button = notificationData.getActionButton(actionIdentifier); + action = button.action; + openApp = button.openApp; + + if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) { + Bundle results = RemoteInput.getResultsFromIntent(intent); + if (results != null) { + String userInput = results.getString(IterableConstants.USER_INPUT); + if (userInput != null) { + dataFields.putOpt(IterableConstants.KEY_USER_TEXT, userInput); + action.userInput = userInput; + } + } + } + } + } catch (JSONException e) { + IterableLogger.e(TAG, "Encountered an exception while trying to handle the push action", e); + } + } + pendingAction = new PendingAction(intent, notificationData, action, openApp, dataFields); + + boolean handled = false; + if (IterableApi.getInstance().getMainActivityContext() != null) { + handled = processPendingAction(context); + } + + // Open the launcher activity if the action was not handled by anything, and openApp is true + if (openApp && !handled) { + Intent launcherIntent = IterableNotificationHelper.getMainActivityIntent(context); + launcherIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + if (launcherIntent.resolveActivity(context.getPackageManager()) != null) { + context.startActivity(launcherIntent); + } + } + } + + private static IterableAction getLegacyDefaultActionFromPayload(Bundle extras) { + try { + if (extras.containsKey(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL)) { + JSONObject actionJson = new JSONObject(); + actionJson.put("type", IterableAction.ACTION_TYPE_OPEN_URL); + actionJson.put("data", extras.getString(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL)); + return IterableAction.from(actionJson); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private static class PendingAction { + Intent intent; + IterableNotificationData notificationData; + IterableAction iterableAction; + boolean openApp; + JSONObject dataFields; + + PendingAction(Intent intent, IterableNotificationData notificationData, IterableAction iterableAction, boolean openApp, JSONObject dataFields) { + this.intent = intent; + this.notificationData = notificationData; + this.iterableAction = iterableAction; + this.openApp = openApp; + this.dataFields = dataFields; + } + } +} diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java index bfb001b6a..83054a083 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java @@ -1,7 +1,11 @@ package com.iterable.iterableapi; +import static com.iterable.iterableapi.IterablePushNotificationUtil.handlePushAction; + import androidx.appcompat.app.AppCompatActivity; +import android.app.NotificationManager; +import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -22,21 +26,35 @@ protected void onResume() { Intent notificationIntent = getIntent(); if (notificationIntent == null) { + IterableLogger.d(TAG, "Intent is null. Doing nothing."); finish(); return; } - String action = notificationIntent.getAction(); - if (action == null) { - IterableLogger.d(TAG, "Notification trampoline activity received intent with null action. Doing nothing."); + String actionName = notificationIntent.getAction(); + if (actionName == null) { + IterableLogger.d(TAG, "Intent actino is null. Doing nothing."); finish(); return; } - Intent pushContentIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); - pushContentIntent.setClass(this, IterablePushActionReceiver.class); - pushContentIntent.putExtras(notificationIntent.getExtras()); - this.sendBroadcast(pushContentIntent); + // Dismiss the notification + int requestCode = notificationIntent.getIntExtra(IterableConstants.REQUEST_CODE, 0); + NotificationManager mNotificationManager = (NotificationManager) + this.getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.cancel(requestCode); + + // Dismiss the notifications panel + try { + this.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } catch (SecurityException e) { + IterableLogger.w(TAG, e.getLocalizedMessage()); + } + + if (IterableConstants.ACTION_PUSH_ACTION.equalsIgnoreCase(actionName)) { + handlePushAction(this, notificationIntent); + } + finish(); } From ce5e5872c48e5e443cf7b3c9a8209548c2c7eb3b Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Wed, 8 Dec 2021 12:52:04 -0800 Subject: [PATCH 5/6] Bug fix - Open app on notification tap Buttons actions were performing okay. But just tapping on notification was not opening the app. Had to create an Intent with IterableConstant string as actionName as before instead of creating Intent right out of the Activity. Tested and now it works on newer and older devices as well --- .../com/iterable/iterableapi/IterableNotificationHelper.java | 3 ++- .../com/iterable/iterableapi/IterableTrampolineActivity.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java index 7b25a1223..62ce24658 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java @@ -178,7 +178,8 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex } //Create an intent for TrampolineActivity instead of BroadcastReceiver - Intent trampolineActivityIntent = new Intent(context, IterableTrampolineActivity.class); + Intent trampolineActivityIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); + trampolineActivityIntent.setClass(context, IterableTrampolineActivity.class); trampolineActivityIntent.putExtras(extras); trampolineActivityIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT); trampolineActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java index 83054a083..2c07ef29c 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java @@ -30,14 +30,14 @@ protected void onResume() { finish(); return; } + String actionName = notificationIntent.getAction(); if (actionName == null) { - IterableLogger.d(TAG, "Intent actino is null. Doing nothing."); + IterableLogger.d(TAG, "Intent action is null. Doing nothing."); finish(); return; } - // Dismiss the notification int requestCode = notificationIntent.getIntExtra(IterableConstants.REQUEST_CODE, 0); NotificationManager mNotificationManager = (NotificationManager) From 4ec13b53072b8b77c845ff9da13c4749905d5845 Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Thu, 9 Dec 2021 09:26:01 -0800 Subject: [PATCH 6/6] Conditional routing PushActionReceiver also uses the util method that trampoline activity uses to process the notification data it gets. It will be invoked and sent data only for those actionButtons with customAction where SDK is sure that openApp is false. In this case, Trampoline Activity will not be launched and the notification intent will be passed to IterablePushActionReceiver, to complete the background customActionHandling. Immutable flag removed from actionButton pending intent as test were failing and it seems like it needs mutable pending intents there. Noticeable change due to this: Customers could earlier add button with CustomAction with openApp -> False. It just prevented the SDK to open the homescreen. But their customActionHandler could always open the activity. Now this wont be allowed as the route it follows will be through a Broadcast Receiver and not through the trampoline Activity --- .../IterableNotificationBuilder.java | 33 +++++++++++++------ .../IterablePushActionReceiver.java | 7 +++- .../IterablePushNotificationUtil.java | 20 ++++++++++- .../IterableTrampolineActivity.java | 22 ++----------- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java index f9cde9322..de7843897 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java @@ -111,24 +111,37 @@ public Notification build() { * @param extras Notification payload */ public void createNotificationActionButton(Context context, IterableNotificationData.Button button, Bundle extras) { + PendingIntent pendingButtonIntent = getPendingIntent(context, button, extras); + NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action + .Builder(NotificationCompat.BADGE_ICON_NONE, button.title, pendingButtonIntent); + if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) { + actionBuilder.addRemoteInput(new RemoteInput.Builder(IterableConstants.USER_INPUT).setLabel(button.inputPlaceholder).build()); + } + addAction(actionBuilder.build()); + } + + private PendingIntent getPendingIntent(Context context, IterableNotificationData.Button button, Bundle extras) { + PendingIntent pendingButtonIntent; + Intent buttonIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION); - buttonIntent.setClass(context, IterableTrampolineActivity.class); buttonIntent.putExtras(extras); buttonIntent.putExtra(IterableConstants.REQUEST_CODE, requestCode); buttonIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, button.identifier); buttonIntent.putExtra(IterableConstants.ACTION_IDENTIFIER, button.identifier); - PendingIntent pendingButtonIntent = PendingIntent.getActivity(context, buttonIntent.hashCode(), - buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action - .Builder(NotificationCompat.BADGE_ICON_NONE, button.title, pendingButtonIntent); - - if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) { - actionBuilder.addRemoteInput(new RemoteInput.Builder(IterableConstants.USER_INPUT).setLabel(button.inputPlaceholder).build()); + if (button.openApp) { + IterableLogger.d(TAG, "Go through TrampolineActivity"); + buttonIntent.setClass(context, IterableTrampolineActivity.class); + pendingButtonIntent = PendingIntent.getActivity(context, buttonIntent.hashCode(), + buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT); + } else { + IterableLogger.d(TAG, "Go through IterablePushActionReceiver"); + buttonIntent.setClass(context, IterablePushActionReceiver.class); + pendingButtonIntent = PendingIntent.getBroadcast(context, buttonIntent.hashCode(), + buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT); } - addAction(actionBuilder.build()); + return pendingButtonIntent; } /** diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java index 543637bdc..03c964d78 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java @@ -13,6 +13,11 @@ public class IterablePushActionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - + IterablePushNotificationUtil.dismissNotification(context, intent); + IterablePushNotificationUtil.dismissNotificationPanel(context); + String actionName = intent.getAction(); + if (IterableConstants.ACTION_PUSH_ACTION.equalsIgnoreCase(actionName)) { + IterablePushNotificationUtil.handlePushAction(context, intent); + } } } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java index 4190e4a90..9499db682 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java @@ -1,5 +1,6 @@ package com.iterable.iterableapi; +import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -9,7 +10,7 @@ import org.json.JSONException; import org.json.JSONObject; -public class IterablePushNotificationUtil { +class IterablePushNotificationUtil { private static PendingAction pendingAction = null; private static final String TAG = "IterablePushNotificationUtil"; @@ -121,4 +122,21 @@ private static class PendingAction { this.dataFields = dataFields; } } + + static void dismissNotificationPanel(Context context) { + // Dismiss the notifications panel + try { + context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } catch (SecurityException e) { + IterableLogger.w(TAG, e.getLocalizedMessage()); + } + } + + static void dismissNotification(Context context, Intent notificationIntent) { + // Dismiss the notification + int requestCode = notificationIntent.getIntExtra(IterableConstants.REQUEST_CODE, 0); + NotificationManager mNotificationManager = (NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.cancel(requestCode); + } } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java index 2c07ef29c..9e0e43781 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java @@ -1,11 +1,7 @@ package com.iterable.iterableapi; -import static com.iterable.iterableapi.IterablePushNotificationUtil.handlePushAction; - import androidx.appcompat.app.AppCompatActivity; -import android.app.NotificationManager; -import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -38,23 +34,11 @@ protected void onResume() { return; } - // Dismiss the notification - int requestCode = notificationIntent.getIntExtra(IterableConstants.REQUEST_CODE, 0); - NotificationManager mNotificationManager = (NotificationManager) - this.getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(requestCode); - - // Dismiss the notifications panel - try { - this.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } catch (SecurityException e) { - IterableLogger.w(TAG, e.getLocalizedMessage()); - } - + IterablePushNotificationUtil.dismissNotification(this, notificationIntent); + IterablePushNotificationUtil.dismissNotificationPanel(this); if (IterableConstants.ACTION_PUSH_ACTION.equalsIgnoreCase(actionName)) { - handlePushAction(this, notificationIntent); + IterablePushNotificationUtil.handlePushAction(this, notificationIntent); } - finish(); }