From f1ab326edcdeafc10649caf3e91339552b9e9228 Mon Sep 17 00:00:00 2001 From: Benny Cao Date: Tue, 4 Mar 2025 15:11:23 +1100 Subject: [PATCH 1/3] add withDisabledCustomTabsPackages option and validate when launching auth url --- .../provider/CustomTabsController.java | 2 +- .../android/provider/CustomTabsOptions.java | 35 ++++++++++++-- .../provider/CustomTabsOptionsTest.java | 48 ++++++++++++++++--- 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java b/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java index d60c57e9f..455bb2d69 100644 --- a/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java +++ b/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java @@ -149,7 +149,7 @@ private void launchAsDefault(Context context, Uri uri) { } catch (InterruptedException ignored) { } Log.d(TAG, "Launching URI. Custom Tabs available: " + available); - final Intent intent = customTabsOptions.toIntent(context, session.get()); + final Intent intent = customTabsOptions.toIntent(context, session.get(), this.preferredPackage); intent.setData(uri); context.startActivity(intent); } diff --git a/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java b/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java index 57339ae63..3fdea1522 100644 --- a/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java +++ b/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java @@ -7,18 +7,20 @@ import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; + import androidx.annotation.ColorRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.browser.customtabs.CustomTabColorSchemeParams; import androidx.browser.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsSession; -import androidx.browser.trusted.TrustedWebActivityIntent; import androidx.browser.trusted.TrustedWebActivityIntentBuilder; import androidx.core.content.ContextCompat; import com.auth0.android.authentication.AuthenticationException; +import java.util.List; + /** * Holder for Custom Tabs customization options. Use {@link CustomTabsOptions#newBuilder()} to begin. */ @@ -29,10 +31,14 @@ public class CustomTabsOptions implements Parcelable { private final int toolbarColor; private final BrowserPicker browserPicker; - private CustomTabsOptions(boolean showTitle, @ColorRes int toolbarColor, @NonNull BrowserPicker browserPicker) { + @Nullable + private final List disabledCustomTabsPackages; + + private CustomTabsOptions(boolean showTitle, @ColorRes int toolbarColor, @NonNull BrowserPicker browserPicker, @Nullable List disabledCustomTabsPackages) { this.showTitle = showTitle; this.toolbarColor = toolbarColor; this.browserPicker = browserPicker; + this.disabledCustomTabsPackages = disabledCustomTabsPackages; } @Nullable @@ -44,6 +50,10 @@ boolean hasCompatibleBrowser(@NonNull PackageManager pm) { return getPreferredPackage(pm) != null; } + boolean isDisabledCustomTabBrowser(@NonNull String preferredPackage) { + return disabledCustomTabsPackages != null && disabledCustomTabsPackages.contains(preferredPackage); + } + /** * Create a new CustomTabsOptions.Builder instance. * @@ -56,7 +66,12 @@ public static Builder newBuilder() { @SuppressLint("ResourceType") - Intent toIntent(@NonNull Context context, @Nullable CustomTabsSession session) { + Intent toIntent(@NonNull Context context, @Nullable CustomTabsSession session, @Nullable String preferredPackage) { + + if (preferredPackage != null && this.isDisabledCustomTabBrowser(preferredPackage)) { + return new Intent(Intent.ACTION_VIEW); + } + final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(session) .setShowTitle(showTitle) .setShareState(CustomTabsIntent.SHARE_STATE_OFF); @@ -85,6 +100,7 @@ protected CustomTabsOptions(@NonNull Parcel in) { showTitle = in.readByte() != 0; toolbarColor = in.readInt(); browserPicker = in.readParcelable(BrowserPicker.class.getClassLoader()); + disabledCustomTabsPackages = in.createStringArrayList(); } @Override @@ -92,6 +108,7 @@ public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeByte((byte) (showTitle ? 1 : 0)); dest.writeInt(toolbarColor); dest.writeParcelable(browserPicker, flags); + dest.writeStringList(disabledCustomTabsPackages); } @Override @@ -120,10 +137,14 @@ public static class Builder { @NonNull private BrowserPicker browserPicker; + @Nullable + private List disabledCustomTabsPackages; + Builder() { this.showTitle = false; this.toolbarColor = 0; this.browserPicker = BrowserPicker.newBuilder().build(); + this.disabledCustomTabsPackages = null; } /** @@ -171,6 +192,12 @@ public Builder withBrowserPicker(@NonNull BrowserPicker browserPicker) { return this; } + @NonNull + public Builder withDisabledCustomTabsPackages(List disabledCustomTabsPackages) { + this.disabledCustomTabsPackages = disabledCustomTabsPackages; + return this; + } + /** * Create a new CustomTabsOptions instance with the customization settings. * @@ -178,7 +205,7 @@ public Builder withBrowserPicker(@NonNull BrowserPicker browserPicker) { */ @NonNull public CustomTabsOptions build() { - return new CustomTabsOptions(showTitle, toolbarColor, browserPicker); + return new CustomTabsOptions(showTitle, toolbarColor, browserPicker, disabledCustomTabsPackages); } } diff --git a/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java b/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java index 2537f3e27..1f8804a88 100644 --- a/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java +++ b/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java @@ -14,12 +14,16 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -74,7 +78,7 @@ public void shouldHaveDefaultValues() { CustomTabsOptions options = CustomTabsOptions.newBuilder().build(); assertThat(options, is(notNullValue())); - Intent intent = options.toIntent(context, null); + Intent intent = options.toIntent(context, null, null); assertThat(intent, is(notNullValue())); assertThat(intent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(false)); @@ -89,7 +93,7 @@ public void shouldHaveDefaultValues() { CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); assertThat(parceledOptions, is(notNullValue())); - Intent parceledIntent = parceledOptions.toIntent(context, null); + Intent parceledIntent = parceledOptions.toIntent(context, null, null); assertThat(parceledIntent, is(notNullValue())); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(false)); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), is(true)); @@ -105,7 +109,7 @@ public void shouldSetShowTitle() { .build(); assertThat(options, is(notNullValue())); - Intent intent = options.toIntent(context, null); + Intent intent = options.toIntent(context, null, null); assertThat(intent, is(notNullValue())); assertThat(intent.hasExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), is(true)); @@ -118,7 +122,7 @@ public void shouldSetShowTitle() { CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); assertThat(parceledOptions, is(notNullValue())); - Intent parceledIntent = parceledOptions.toIntent(context, null); + Intent parceledIntent = parceledOptions.toIntent(context, null, null); assertThat(parceledIntent, is(notNullValue())); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), is(true)); assertThat(parceledIntent.getIntExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE, CustomTabsIntent.NO_TITLE), is(CustomTabsIntent.SHOW_PAGE_TITLE)); @@ -131,7 +135,7 @@ public void shouldSetToolbarColor() { .build(); assertThat(options, is(notNullValue())); - Intent intent = options.toIntent(context, null); + Intent intent = options.toIntent(context, null, null); assertThat(intent, is(notNullValue())); assertThat(intent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(true)); @@ -145,7 +149,7 @@ public void shouldSetToolbarColor() { CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); assertThat(parceledOptions, is(notNullValue())); - Intent parceledIntent = parceledOptions.toIntent(context, null); + Intent parceledIntent = parceledOptions.toIntent(context, null, null); assertThat(parceledIntent, is(notNullValue())); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(true)); assertThat(parceledIntent.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0), is(resolvedColor)); @@ -174,4 +178,36 @@ public void shouldSetBrowserPicker() { String preferredPackageNow = parceledOptions.getPreferredPackage(activity.getPackageManager()); assertThat(preferredPackageNow, is("com.auth0.browser")); } + + @Test + public void shouldSetDisabledCustomTabPackages() { + CustomTabsOptions options = CustomTabsOptions.newBuilder() + .withDisabledCustomTabsPackages(List.of("com.auth0.browser")) + .withToolbarColor(android.R.color.black) + .build(); + assertThat(options, is(notNullValue())); + + Intent intentNoExtras = options.toIntent(context, null, "com.auth0.browser"); + + assertThat(intentNoExtras, is(notNullValue())); + assertThat(intentNoExtras.getExtras(), is(nullValue())); + assertEquals(intentNoExtras.getAction(), "android.intent.action.VIEW"); + + Intent intentWithToolbarExtra = options.toIntent(context, null, "com.another.browser"); + assertThat(intentWithToolbarExtra, is(notNullValue())); + assertThat(intentWithToolbarExtra.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(true)); + int resolvedColor = ContextCompat.getColor(context, android.R.color.black); + assertThat(intentWithToolbarExtra.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0), is(resolvedColor)); + + Parcel parcel = Parcel.obtain(); + options.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); + assertThat(parceledOptions, is(notNullValue())); + + Intent parceledIntent = parceledOptions.toIntent(context, null, "com.auth0.browser"); + assertThat(parceledIntent, is(notNullValue())); + assertThat(parceledIntent.getExtras(), is(nullValue())); + assertEquals(parceledIntent.getAction(), "android.intent.action.VIEW"); + } } \ No newline at end of file From 288c2c90e2074942726d08521c3da7999921063c Mon Sep 17 00:00:00 2001 From: Benny Cao Date: Tue, 4 Mar 2025 15:44:22 +1100 Subject: [PATCH 2/3] don't pass preferredPackage but use existing fun --- .../provider/CustomTabsController.java | 2 +- .../android/provider/CustomTabsOptions.java | 3 +- .../provider/CustomTabsOptionsTest.java | 42 ++++++++++++------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java b/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java index 455bb2d69..d60c57e9f 100644 --- a/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java +++ b/auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java @@ -149,7 +149,7 @@ private void launchAsDefault(Context context, Uri uri) { } catch (InterruptedException ignored) { } Log.d(TAG, "Launching URI. Custom Tabs available: " + available); - final Intent intent = customTabsOptions.toIntent(context, session.get(), this.preferredPackage); + final Intent intent = customTabsOptions.toIntent(context, session.get()); intent.setData(uri); context.startActivity(intent); } diff --git a/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java b/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java index 3fdea1522..81fd98e0e 100644 --- a/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java +++ b/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java @@ -66,7 +66,8 @@ public static Builder newBuilder() { @SuppressLint("ResourceType") - Intent toIntent(@NonNull Context context, @Nullable CustomTabsSession session, @Nullable String preferredPackage) { + Intent toIntent(@NonNull Context context, @Nullable CustomTabsSession session) { + String preferredPackage = this.getPreferredPackage(context.getPackageManager()); if (preferredPackage != null && this.isDisabledCustomTabBrowser(preferredPackage)) { return new Intent(Intent.ACTION_VIEW); diff --git a/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java b/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java index 1f8804a88..c4f03efb4 100644 --- a/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java +++ b/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java @@ -78,7 +78,7 @@ public void shouldHaveDefaultValues() { CustomTabsOptions options = CustomTabsOptions.newBuilder().build(); assertThat(options, is(notNullValue())); - Intent intent = options.toIntent(context, null, null); + Intent intent = options.toIntent(context, null); assertThat(intent, is(notNullValue())); assertThat(intent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(false)); @@ -93,7 +93,7 @@ public void shouldHaveDefaultValues() { CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); assertThat(parceledOptions, is(notNullValue())); - Intent parceledIntent = parceledOptions.toIntent(context, null, null); + Intent parceledIntent = parceledOptions.toIntent(context, null); assertThat(parceledIntent, is(notNullValue())); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(false)); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), is(true)); @@ -109,7 +109,7 @@ public void shouldSetShowTitle() { .build(); assertThat(options, is(notNullValue())); - Intent intent = options.toIntent(context, null, null); + Intent intent = options.toIntent(context, null); assertThat(intent, is(notNullValue())); assertThat(intent.hasExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), is(true)); @@ -122,7 +122,7 @@ public void shouldSetShowTitle() { CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); assertThat(parceledOptions, is(notNullValue())); - Intent parceledIntent = parceledOptions.toIntent(context, null, null); + Intent parceledIntent = parceledOptions.toIntent(context, null); assertThat(parceledIntent, is(notNullValue())); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), is(true)); assertThat(parceledIntent.getIntExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE, CustomTabsIntent.NO_TITLE), is(CustomTabsIntent.SHOW_PAGE_TITLE)); @@ -135,7 +135,7 @@ public void shouldSetToolbarColor() { .build(); assertThat(options, is(notNullValue())); - Intent intent = options.toIntent(context, null, null); + Intent intent = options.toIntent(context, null); assertThat(intent, is(notNullValue())); assertThat(intent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(true)); @@ -149,7 +149,7 @@ public void shouldSetToolbarColor() { CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); assertThat(parceledOptions, is(notNullValue())); - Intent parceledIntent = parceledOptions.toIntent(context, null, null); + Intent parceledIntent = parceledOptions.toIntent(context, null); assertThat(parceledIntent, is(notNullValue())); assertThat(parceledIntent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(true)); assertThat(parceledIntent.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0), is(resolvedColor)); @@ -181,33 +181,47 @@ public void shouldSetBrowserPicker() { @Test public void shouldSetDisabledCustomTabPackages() { + Activity activity = spy(Robolectric.setupActivity(Activity.class)); + BrowserPickerTest.setupBrowserContext(activity, Collections.singletonList("com.auth0.browser"), null, null); + BrowserPicker browserPicker = BrowserPicker.newBuilder().build(); + CustomTabsOptions options = CustomTabsOptions.newBuilder() + .withBrowserPicker(browserPicker) .withDisabledCustomTabsPackages(List.of("com.auth0.browser")) .withToolbarColor(android.R.color.black) .build(); assertThat(options, is(notNullValue())); - Intent intentNoExtras = options.toIntent(context, null, "com.auth0.browser"); + Intent intentNoExtras = options.toIntent(activity, null); assertThat(intentNoExtras, is(notNullValue())); assertThat(intentNoExtras.getExtras(), is(nullValue())); assertEquals(intentNoExtras.getAction(), "android.intent.action.VIEW"); - Intent intentWithToolbarExtra = options.toIntent(context, null, "com.another.browser"); - assertThat(intentWithToolbarExtra, is(notNullValue())); - assertThat(intentWithToolbarExtra.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(true)); - int resolvedColor = ContextCompat.getColor(context, android.R.color.black); - assertThat(intentWithToolbarExtra.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0), is(resolvedColor)); - Parcel parcel = Parcel.obtain(); options.writeToParcel(parcel, 0); parcel.setDataPosition(0); CustomTabsOptions parceledOptions = CustomTabsOptions.CREATOR.createFromParcel(parcel); assertThat(parceledOptions, is(notNullValue())); - Intent parceledIntent = parceledOptions.toIntent(context, null, "com.auth0.browser"); + Intent parceledIntent = parceledOptions.toIntent(activity, null); assertThat(parceledIntent, is(notNullValue())); assertThat(parceledIntent.getExtras(), is(nullValue())); assertEquals(parceledIntent.getAction(), "android.intent.action.VIEW"); + + BrowserPickerTest.setupBrowserContext(activity, Collections.singletonList("com.another.browser"), null, null); + BrowserPicker browserPicker2 = BrowserPicker.newBuilder().build(); + + CustomTabsOptions options2 = CustomTabsOptions.newBuilder() + .withBrowserPicker(browserPicker2) + .withDisabledCustomTabsPackages(List.of("com.auth0.browser")) + .withToolbarColor(android.R.color.black) + .build(); + + Intent intentWithToolbarExtra = options2.toIntent(activity, null); + assertThat(intentWithToolbarExtra, is(notNullValue())); + assertThat(intentWithToolbarExtra.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR), is(true)); + int resolvedColor = ContextCompat.getColor(activity, android.R.color.black); + assertThat(intentWithToolbarExtra.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0), is(resolvedColor)); } } \ No newline at end of file From 377a16d10365aae7bf0a9e00efed074144720bd3 Mon Sep 17 00:00:00 2001 From: Benny Cao Date: Thu, 6 Mar 2025 18:42:24 +1100 Subject: [PATCH 3/3] add comments --- .../auth0/android/provider/CustomTabsOptions.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java b/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java index 81fd98e0e..765eed204 100644 --- a/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java +++ b/auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java @@ -50,6 +50,12 @@ boolean hasCompatibleBrowser(@NonNull PackageManager pm) { return getPreferredPackage(pm) != null; } + /** + * Returns whether the browser preferred package has custom tab disabled or not. + * + * @param preferredPackage the preferred browser package name. + * @return whether the browser preferred package has custom tab disabled or not. + */ boolean isDisabledCustomTabBrowser(@NonNull String preferredPackage) { return disabledCustomTabsPackages != null && disabledCustomTabsPackages.contains(preferredPackage); } @@ -193,6 +199,13 @@ public Builder withBrowserPicker(@NonNull BrowserPicker browserPicker) { return this; } + /** + * Define a list of browser packages that disables the launching of authentication on custom tabs. + * The authentication url will launch on the preferred package external browser. + * + * @param disabledCustomTabsPackages list of browser packages. + * @return the current builder instance + */ @NonNull public Builder withDisabledCustomTabsPackages(List disabledCustomTabsPackages) { this.disabledCustomTabsPackages = disabledCustomTabsPackages;