Skip to content

Commit 2a41d74

Browse files
authored
Merge branch 'master' into scaires/Issue-377_pending_intents_android_12
2 parents 43c90fc + 531311d commit 2a41d74

File tree

8 files changed

+251
-150
lines changed

8 files changed

+251
-150
lines changed

iterableapi/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
</intent-filter>
2323
</receiver>
2424

25+
<activity
26+
android:name=".IterableTrampolineActivity"
27+
android:exported="false"
28+
android:launchMode="singleInstance"
29+
android:theme="@style/TrampolineActivity.Transparent"/>
2530
</application>
2631

2732
<queries>

iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey,
303303
}
304304

305305
loadLastSavedConfiguration(context);
306-
IterablePushActionReceiver.processPendingAction(context);
306+
IterablePushNotificationUtil.processPendingAction(context);
307307
}
308308

309309
public static void setContext(Context context) {

iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationBuilder.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,37 @@ public Notification build() {
111111
* @param extras Notification payload
112112
*/
113113
public void createNotificationActionButton(Context context, IterableNotificationData.Button button, Bundle extras) {
114+
PendingIntent pendingButtonIntent = getPendingIntent(context, button, extras);
115+
NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action
116+
.Builder(NotificationCompat.BADGE_ICON_NONE, button.title, pendingButtonIntent);
117+
if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) {
118+
actionBuilder.addRemoteInput(new RemoteInput.Builder(IterableConstants.USER_INPUT).setLabel(button.inputPlaceholder).build());
119+
}
120+
addAction(actionBuilder.build());
121+
}
122+
123+
private PendingIntent getPendingIntent(Context context, IterableNotificationData.Button button, Bundle extras) {
124+
PendingIntent pendingButtonIntent;
125+
114126
Intent buttonIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION);
115-
buttonIntent.setClass(context, IterablePushActionReceiver.class);
116127
buttonIntent.putExtras(extras);
117128
buttonIntent.putExtra(IterableConstants.REQUEST_CODE, requestCode);
118129
buttonIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, button.identifier);
119130
buttonIntent.putExtra(IterableConstants.ACTION_IDENTIFIER, button.identifier);
120131

121-
PendingIntent pendingButtonIntent = PendingIntent.getBroadcast(context, buttonIntent.hashCode(),
122-
buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
123-
124-
NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action
125-
.Builder(NotificationCompat.BADGE_ICON_NONE, button.title, pendingButtonIntent);
126-
127-
if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) {
128-
actionBuilder.addRemoteInput(new RemoteInput.Builder(IterableConstants.USER_INPUT).setLabel(button.inputPlaceholder).build());
132+
if (button.openApp) {
133+
IterableLogger.d(TAG, "Go through TrampolineActivity");
134+
buttonIntent.setClass(context, IterableTrampolineActivity.class);
135+
pendingButtonIntent = PendingIntent.getActivity(context, buttonIntent.hashCode(),
136+
buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
137+
} else {
138+
IterableLogger.d(TAG, "Go through IterablePushActionReceiver");
139+
buttonIntent.setClass(context, IterablePushActionReceiver.class);
140+
pendingButtonIntent = PendingIntent.getBroadcast(context, buttonIntent.hashCode(),
141+
buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
129142
}
130143

131-
addAction(actionBuilder.build());
144+
return pendingButtonIntent;
132145
}
133146

134147
/**

iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,12 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex
177177
IterableLogger.d(IterableNotificationBuilder.TAG, "Request code = " + notificationBuilder.requestCode);
178178
}
179179

180-
Intent pushContentIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION);
181-
pushContentIntent.setClass(context, IterablePushActionReceiver.class);
182-
pushContentIntent.putExtras(extras);
183-
pushContentIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT);
180+
//Create an intent for TrampolineActivity instead of BroadcastReceiver
181+
Intent trampolineActivityIntent = new Intent(IterableConstants.ACTION_PUSH_ACTION);
182+
trampolineActivityIntent.setClass(context, IterableTrampolineActivity.class);
183+
trampolineActivityIntent.putExtras(extras);
184+
trampolineActivityIntent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT);
185+
trampolineActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
184186

185187
// Action buttons
186188
if (notificationData.getActionButtons() != null) {
@@ -192,8 +194,8 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex
192194
}
193195
}
194196

195-
PendingIntent notificationClickedIntent = PendingIntent.getBroadcast(context, notificationBuilder.requestCode,
196-
pushContentIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
197+
PendingIntent notificationClickedIntent = PendingIntent.getActivity(context, notificationBuilder.requestCode,
198+
trampolineActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
197199

198200
notificationBuilder.setContentIntent(notificationClickedIntent);
199201
notificationBuilder.setIsGhostPush(isGhostPush(extras));
Lines changed: 3 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,23 @@
11
package com.iterable.iterableapi;
22

3-
import android.app.NotificationManager;
43
import android.content.BroadcastReceiver;
54
import android.content.Context;
65
import android.content.Intent;
7-
import android.os.Bundle;
8-
9-
import androidx.core.app.RemoteInput;
10-
11-
import org.json.JSONException;
12-
import org.json.JSONObject;
136

147
/**
158
* Handles incoming push actions built by {@link IterableNotificationBuilder}
169
* Action id is passed in the Intent extras under {@link IterableConstants#REQUEST_CODE}
1710
*/
1811
public class IterablePushActionReceiver extends BroadcastReceiver {
1912
private static final String TAG = "IterablePushActionReceiver";
20-
// Used to hold intents until the SDK is initialized
21-
private static PendingAction pendingAction = null;
2213

2314
@Override
2415
public void onReceive(Context context, Intent intent) {
25-
// Dismiss the notification
26-
int requestCode = intent.getIntExtra(IterableConstants.REQUEST_CODE, 0);
27-
NotificationManager mNotificationManager = (NotificationManager)
28-
context.getSystemService(Context.NOTIFICATION_SERVICE);
29-
mNotificationManager.cancel(requestCode);
30-
31-
// Dismiss the notifications panel
32-
try {
33-
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
34-
} catch (SecurityException e) {
35-
IterableLogger.w(TAG, e.getLocalizedMessage());
36-
}
37-
16+
IterablePushNotificationUtil.dismissNotification(context, intent);
17+
IterablePushNotificationUtil.dismissNotificationPanel(context);
3818
String actionName = intent.getAction();
3919
if (IterableConstants.ACTION_PUSH_ACTION.equalsIgnoreCase(actionName)) {
40-
handlePushAction(context, intent);
41-
}
42-
}
43-
44-
static boolean processPendingAction(Context context) {
45-
boolean handled = false;
46-
if (pendingAction != null) {
47-
handled = executeAction(context, pendingAction);
48-
pendingAction = null;
20+
IterablePushNotificationUtil.handlePushAction(context, intent);
4921
}
50-
return handled;
5122
}
52-
53-
private static void handlePushAction(Context context, Intent intent) {
54-
if (intent.getExtras() == null) {
55-
IterableLogger.e(TAG, "handlePushAction: extras == null, can't handle push action");
56-
return;
57-
}
58-
IterableNotificationData notificationData = new IterableNotificationData(intent.getExtras());
59-
String actionIdentifier = intent.getStringExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER);
60-
IterableAction action = null;
61-
JSONObject dataFields = new JSONObject();
62-
63-
boolean openApp = true;
64-
65-
if (actionIdentifier != null) {
66-
try {
67-
if (actionIdentifier.equals(IterableConstants.ITERABLE_ACTION_DEFAULT)) {
68-
// Default action (click on a push)
69-
dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT);
70-
action = notificationData.getDefaultAction();
71-
if (action == null) {
72-
action = getLegacyDefaultActionFromPayload(intent.getExtras());
73-
}
74-
} else {
75-
dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, actionIdentifier);
76-
IterableNotificationData.Button button = notificationData.getActionButton(actionIdentifier);
77-
action = button.action;
78-
openApp = button.openApp;
79-
80-
if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) {
81-
Bundle results = RemoteInput.getResultsFromIntent(intent);
82-
if (results != null) {
83-
String userInput = results.getString(IterableConstants.USER_INPUT);
84-
if (userInput != null) {
85-
dataFields.putOpt(IterableConstants.KEY_USER_TEXT, userInput);
86-
action.userInput = userInput;
87-
}
88-
}
89-
}
90-
}
91-
} catch (JSONException e) {
92-
IterableLogger.e(TAG, "Encountered an exception while trying to handle the push action", e);
93-
}
94-
}
95-
96-
pendingAction = new PendingAction(intent, notificationData, action, openApp, dataFields);
97-
98-
boolean handled = false;
99-
if (IterableApi.getInstance().getMainActivityContext() != null) {
100-
handled = processPendingAction(context);
101-
}
102-
103-
// Open the launcher activity if the action was not handled by anything, and openApp is true
104-
if (openApp && !handled) {
105-
Intent launcherIntent = IterableNotificationHelper.getMainActivityIntent(context);
106-
launcherIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
107-
if (launcherIntent.resolveActivity(context.getPackageManager()) != null) {
108-
context.startActivity(launcherIntent);
109-
}
110-
}
111-
}
112-
113-
private static boolean executeAction(Context context, PendingAction action) {
114-
// Automatic tracking
115-
IterableApi.sharedInstance.setPayloadData(action.intent);
116-
IterableApi.sharedInstance.setNotificationData(action.notificationData);
117-
IterableApi.sharedInstance.trackPushOpen(action.notificationData.getCampaignId(), action.notificationData.getTemplateId(),
118-
action.notificationData.getMessageId(), action.dataFields);
119-
120-
return IterableActionRunner.executeAction(context, action.iterableAction, IterableActionSource.PUSH);
121-
}
122-
123-
private static IterableAction getLegacyDefaultActionFromPayload(Bundle extras) {
124-
try {
125-
if (extras.containsKey(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL)) {
126-
JSONObject actionJson = new JSONObject();
127-
actionJson.put("type", IterableAction.ACTION_TYPE_OPEN_URL);
128-
actionJson.put("data", extras.getString(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL));
129-
return IterableAction.from(actionJson);
130-
}
131-
} catch (Exception e) {
132-
e.printStackTrace();
133-
}
134-
return null;
135-
}
136-
137-
private static class PendingAction {
138-
Intent intent;
139-
IterableNotificationData notificationData;
140-
IterableAction iterableAction;
141-
boolean openApp;
142-
JSONObject dataFields;
143-
144-
PendingAction(Intent intent, IterableNotificationData notificationData, IterableAction iterableAction, boolean openApp, JSONObject dataFields) {
145-
this.intent = intent;
146-
this.notificationData = notificationData;
147-
this.iterableAction = iterableAction;
148-
this.openApp = openApp;
149-
this.dataFields = dataFields;
150-
}
151-
}
152-
15323
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package com.iterable.iterableapi;
2+
3+
import android.app.NotificationManager;
4+
import android.content.Context;
5+
import android.content.Intent;
6+
import android.os.Bundle;
7+
8+
import androidx.core.app.RemoteInput;
9+
10+
import org.json.JSONException;
11+
import org.json.JSONObject;
12+
13+
class IterablePushNotificationUtil {
14+
private static PendingAction pendingAction = null;
15+
private static final String TAG = "IterablePushNotificationUtil";
16+
17+
static boolean processPendingAction(Context context) {
18+
boolean handled = false;
19+
if (pendingAction != null) {
20+
handled = executeAction(context, pendingAction);
21+
pendingAction = null;
22+
}
23+
return handled;
24+
}
25+
26+
static boolean executeAction(Context context, PendingAction action) {
27+
// Automatic tracking
28+
IterableApi.sharedInstance.setPayloadData(action.intent);
29+
IterableApi.sharedInstance.setNotificationData(action.notificationData);
30+
IterableApi.sharedInstance.trackPushOpen(action.notificationData.getCampaignId(), action.notificationData.getTemplateId(),
31+
action.notificationData.getMessageId(), action.dataFields);
32+
33+
return IterableActionRunner.executeAction(context, action.iterableAction, IterableActionSource.PUSH);
34+
}
35+
36+
37+
static void handlePushAction(Context context, Intent intent) {
38+
if (intent.getExtras() == null) {
39+
IterableLogger.e(TAG, "handlePushAction: extras == null, can't handle push action");
40+
return;
41+
}
42+
IterableNotificationData notificationData = new IterableNotificationData(intent.getExtras());
43+
String actionIdentifier = intent.getStringExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER);
44+
IterableAction action = null;
45+
JSONObject dataFields = new JSONObject();
46+
47+
boolean openApp = true;
48+
49+
if (actionIdentifier != null) {
50+
try {
51+
if (actionIdentifier.equals(IterableConstants.ITERABLE_ACTION_DEFAULT)) {
52+
// Default action (click on a push)
53+
dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT);
54+
action = notificationData.getDefaultAction();
55+
if (action == null) {
56+
action = getLegacyDefaultActionFromPayload(intent.getExtras());
57+
}
58+
} else {
59+
dataFields.put(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, actionIdentifier);
60+
IterableNotificationData.Button button = notificationData.getActionButton(actionIdentifier);
61+
action = button.action;
62+
openApp = button.openApp;
63+
64+
if (button.buttonType.equals(IterableNotificationData.Button.BUTTON_TYPE_TEXT_INPUT)) {
65+
Bundle results = RemoteInput.getResultsFromIntent(intent);
66+
if (results != null) {
67+
String userInput = results.getString(IterableConstants.USER_INPUT);
68+
if (userInput != null) {
69+
dataFields.putOpt(IterableConstants.KEY_USER_TEXT, userInput);
70+
action.userInput = userInput;
71+
}
72+
}
73+
}
74+
}
75+
} catch (JSONException e) {
76+
IterableLogger.e(TAG, "Encountered an exception while trying to handle the push action", e);
77+
}
78+
}
79+
pendingAction = new PendingAction(intent, notificationData, action, openApp, dataFields);
80+
81+
boolean handled = false;
82+
if (IterableApi.getInstance().getMainActivityContext() != null) {
83+
handled = processPendingAction(context);
84+
}
85+
86+
// Open the launcher activity if the action was not handled by anything, and openApp is true
87+
if (openApp && !handled) {
88+
Intent launcherIntent = IterableNotificationHelper.getMainActivityIntent(context);
89+
launcherIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
90+
if (launcherIntent.resolveActivity(context.getPackageManager()) != null) {
91+
context.startActivity(launcherIntent);
92+
}
93+
}
94+
}
95+
96+
private static IterableAction getLegacyDefaultActionFromPayload(Bundle extras) {
97+
try {
98+
if (extras.containsKey(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL)) {
99+
JSONObject actionJson = new JSONObject();
100+
actionJson.put("type", IterableAction.ACTION_TYPE_OPEN_URL);
101+
actionJson.put("data", extras.getString(IterableConstants.ITERABLE_DATA_DEEP_LINK_URL));
102+
return IterableAction.from(actionJson);
103+
}
104+
} catch (Exception e) {
105+
e.printStackTrace();
106+
}
107+
return null;
108+
}
109+
110+
private static class PendingAction {
111+
Intent intent;
112+
IterableNotificationData notificationData;
113+
IterableAction iterableAction;
114+
boolean openApp;
115+
JSONObject dataFields;
116+
117+
PendingAction(Intent intent, IterableNotificationData notificationData, IterableAction iterableAction, boolean openApp, JSONObject dataFields) {
118+
this.intent = intent;
119+
this.notificationData = notificationData;
120+
this.iterableAction = iterableAction;
121+
this.openApp = openApp;
122+
this.dataFields = dataFields;
123+
}
124+
}
125+
126+
static void dismissNotificationPanel(Context context) {
127+
// Dismiss the notifications panel
128+
try {
129+
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
130+
} catch (SecurityException e) {
131+
IterableLogger.w(TAG, e.getLocalizedMessage());
132+
}
133+
}
134+
135+
static void dismissNotification(Context context, Intent notificationIntent) {
136+
// Dismiss the notification
137+
int requestCode = notificationIntent.getIntExtra(IterableConstants.REQUEST_CODE, 0);
138+
NotificationManager mNotificationManager = (NotificationManager)
139+
context.getSystemService(Context.NOTIFICATION_SERVICE);
140+
mNotificationManager.cancel(requestCode);
141+
}
142+
}

0 commit comments

Comments
 (0)