Skip to content

Commit 58ad144

Browse files
authored
fix(aws-android-sdk-cognitoauth): custom tabs app crash when chrome not available (#2529)
* fix custom tabs app crash - add support for launching intent chooser for custom tabs - fix app crash if chrome not available by choosing availalbe browser * use supported browser for signout instead of default Chrome * launch intent chooser by dafault - do not force browsers, app should handle this logic * remove unused imports * call failure callback if no browsers installed on device * call failure callback if custom tabs not supported * cache result of resolved browser activites * return generic collection and fix whitespaces * add exception classes for no browser and no custom tabs
1 parent 0dc8c51 commit 58ad144

File tree

3 files changed

+196
-12
lines changed

3 files changed

+196
-12
lines changed

aws-android-sdk-cognitoauth/src/main/java/com/amazonaws/mobileconnectors/cognitoauth/AuthClient.java

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import android.content.pm.ActivityInfo;
2525
import android.content.pm.PackageInfo;
2626
import android.content.pm.PackageManager;
27+
import android.content.pm.ResolveInfo;
2728
import android.net.Uri;
2829
import android.os.Handler;
2930
import androidx.browser.customtabs.CustomTabsClient;
@@ -39,6 +40,8 @@
3940
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.AuthInvalidGrantException;
4041
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.AuthNavigationException;
4142
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.AuthServiceException;
43+
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.BrowserNotInstalledException;
44+
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.CustomTabsNotSupportedException;
4245
import com.amazonaws.mobileconnectors.cognitoauth.util.AuthHttpResponseParser;
4346
import com.amazonaws.mobileconnectors.cognitoauth.handlers.AuthHandler;
4447
import com.amazonaws.mobileconnectors.cognitoauth.util.ClientConstants;
@@ -48,13 +51,17 @@
4851

4952
import java.net.URL;
5053
import java.security.InvalidParameterException;
54+
import java.util.ArrayList;
55+
import java.util.Collection;
5156
import java.util.HashMap;
5257
import java.util.List;
5358
import java.util.Map;
5459
import java.util.Set;
5560
import java.util.concurrent.CountDownLatch;
5661
import java.util.concurrent.TimeUnit;
5762

63+
import static androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION;
64+
5865
/**
5966
* Local client for {@link Auth}.
6067
* <p>
@@ -132,6 +139,16 @@ public class AuthClient {
132139
*/
133140
private boolean isRedirectActivityDeclared;
134141

142+
/**
143+
* Cache whether browser is installed on the device.
144+
*/
145+
private boolean isBrowserInstalled;
146+
147+
/**
148+
* Cache whether there is browser that supports custom tabs on the device.
149+
*/
150+
private boolean isCustomTabSupported;
151+
135152

136153
// - Chrome Custom Tabs Controls
137154
private CustomTabsClient mCustomTabsClient;
@@ -161,6 +178,8 @@ protected AuthClient(final Context context, final Auth pool, final String userna
161178
this.pool = pool;
162179
this.userId = username;
163180
this.isRedirectActivityDeclared = false;
181+
this.isBrowserInstalled = false;
182+
this.isCustomTabSupported = false;
164183
preWarmChrome();
165184
}
166185

@@ -199,7 +218,7 @@ protected void setUsername(final String username) {
199218
* This must not be null when showSignInIfExpired is true.
200219
*/
201220
protected void getSession(final boolean showSignInIfExpired, final Activity activity) {
202-
getSession(showSignInIfExpired, activity, DEFAULT_BROWSER_PACKAGE);
221+
getSession(showSignInIfExpired, activity, null);
203222
}
204223

205224
/**
@@ -269,7 +288,7 @@ protected String getUsername() {
269288
* </p>
270289
*/
271290
public void signOut() {
272-
signOut(DEFAULT_BROWSER_PACKAGE);
291+
signOut(null);
273292
}
274293

275294
/**
@@ -296,7 +315,7 @@ public void signOut(String browserPackage) {
296315
* but the session may still be alive from the browser.
297316
*/
298317
public void signOut(final boolean clearLocalTokensOnly) {
299-
signOut(clearLocalTokensOnly, DEFAULT_BROWSER_PACKAGE);
318+
signOut(clearLocalTokensOnly, null);
300319
}
301320

302321
/**
@@ -709,21 +728,90 @@ private void launchSignOut(final String redirectUri, final String browserPackage
709728
launchCustomTabs(fqdn, null, browserPackage);
710729
}
711730

731+
/***
732+
* Check if a browser is installed on the device to launch HostedUI.
733+
* @return true if a browser exists else false.
734+
*/
735+
private boolean isBrowserInstalled() {
736+
if (isBrowserInstalled) {
737+
return true;
738+
}
739+
String url = "https://docs.amplify.aws/";
740+
Uri webAddress = Uri.parse(url);
741+
Intent intentWeb = new Intent(Intent.ACTION_VIEW, webAddress);
742+
if (intentWeb.resolveActivity(context.getPackageManager()) != null) {
743+
isBrowserInstalled = true;
744+
return true;
745+
}
746+
return false;
747+
}
748+
749+
/**
750+
* Get list of browser packages that support Custom Tabs Service.
751+
* @return list of package names that support Custom Tabs.
752+
*/
753+
private Collection<String> getSupportedBrowserPackage(){
754+
PackageManager packageManager = context.getPackageManager();
755+
// Get default VIEW intent handler.
756+
Intent activityIntent = new Intent()
757+
.setAction(Intent.ACTION_VIEW)
758+
.addCategory(Intent.CATEGORY_BROWSABLE)
759+
.setData(Uri.fromParts("http", "", null));
760+
761+
// Get all apps that can handle VIEW intents.
762+
List<ResolveInfo> resolvedActivityList = packageManager.queryIntentActivities(activityIntent, 0);
763+
List<String> packageNamesSupportingCustomTabs = new ArrayList<>();
764+
for (ResolveInfo info : resolvedActivityList) {
765+
Intent serviceIntent = new Intent()
766+
.setAction(ACTION_CUSTOM_TABS_CONNECTION)
767+
.setPackage(info.activityInfo.packageName);
768+
// Check if this package also resolves the Custom Tabs service.
769+
if(packageManager.resolveService(serviceIntent, 0) != null) {
770+
packageNamesSupportingCustomTabs.add(info.activityInfo.packageName);
771+
}
772+
}
773+
return packageNamesSupportingCustomTabs;
774+
}
775+
776+
/***
777+
* Check if there are any browsers on the deivce that support custom tabs.
778+
* @return true if custom tabs is supported by any browsers on the device else false.
779+
*/
780+
private boolean isCustomTabSupported() {
781+
if (isCustomTabSupported) {
782+
return true;
783+
}
784+
if (getSupportedBrowserPackage().size() > 0) {
785+
isCustomTabSupported = true;
786+
return true;
787+
}
788+
return false;
789+
}
790+
712791
/**
713792
* Launches the HostedUI webpage on a Custom Tab.
714793
* @param uri Required: {@link Uri}.
715794
* @param activity Activity to launch custom tabs from and which will listen for the intent completion.
716795
* @param browserPackage Optional string specifying the browser package to launch the specified url.
717-
* Defaults to Chrome if null.
796+
* Launches intent chooser if set to null.
718797
*/
719798
private void launchCustomTabs(final Uri uri, final Activity activity, final String browserPackage) {
720799
try {
721-
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(mCustomTabsSession);
722-
mCustomTabsIntent = builder.build();
723-
if(pool.getCustomTabExtras() != null)
724-
mCustomTabsIntent.intent.putExtras(pool.getCustomTabExtras());
725-
mCustomTabsIntent.intent.setPackage(
726-
browserPackage != null ? browserPackage : DEFAULT_BROWSER_PACKAGE);
800+
if(!isBrowserInstalled()) {
801+
userHandler.onFailure(new BrowserNotInstalledException("No browsers installed."));
802+
return;
803+
}
804+
if(!isCustomTabSupported()) {
805+
userHandler.onFailure(new CustomTabsNotSupportedException("Browser with custom tabs support not found."));
806+
return;
807+
}
808+
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(mCustomTabsSession);
809+
mCustomTabsIntent = builder.build();
810+
if(pool.getCustomTabExtras() != null)
811+
mCustomTabsIntent.intent.putExtras(pool.getCustomTabExtras());
812+
if(browserPackage != null) {
813+
mCustomTabsIntent.intent.setPackage(browserPackage);
814+
}
727815
mCustomTabsIntent.intent.setData(uri);
728816
if (activity != null) {
729817
activity.startActivityForResult(
@@ -736,8 +824,8 @@ private void launchCustomTabs(final Uri uri, final Activity activity, final Stri
736824
context.startActivity(startIntent);
737825
}
738826
} catch (final Exception e) {
739-
userHandler.onFailure(e);
740-
}
827+
userHandler.onFailure(e);
828+
}
741829
}
742830

743831
private String getUserContextData() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.amazonaws.mobileconnectors.cognitoauth.exceptions;
19+
20+
/**
21+
* Thrown when no browser packages found on device to launch custom tabs required for sign in/out flow.
22+
*/
23+
public class BrowserNotInstalledException extends RuntimeException {
24+
/**
25+
* Exception unique id.
26+
*/
27+
private static final long serialVersionUID = -3978803758693478385L;
28+
29+
/**
30+
* Creates a new BrowserNotInstalledException with the specified message, and root
31+
* cause.
32+
*
33+
* @param message An error message describing why this exception was thrown.
34+
* @param t The underlying cause of this exception.
35+
*/
36+
public BrowserNotInstalledException(String message, Throwable t) {
37+
super(message, t);
38+
}
39+
40+
/**
41+
* Creates a new BrowserNotInstalledException with the specified message.
42+
*
43+
* @param message An error message describing why this exception was thrown.
44+
*/
45+
public BrowserNotInstalledException(String message) {
46+
super(message);
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.amazonaws.mobileconnectors.cognitoauth.exceptions;
19+
20+
/**
21+
* Thrown when no browser packages on device supports custom tabs required for sign in/out flow.
22+
*/
23+
public class CustomTabsNotSupportedException extends RuntimeException {
24+
/**
25+
* Exception unique id.
26+
*/
27+
private static final long serialVersionUID = -5288976815741183128L;
28+
29+
/**
30+
* Creates a new CustomTabsNotSupportedException with the specified message, and root
31+
* cause.
32+
*
33+
* @param message An error message describing why this exception was thrown.
34+
* @param t The underlying cause of this exception.
35+
*/
36+
public CustomTabsNotSupportedException(String message, Throwable t) {
37+
super(message, t);
38+
}
39+
40+
/**
41+
* Creates a new CustomTabsNotSupportedException with the specified message.
42+
*
43+
* @param message An error message describing why this exception was thrown.
44+
*/
45+
public CustomTabsNotSupportedException(String message) {
46+
super(message);
47+
}
48+
}

0 commit comments

Comments
 (0)