Skip to content

Commit

Permalink
Merge pull request #404 from Iterable/MOB-3754-Handling-Trampoline-Re…
Browse files Browse the repository at this point in the history
…striction

[MOB - 3754] - Handling Trampoline Restriction
  • Loading branch information
Ayyanchira authored Jan 10, 2022
2 parents 4271944 + 4ec13b5 commit 531311d
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 150 deletions.
5 changes: 5 additions & 0 deletions iterableapi/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
</intent-filter>
</receiver>

<activity
android:name=".IterableTrampolineActivity"
android:exported="false"
android:launchMode="singleInstance"
android:theme="@style/TrampolineActivity.Transparent"/>
</application>

<queries>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,153 +1,23 @@
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}
* Action id is passed in the Intent extras under {@link IterableConstants#REQUEST_CODE}
*/
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;
}
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 531311d

Please sign in to comment.