Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions iterableapi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ dependencies {
testImplementation 'org.robolectric:robolectric:4.4'
testImplementation 'org.robolectric:shadows-playservices:4.4'
testImplementation 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
testImplementation "org.powermock:powermock-module-junit4:2.0.9"
testImplementation "org.powermock:powermock-module-junit4-rule:2.0.9"
testImplementation "org.powermock:powermock-api-mockito2:2.0.9"
testImplementation "org.powermock:powermock-classloading-xstream:2.0.9"
testImplementation 'com.squareup.okhttp3:mockwebserver:4.2.2'
testImplementation 'org.skyscreamer:jsonassert:1.5.0'
androidTestImplementation 'androidx.test:runner:1.3.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,92 +4,108 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import android.util.Log;

import java.util.List;

class IterableActionRunner {
private static final String TAG = "IterableActionRunner";

/**
* Execute an {@link IterableAction} as a response to push action
* @param context Context
* @param action The original action object
* @return `true` if the action was handled, `false` if it was not
*/
static boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source) {
if (action == null) {
return false;
}

IterableActionContext actionContext = new IterableActionContext(action, source);
@VisibleForTesting
static IterableActionRunnerImpl instance = new IterableActionRunnerImpl();

if (action.isOfType(IterableAction.ACTION_TYPE_OPEN_URL)) {
return openUri(context, Uri.parse(action.getData()), actionContext);
} else {
return callCustomActionIfSpecified(action, actionContext);
}
static boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source) {
return instance.executeAction(context, action, source);
}

/**
* Handle {@link IterableAction#ACTION_TYPE_OPEN_URL} action type
* Calls {@link IterableUrlHandler} for custom handling by the app. If the handle does not exist
* or returns `false`, the SDK tries to find an activity that can open this URL.
* @param context Context
* @param uri The URL to open
* @param actionContext The original action object
* @return `true` if the action was handled, or an activity was found for this URL
* `false` if the handler did not handle this URL and no activity was found to open it with
*/
private static boolean openUri(@NonNull Context context, @NonNull Uri uri, @NonNull IterableActionContext actionContext) {
if (IterableApi.sharedInstance.config.urlHandler != null) {
if (IterableApi.sharedInstance.config.urlHandler.handleIterableURL(uri, actionContext)) {
return true;
static class IterableActionRunnerImpl {
private static final String TAG = "IterableActionRunner";

/**
* Execute an {@link IterableAction} as a response to push action
*
* @param context Context
* @param action The original action object
* @return `true` if the action was handled, `false` if it was not
*/
boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source) {
if (action == null) {
return false;
}

IterableActionContext actionContext = new IterableActionContext(action, source);

if (action.isOfType(IterableAction.ACTION_TYPE_OPEN_URL)) {
return openUri(context, Uri.parse(action.getData()), actionContext);
} else {
return callCustomActionIfSpecified(action, actionContext);
}
}

// Handle URL: check for deep links within the app
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);

List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities(intent, 0);
if (resolveInfos.size() > 1) {
for (ResolveInfo resolveInfo : resolveInfos) {
if (resolveInfo.activityInfo.packageName.equals(context.getPackageName())) {
Log.d(TAG, "The deep link will be handled by the app: " + resolveInfo.activityInfo.packageName);
intent.setPackage(resolveInfo.activityInfo.packageName);
break;
/**
* Handle {@link IterableAction#ACTION_TYPE_OPEN_URL} action type
* Calls {@link IterableUrlHandler} for custom handling by the app. If the handle does not exist
* or returns `false`, the SDK tries to find an activity that can open this URL.
*
* @param context Context
* @param uri The URL to open
* @param actionContext The original action object
* @return `true` if the action was handled, or an activity was found for this URL
* `false` if the handler did not handle this URL and no activity was found to open it with
*/
private boolean openUri(@NonNull Context context, @NonNull Uri uri, @NonNull IterableActionContext actionContext) {
if (IterableApi.sharedInstance.config.urlHandler != null) {
if (IterableApi.sharedInstance.config.urlHandler.handleIterableURL(uri, actionContext)) {
return true;
}
}
}

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// Handle URL: check for deep links within the app
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);

if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
return true;
} else {
IterableLogger.e(TAG, "Could not find activities to handle deep link:" + uri);
return false;
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities(intent, 0);
if (resolveInfos.size() > 1) {
for (ResolveInfo resolveInfo : resolveInfos) {
if (resolveInfo.activityInfo.packageName.equals(context.getPackageName())) {
Log.d(TAG, "The deep link will be handled by the app: " + resolveInfo.activityInfo.packageName);
intent.setPackage(resolveInfo.activityInfo.packageName);
break;
}
}
}

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
return true;
} else {
IterableLogger.e(TAG, "Could not find activities to handle deep link:" + uri);
return false;
}
}
}

/**
* Handle custom actions passed from push notifications
* @param action {@link IterableAction} object that contains action payload
* @return `true` if the action is valid and was handled by the handler
* `false` if the action is invalid or the handler returned `false`
*/
private static boolean callCustomActionIfSpecified(@NonNull IterableAction action, @NonNull IterableActionContext actionContext) {
if (action.getType() != null && !action.getType().isEmpty()) {
// Call custom action handler
if (IterableApi.sharedInstance.config.customActionHandler != null) {
return IterableApi.sharedInstance.config.customActionHandler.handleIterableCustomAction(action, actionContext);
/**
* Handle custom actions passed from push notifications
*
* @param action {@link IterableAction} object that contains action payload
* @return `true` if the action is valid and was handled by the handler
* `false` if the action is invalid or the handler returned `false`
*/
private boolean callCustomActionIfSpecified(@NonNull IterableAction action, @NonNull IterableActionContext actionContext) {
if (action.getType() != null && !action.getType().isEmpty()) {
// Call custom action handler
if (IterableApi.sharedInstance.config.customActionHandler != null) {
return IterableApi.sharedInstance.config.customActionHandler.handleIterableCustomAction(action, actionContext);
}
}
return false;
}
return false;
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.robolectric.RuntimeEnvironment;

import java.util.concurrent.TimeUnit;
Expand All @@ -27,23 +25,30 @@
import static junit.framework.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;

@PrepareForTest(IterableActionRunner.class)
public class IterablePushActionReceiverTest extends BasePowerMockTest {
public class IterablePushActionReceiverTest extends BaseTest {

private MockWebServer server;
private IterableActionRunner.IterableActionRunnerImpl actionRunnerMock;

@Before
public void setUp() throws Exception {
IterableApi.sharedInstance = new IterableApi();
IterableTestUtils.createIterableApi();
server = new MockWebServer();
IterableApi.overrideURLEndpointPath(server.url("").toString());

actionRunnerMock = mock(IterableActionRunner.IterableActionRunnerImpl.class);
IterableActionRunner.instance = actionRunnerMock;
}

@After
public void tearDown() throws Exception {
IterableActionRunner.instance = new IterableActionRunner.IterableActionRunnerImpl();

server.shutdown();
server = null;
}
Expand All @@ -70,14 +75,12 @@ public void testTrackPushOpenWithCustomAction() throws Exception {
Intent intent = new Intent(IterableConstants.ACTION_PUSH_ACTION);
intent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT);
intent.putExtra(IterableConstants.ITERABLE_DATA_KEY, IterableTestUtils.getResourceString("push_payload_custom_action.json"));
PowerMockito.mockStatic(IterableActionRunner.class);

iterablePushActionReceiver.onReceive(RuntimeEnvironment.application, intent);

// Verify that IterableActionRunner was called with the proper action
PowerMockito.verifyStatic(IterableActionRunner.class);
ArgumentCaptor<IterableAction> capturedAction = ArgumentCaptor.forClass(IterableAction.class);
IterableActionRunner.executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH));
verify(actionRunnerMock).executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH));
assertEquals("customAction", capturedAction.getValue().getType());

// Verify that the main app activity was launched
Expand Down Expand Up @@ -126,14 +129,11 @@ public void testPushActionWithTextInput() throws Exception {
clipDataIntent.putExtra(RemoteInput.EXTRA_RESULTS_DATA, resultsBundle);
intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));

PowerMockito.mockStatic(IterableActionRunner.class);

iterablePushActionReceiver.onReceive(RuntimeEnvironment.application, intent);

// Verify that IterableActionRunner was called with the proper action
PowerMockito.verifyStatic(IterableActionRunner.class);
ArgumentCaptor<IterableAction> actionCaptor = ArgumentCaptor.forClass(IterableAction.class);
IterableActionRunner.executeAction(any(Context.class), actionCaptor.capture(), eq(IterableActionSource.PUSH));
verify(actionRunnerMock).executeAction(any(Context.class), actionCaptor.capture(), eq(IterableActionSource.PUSH));
IterableAction capturedAction = actionCaptor.getValue();
assertEquals("handleTextInput", capturedAction.getType());
assertEquals("input text", capturedAction.userInput);
Expand All @@ -146,14 +146,12 @@ public void testLegacyDeepLinkPayload() throws Exception {
Intent intent = new Intent(IterableConstants.ACTION_PUSH_ACTION);
intent.putExtras(IterableTestUtils.getBundleFromJsonResource("push_payload_legacy_deep_link.json"));
intent.putExtra(IterableConstants.ITERABLE_DATA_ACTION_IDENTIFIER, IterableConstants.ITERABLE_ACTION_DEFAULT);
PowerMockito.mockStatic(IterableActionRunner.class);

iterablePushActionReceiver.onReceive(RuntimeEnvironment.application, intent);

// Verify that IterableActionRunner was called with openUrl action
PowerMockito.verifyStatic(IterableActionRunner.class);
ArgumentCaptor<IterableAction> capturedAction = ArgumentCaptor.forClass(IterableAction.class);
IterableActionRunner.executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH));
verify(actionRunnerMock).executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH));
assertEquals("openUrl", capturedAction.getValue().getType());
assertEquals("https://example.com", capturedAction.getValue().getData());
}
Expand Down