2424import android .content .pm .ActivityInfo ;
2525import android .content .pm .PackageInfo ;
2626import android .content .pm .PackageManager ;
27+ import android .content .pm .ResolveInfo ;
2728import android .net .Uri ;
2829import android .os .Handler ;
2930import androidx .browser .customtabs .CustomTabsClient ;
3940import com .amazonaws .mobileconnectors .cognitoauth .exceptions .AuthInvalidGrantException ;
4041import com .amazonaws .mobileconnectors .cognitoauth .exceptions .AuthNavigationException ;
4142import com .amazonaws .mobileconnectors .cognitoauth .exceptions .AuthServiceException ;
43+ import com .amazonaws .mobileconnectors .cognitoauth .exceptions .BrowserNotInstalledException ;
44+ import com .amazonaws .mobileconnectors .cognitoauth .exceptions .CustomTabsNotSupportedException ;
4245import com .amazonaws .mobileconnectors .cognitoauth .util .AuthHttpResponseParser ;
4346import com .amazonaws .mobileconnectors .cognitoauth .handlers .AuthHandler ;
4447import com .amazonaws .mobileconnectors .cognitoauth .util .ClientConstants ;
4851
4952import java .net .URL ;
5053import java .security .InvalidParameterException ;
54+ import java .util .ArrayList ;
55+ import java .util .Collection ;
5156import java .util .HashMap ;
5257import java .util .List ;
5358import java .util .Map ;
5459import java .util .Set ;
5560import java .util .concurrent .CountDownLatch ;
5661import 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 () {
0 commit comments