Skip to content

Commit 11902f9

Browse files
authored
Add ability to set Intent.FLAG_ACTIVITY_CLEAR_TOP on the CustomTabsIntent (#116)
* WIP - Add ability to set Intent.FLAG_ACTIVITY_CLEAR_TOP on the CustomTabsIntent * Add a null check for launchType * Add unit tests and mockk * Add changelog entry and kdoc for LaunchType
1 parent 91b96cf commit 11902f9

File tree

9 files changed

+128
-11
lines changed

9 files changed

+128
-11
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# browser-switch-android Release Notes
22

3+
## unreleased
4+
5+
* Add `LaunchType` to `BrowserSwitchOptions` to specify how the browser switch should be launched
6+
* Add ability to set `Intent.FLAG_ACTIVITY_CLEAR_TOP` on the `CustomTabsIntent`
7+
* Deprecate `launchAsNewTask` in `BrowserSwitchOptions` in favor of `LaunchType`
8+
39
## 3.0.0
410

511
* Upgrade `compileSdkVersion` and `targetSdkVersion` to API 35

browser-switch/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ dependencies {
4747
testImplementation deps.mockitoCore
4848
testImplementation deps.jsonassert
4949
testImplementation deps.robolectric
50+
testImplementation deps.mockk
5051
}
5152

5253
// region signing and publishing
@@ -60,4 +61,3 @@ project.ext.pom_desc = "Android Browser Switch makes it easy to open a url in a
6061
apply from: rootProject.file("gradle/gradle-publish.gradle")
6162

6263
// endregion
63-

browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public BrowserSwitchStartResult start(@NonNull ComponentActivity activity, @NonN
6666
"Unable to start browser switch while host Activity is finishing.";
6767
return new BrowserSwitchStartResult.Failure(new BrowserSwitchException(activityFinishingMessage));
6868
} else {
69-
boolean launchAsNewTask = browserSwitchOptions.isLaunchAsNewTask();
69+
LaunchType launchType = browserSwitchOptions.getLaunchType();
7070
BrowserSwitchRequest request;
7171
try {
7272
request = new BrowserSwitchRequest(
@@ -76,7 +76,7 @@ public BrowserSwitchStartResult start(@NonNull ComponentActivity activity, @NonN
7676
returnUrlScheme,
7777
appLinkUri
7878
);
79-
customTabsInternalClient.launchUrl(activity, browserSwitchUrl, launchAsNewTask);
79+
customTabsInternalClient.launchUrl(activity, browserSwitchUrl, launchType);
8080
return new BrowserSwitchStartResult.Started(request.toBase64EncodedJSON());
8181
} catch (ActivityNotFoundException | BrowserSwitchException e) {
8282
return new BrowserSwitchStartResult.Failure(new BrowserSwitchException("Unable to start browser switch without a web browser.", e));

browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchOptions.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class BrowserSwitchOptions {
1919
private Uri appLinkUri;
2020

2121
private boolean launchAsNewTask;
22+
private LaunchType launchType;
2223

2324
/**
2425
* Set browser switch metadata.
@@ -117,12 +118,39 @@ public Uri getAppLinkUri() {
117118
return appLinkUri;
118119
}
119120

121+
/**
122+
* @deprecated Use {@link #getLaunchType()} instead.
123+
*/
124+
@Deprecated
120125
public boolean isLaunchAsNewTask() {
121126
return launchAsNewTask;
122127
}
123128

129+
/**
130+
* @deprecated Use {@link #launchType(LaunchType)} instead.
131+
*/
132+
@Deprecated
124133
public BrowserSwitchOptions launchAsNewTask(boolean launchAsNewTask) {
125134
this.launchAsNewTask = launchAsNewTask;
135+
this.launchType = LaunchType.ACTIVITY_NEW_TASK;
136+
return this;
137+
}
138+
139+
/**
140+
* @return the activity launch type flag.
141+
*/
142+
public LaunchType getLaunchType() {
143+
return launchType;
144+
}
145+
146+
/**
147+
* Sets the activity launch type flag.
148+
*
149+
* @param launchType the type of launch for the browser activity
150+
* @return {@link BrowserSwitchOptions} reference to instance to allow setter invocations to be chained
151+
*/
152+
public BrowserSwitchOptions launchType(LaunchType launchType) {
153+
this.launchType = launchType;
126154
return this;
127155
}
128156
}

browser-switch/src/main/java/com/braintreepayments/api/ChromeCustomTabsInternalClient.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ class ChromeCustomTabsInternalClient {
2121
this.customTabsIntentBuilder = builder;
2222
}
2323

24-
void launchUrl(Context context, Uri url, boolean launchAsNewTask) throws ActivityNotFoundException{
24+
void launchUrl(Context context, Uri url, LaunchType launchType) throws ActivityNotFoundException {
2525
CustomTabsIntent customTabsIntent = customTabsIntentBuilder.build();
26-
if (launchAsNewTask) {
27-
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
26+
if (launchType != null) {
27+
switch (launchType) {
28+
case ACTIVITY_NEW_TASK:
29+
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
30+
break;
31+
case ACTIVITY_CLEAR_TOP:
32+
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
33+
break;
34+
}
2835
}
2936
customTabsIntent.launchUrl(context, url);
3037
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.braintreepayments.api
2+
3+
/**
4+
* Enum representing the type of launch for an activity.
5+
*
6+
* - [ACTIVITY_NEW_TASK]: sets the `Intent.FLAG_ACTIVITY_NEW_TASK` flag.
7+
* - [ACTIVITY_CLEAR_TOP]: sets the `Intent.FLAG_ACTIVITY_CLEAR_TOP` flag.
8+
*/
9+
enum class LaunchType {
10+
ACTIVITY_NEW_TASK,
11+
ACTIVITY_CLEAR_TOP
12+
}

browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchClientUnitTest.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public class BrowserSwitchClientUnitTest {
3737
private ChromeCustomTabsInternalClient customTabsInternalClient;
3838

3939
private Uri browserSwitchDestinationUrl;
40-
private Uri appLinkUri;
4140
private Context applicationContext;
4241

4342
private ComponentActivity componentActivity;
@@ -48,7 +47,6 @@ public void beforeEach() {
4847
customTabsInternalClient = mock(ChromeCustomTabsInternalClient.class);
4948

5049
browserSwitchDestinationUrl = Uri.parse("https://example.com/browser_switch_destination");
51-
appLinkUri = Uri.parse("https://example.com");
5250

5351
ActivityController<ComponentActivity> componentActivityController =
5452
Robolectric.buildActivity(ComponentActivity.class).setup();
@@ -90,10 +88,11 @@ public void start_whenSuccessful_returnsBrowserSwitchRequest() throws BrowserSwi
9088
.requestCode(123)
9189
.url(browserSwitchDestinationUrl)
9290
.returnUrlScheme("return-url-scheme")
91+
.launchType(LaunchType.ACTIVITY_CLEAR_TOP)
9392
.metadata(metadata);
9493
BrowserSwitchStartResult browserSwitchPendingRequest = sut.start(componentActivity, options);
9594

96-
verify(customTabsInternalClient).launchUrl(componentActivity, browserSwitchDestinationUrl, false);
95+
verify(customTabsInternalClient).launchUrl(componentActivity, browserSwitchDestinationUrl, LaunchType.ACTIVITY_CLEAR_TOP);
9796

9897
assertNotNull(browserSwitchPendingRequest);
9998
assertTrue(browserSwitchPendingRequest instanceof BrowserSwitchStartResult.Started);
@@ -111,7 +110,7 @@ public void start_whenSuccessful_returnsBrowserSwitchRequest() throws BrowserSwi
111110
@Test
112111
public void start_whenNoBrowserAvailable_returnsFailure() {
113112
when(browserSwitchInspector.isDeviceConfiguredForDeepLinking(applicationContext, "return-url-scheme")).thenReturn(true);
114-
doThrow(new ActivityNotFoundException()).when(customTabsInternalClient).launchUrl(any(Context.class), any(Uri.class), eq(false));
113+
doThrow(new ActivityNotFoundException()).when(customTabsInternalClient).launchUrl(any(Context.class), any(Uri.class), eq(null));
115114

116115
BrowserSwitchClient sut = new BrowserSwitchClient(browserSwitchInspector,
117116
customTabsInternalClient);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.braintreepayments.api
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import android.net.Uri
6+
import androidx.browser.customtabs.CustomTabsIntent
7+
import io.mockk.clearAllMocks
8+
import io.mockk.every
9+
import io.mockk.mockk
10+
import org.junit.Assert.assertEquals
11+
import org.junit.Assert.assertTrue
12+
import org.junit.Before
13+
import org.junit.Test
14+
import org.junit.runner.RunWith
15+
import org.robolectric.RobolectricTestRunner
16+
17+
@RunWith(RobolectricTestRunner::class)
18+
class ChromeCustomTabsInternalClientUnitTest {
19+
20+
private lateinit var builder: CustomTabsIntent.Builder
21+
private lateinit var customTabsIntent: CustomTabsIntent
22+
private lateinit var context: Context
23+
private lateinit var url: Uri
24+
25+
@Before
26+
fun setUp() {
27+
clearAllMocks()
28+
builder = mockk(relaxed = true)
29+
context = mockk(relaxed = true)
30+
url = mockk(relaxed = true)
31+
customTabsIntent = CustomTabsIntent.Builder().build()
32+
every { builder.build() } returns customTabsIntent
33+
}
34+
35+
@Test
36+
fun `launchUrl with null LaunchType does not add flags`() {
37+
val client = ChromeCustomTabsInternalClient(builder)
38+
val intent = customTabsIntent.intent
39+
40+
client.launchUrl(context, url, null)
41+
42+
assertEquals(0, intent.flags)
43+
}
44+
45+
@Test
46+
fun `launchUrl with ACTIVITY_NEW_TASK adds new task flag`() {
47+
val client = ChromeCustomTabsInternalClient(builder)
48+
val intent = customTabsIntent.intent
49+
50+
client.launchUrl(context, url, LaunchType.ACTIVITY_NEW_TASK)
51+
52+
assertTrue(intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0)
53+
}
54+
55+
@Test
56+
fun `launchUrl with ACTIVITY_CLEAR_TOP adds clear top flag`() {
57+
val client = ChromeCustomTabsInternalClient(builder)
58+
val intent = customTabsIntent.intent
59+
60+
client.launchUrl(context, url, LaunchType.ACTIVITY_CLEAR_TOP)
61+
62+
assertTrue(intent.flags and Intent.FLAG_ACTIVITY_CLEAR_TOP != 0)
63+
}
64+
}

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ buildscript {
2424
'junit' : 'junit:junit:4.13.2',
2525
'mockitoCore' : 'org.mockito:mockito-core:5.7.0',
2626
'jsonassert' : 'org.skyscreamer:jsonassert:1.5.1',
27-
'robolectric' : 'org.robolectric:robolectric:4.11.1'
27+
'robolectric' : 'org.robolectric:robolectric:4.11.1',
28+
'mockk' : 'io.mockk:mockk:1.13.10'
2829
]
2930

3031
dependencies {

0 commit comments

Comments
 (0)