diff --git a/iterableapi/src/main/AndroidManifest.xml b/iterableapi/src/main/AndroidManifest.xml index 3afe07bca..e8ec645a2 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/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/IterableNotificationBuilder.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java index 5670b18be..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, IterablePushActionReceiver.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); - - 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/IterableNotificationHelper.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java index 1a603ed5c..62ce24658 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java @@ -177,10 +177,12 @@ 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(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); // Action buttons if (notificationData.getActionButtons() != null) { @@ -192,8 +194,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/IterablePushActionReceiver.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushActionReceiver.java index 0403d4846..03c964d78 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,14 @@ */ 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()); - } - + IterablePushNotificationUtil.dismissNotification(context, intent); + IterablePushNotificationUtil.dismissNotificationPanel(context); 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; + IterablePushNotificationUtil.handlePushAction(context, intent); } - 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..9499db682 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterablePushNotificationUtil.java @@ -0,0 +1,142 @@ +package com.iterable.iterableapi; + +import android.app.NotificationManager; +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; + +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; + } + } + + 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 new file mode 100644 index 000000000..9e0e43781 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableTrampolineActivity.java @@ -0,0 +1,57 @@ +package com.iterable.iterableapi; + +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.os.Bundle; + +public class IterableTrampolineActivity 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) { + IterableLogger.d(TAG, "Intent is null. Doing nothing."); + finish(); + return; + } + + String actionName = notificationIntent.getAction(); + if (actionName == null) { + IterableLogger.d(TAG, "Intent action is null. Doing nothing."); + finish(); + return; + } + + IterablePushNotificationUtil.dismissNotification(this, notificationIntent); + IterablePushNotificationUtil.dismissNotificationPanel(this); + if (IterableConstants.ACTION_PUSH_ACTION.equalsIgnoreCase(actionName)) { + IterablePushNotificationUtil.handlePushAction(this, notificationIntent); + } + 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 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