Skip to content

Commit 3558406

Browse files
committed
Completed implementation of AuthTab Support:
- added AuthTabInternalClient, - removed ChromeCustomTabsInternalClient, - modified ComposeActivity & DemoActivitySingleTop to accept the above changes, - added toast visuals to see whether or not the AuthTab is supported
1 parent e84b429 commit 3558406

File tree

6 files changed

+44
-63
lines changed

6 files changed

+44
-63
lines changed

browser-switch/src/main/java/com/braintreepayments/api/AuthTabInternalClient.kt

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ class AuthTabInternalClient @VisibleForTesting constructor(
1818

1919
constructor() : this(AuthTabIntent.Builder(), CustomTabsIntent.Builder())
2020

21-
/**
22-
* Checks if Auth Tab is supported by the current browser
23-
*/
2421
fun isAuthTabSupported(context: Context): Boolean {
2522
val packageName = CustomTabsClient.getPackageName(context, null)
2623
return when (packageName) {
@@ -38,32 +35,33 @@ class AuthTabInternalClient @VisibleForTesting constructor(
3835
url: Uri,
3936
returnUrlScheme: String?,
4037
appLinkUri: Uri?,
41-
launcher: ActivityResultLauncher<Intent>?,
38+
launcher: ActivityResultLauncher<Intent>,
4239
launchType: LaunchType?
4340
) {
44-
val useAuthTab = launcher != null &&
45-
isAuthTabSupported(context) &&
46-
(returnUrlScheme != null || appLinkUri?.host != null)
41+
val useAuthTab = isAuthTabSupported(context)
4742

4843
if (useAuthTab) {
4944
val authTabIntent = authTabIntentBuilder.build()
5045

5146
if (launchType == LaunchType.ACTIVITY_CLEAR_TOP) {
5247
authTabIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
5348
}
54-
5549
when {
56-
appLinkUri?.host != null -> {
57-
val host = appLinkUri.host!!
58-
val path = appLinkUri.path ?: "/"
59-
authTabIntent.launch(launcher!!, url, host, path)
50+
appLinkUri != null -> {
51+
appLinkUri.host?.let { host ->
52+
val path = appLinkUri.path ?: "/"
53+
authTabIntent.launch(launcher, url, host, path)
54+
}
6055
}
6156
returnUrlScheme != null -> {
62-
authTabIntent.launch(launcher!!, url, returnUrlScheme)
57+
authTabIntent.launch(launcher, url, returnUrlScheme)
58+
}
59+
else -> {
60+
throw IllegalArgumentException("Either returnUrlScheme or appLinkUri must be provided")
6361
}
6462
}
6563
} else {
66-
//fallback to Custom Tabs
64+
//fall back to Custom Tabs
6765
launchCustomTabs(context, url, launchType)
6866
}
6967
}

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

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ public class BrowserSwitchClient {
2424
private final AuthTabInternalClient authTabInternalClient;
2525
private ActivityResultLauncher<Intent> authTabLauncher;
2626
private BrowserSwitchRequest pendingAuthTabRequest;
27-
private AuthTabCallback authTabCallback;
2827

2928
/**
3029
* Construct a client that manages the logic for browser switching.
@@ -46,7 +45,6 @@ public BrowserSwitchClient() {
4645
*/
4746
public void initializeAuthTabLauncher(@NonNull ComponentActivity activity,
4847
@NonNull AuthTabCallback callback) {
49-
this.authTabCallback = callback;
5048
this.authTabLauncher = AuthTabIntent.registerActivityResultLauncher(
5149
activity,
5250
result -> {
@@ -75,10 +73,7 @@ public void initializeAuthTabLauncher(@NonNull ComponentActivity activity,
7573
default:
7674
finalResult = BrowserSwitchFinalResult.NoResult.INSTANCE;
7775
}
78-
79-
if (this.authTabCallback != null) {
80-
this.authTabCallback.onResult(finalResult);
81-
}
76+
callback.onResult(finalResult);
8277
pendingAuthTabRequest = null;
8378
}
8479
);
@@ -126,22 +121,18 @@ public BrowserSwitchStartResult start(@NonNull ComponentActivity activity,
126121
appLinkUri
127122
);
128123

129-
// Check if we should use Auth Tab
130-
boolean useAuthTab = authTabLauncher != null &&
131-
authTabInternalClient.isAuthTabSupported(activity);
124+
boolean useAuthTab = authTabInternalClient.isAuthTabSupported(activity);
132125

133126
if (useAuthTab) {
134-
// Store the pending request for Auth Tab callback
135127
this.pendingAuthTabRequest = request;
136128
}
137129

138-
// Launch using Auth Tab or Custom Tabs
139130
authTabInternalClient.launchUrl(
140131
activity,
141132
browserSwitchUrl,
142133
returnUrlScheme,
143134
appLinkUri,
144-
authTabLauncher, // Will be null if not initialized or not supported
135+
authTabLauncher,
145136
launchType
146137
);
147138

@@ -205,7 +196,7 @@ private boolean isValidRequestCode(int requestCode) {
205196
* @return a {@link BrowserSwitchFinalResult}
206197
*/
207198
public BrowserSwitchFinalResult completeRequest(@NonNull Intent intent, @NonNull String pendingRequest) {
208-
if (intent != null && intent.getData() != null) {
199+
if (intent.getData() != null) {
209200
Uri returnUrl = intent.getData();
210201

211202
try {
@@ -221,9 +212,6 @@ public BrowserSwitchFinalResult completeRequest(@NonNull Intent intent, @NonNull
221212
return BrowserSwitchFinalResult.NoResult.INSTANCE;
222213
}
223214

224-
/**
225-
* Check if Auth Tab is supported on the current device
226-
*/
227215
public boolean isAuthTabSupported(Context context) {
228216
return authTabInternalClient.isAuthTabSupported(context);
229217
}

browser-switch/src/test/java/com/braintreepayments/api/AuthTabInternalClientUnitTest.kt

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -134,26 +134,6 @@ class AuthTabInternalClientUnitTest {
134134
}
135135
}
136136

137-
138-
@Test
139-
fun `launchUrl falls back to Custom Tabs when launcher is null`() {
140-
authTabIntent = mockk(relaxed = true)
141-
every { authTabBuilder.build() } returns authTabIntent
142-
143-
val returnUrlScheme = "example"
144-
145-
val client = AuthTabInternalClient(authTabBuilder, customTabsBuilder)
146-
147-
client.launchUrl(context, url, returnUrlScheme, null, null, null)
148-
149-
verify {
150-
customTabsIntent.launchUrl(context, url)
151-
}
152-
verify(exactly = 0) {
153-
authTabIntent.launch(any(), any(), any<String>())
154-
}
155-
}
156-
157137
@Test
158138
fun `launchUrl falls back to Custom Tabs when Auth Tab not supported`() {
159139
authTabIntent = mockk(relaxed = true)
@@ -199,8 +179,12 @@ class AuthTabInternalClientUnitTest {
199179
fun `launchUrl with null LaunchType does not add flags to Custom Tabs`() {
200180
val client = AuthTabInternalClient(authTabBuilder, customTabsBuilder)
201181
val intent = customTabsIntent.intent
182+
val returnUrlScheme = "example"
202183

203-
client.launchUrl(context, url, null, null, null, null)
184+
// Force AuthTab not to be supported to fall back to Custom Tabs
185+
every { CustomTabsClient.getPackageName(context, null) } returns null
186+
187+
client.launchUrl(context, url, returnUrlScheme, null, launcher, null)
204188

205189
assertEquals(0, intent.flags)
206190
}
@@ -209,8 +193,12 @@ class AuthTabInternalClientUnitTest {
209193
fun `launchUrl with ACTIVITY_NEW_TASK adds new task flag to Custom Tabs`() {
210194
val client = AuthTabInternalClient(authTabBuilder, customTabsBuilder)
211195
val intent = customTabsIntent.intent
196+
val returnUrlScheme = "example"
197+
198+
// Force AuthTab not to be supported to fall back to Custom Tabs
199+
every { CustomTabsClient.getPackageName(context, null) } returns null
212200

213-
client.launchUrl(context, url, null, null, null, LaunchType.ACTIVITY_NEW_TASK)
201+
client.launchUrl(context, url, returnUrlScheme, null, launcher, LaunchType.ACTIVITY_NEW_TASK)
214202

215203
assertTrue(intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0)
216204
}
@@ -219,8 +207,12 @@ class AuthTabInternalClientUnitTest {
219207
fun `launchUrl with ACTIVITY_CLEAR_TOP adds clear top flag to Custom Tabs`() {
220208
val client = AuthTabInternalClient(authTabBuilder, customTabsBuilder)
221209
val intent = customTabsIntent.intent
210+
val returnUrlScheme = "example"
222211

223-
client.launchUrl(context, url, null, null, null, LaunchType.ACTIVITY_CLEAR_TOP)
212+
// Force AuthTab not to be supported to fall back to Custom Tabs
213+
every { CustomTabsClient.getPackageName(context, null) } returns null
214+
215+
client.launchUrl(context, url, returnUrlScheme, null, launcher, LaunchType.ACTIVITY_CLEAR_TOP)
224216

225217
assertTrue(intent.flags and Intent.FLAG_ACTIVITY_CLEAR_TOP != 0)
226218
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,8 +538,11 @@ public void completeRequest_whenIntentIsNull_returnsNoResult() throws BrowserSwi
538538
BrowserSwitchRequest request =
539539
new BrowserSwitchRequest(123, browserSwitchDestinationUrl, requestMetadata, "fake-url-scheme", null);
540540

541+
Intent mockIntent = mock(Intent.class);
542+
when(mockIntent.getData()).thenReturn(null);
543+
541544
BrowserSwitchFinalResult result =
542-
sut.completeRequest(null, request.toBase64EncodedJSON());
545+
sut.completeRequest(mockIntent, request.toBase64EncodedJSON());
543546
assertTrue(result instanceof BrowserSwitchFinalResult.NoResult);
544547
}
545548
}

demo/src/main/java/com/braintreepayments/api/browserswitch/demo/ComposeActivity.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,12 @@ class ComposeActivity : ComponentActivity() {
3535
override fun onCreate(savedInstanceState: Bundle?) {
3636
super.onCreate(savedInstanceState)
3737
browserSwitchClient = BrowserSwitchClient()
38+
browserSwitchClient.initializeAuthTabLauncher(this) { result ->
39+
handleBrowserSwitchResult(result)
40+
}
3841

39-
// Initialize Auth Tab if supported
4042
if (browserSwitchClient.isAuthTabSupported(this)) {
4143
useAuthTab = true
42-
browserSwitchClient.initializeAuthTabLauncher(this) { result ->
43-
handleBrowserSwitchResult(result)
44-
}
4544
}
4645

4746
setContent {
@@ -56,12 +55,13 @@ class ComposeActivity : ComponentActivity() {
5655

5756
override fun onResume() {
5857
super.onResume()
59-
// Only handle Custom Tabs fallback case
58+
// Only handle Custom Tabs fall back case
6059
if (!useAuthTab) {
6160
PendingRequestStore.get(this)?.let { startedRequest ->
6261
val completeRequestResult = browserSwitchClient.completeRequest(intent, startedRequest)
6362
handleBrowserSwitchResult(completeRequestResult)
6463
PendingRequestStore.clear(this)
64+
intent.data = null
6565
}
6666
}
6767
}
@@ -90,7 +90,7 @@ class ComposeActivity : ComponentActivity() {
9090

9191
when (val startResult = browserSwitchClient.start(this, browserSwitchOptions)) {
9292
is BrowserSwitchStartResult.Started -> {
93-
// Only store for Custom Tabs fallback
93+
// Only store for Custom Tabs fall back
9494
if (!useAuthTab) {
9595
PendingRequestStore.put(this, startResult.pendingRequest)
9696
}

demo/src/main/java/com/braintreepayments/api/browserswitch/demo/DemoActivitySingleTop.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,10 @@ public class DemoActivitySingleTop extends AppCompatActivity {
3737
protected void onCreate(@Nullable Bundle savedInstanceState) {
3838
super.onCreate(savedInstanceState);
3939
browserSwitchClient = new BrowserSwitchClient();
40+
browserSwitchClient.initializeAuthTabLauncher(this, this::handleBrowserSwitchResult);
4041

41-
// Initialize Auth Tab if supported
4242
if (browserSwitchClient.isAuthTabSupported(this)) {
4343
useAuthTab = true;
44-
browserSwitchClient.initializeAuthTabLauncher(this, this::handleBrowserSwitchResult);
45-
4644
// Show a toast to indicate Auth Tab is being used
4745
Toast.makeText(this, "Using Auth Tab", Toast.LENGTH_SHORT).show();
4846
} else {
@@ -82,6 +80,7 @@ protected void onNewIntent(Intent intent) {
8280
browserSwitchClient.completeRequest(intent, pendingRequest);
8381
handleBrowserSwitchResult(result);
8482
PendingRequestStore.clear(this);
83+
intent.setData(null);
8584
}
8685
}
8786
}
@@ -97,6 +96,7 @@ protected void onResume() {
9796
Objects.requireNonNull(getDemoFragment())
9897
.onBrowserSwitchError(new Exception("User did not complete browser switch"));
9998
PendingRequestStore.clear(this);
99+
getIntent().setData(null);
100100
}
101101
}
102102
}

0 commit comments

Comments
 (0)