// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.customtabs;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_HEIGHT_FIXED;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_POSITION_DEFAULT;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_POSITION_END;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_POSITION_START;
import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_SIDE;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextThemeWrapper;
import androidx.browser.customtabs.CustomTabColorSchemeParams;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsSession;
import androidx.browser.trusted.ScreenOrientation;
import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.chromium.base.BuildInfo;
import org.chromium.base.IntentUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.BackgroundInteractBehavior;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.ui.google_bottom_bar.proto.IntentParams.GoogleBottomBarIntentParams;
import org.chromium.chrome.browser.ui.google_bottom_bar.proto.IntentParams.GoogleBottomBarIntentParams.VariantLayoutType;
import org.chromium.device.mojom.ScreenOrientationLockType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** Tests for {@link CustomTabIntentDataProvider}. */
@RunWith(BaseRobolectricTestRunner.class)
@Batch(Batch.UNIT_TESTS)
@Config(manifest = Config.NONE)
public class CustomTabIntentDataProviderTest {
private static final String BUTTON_DESCRIPTION = "buttonDescription";
private Context mContext;
@Before
public void setUp() {
mContext =
new ContextThemeWrapper(
ApplicationProvider.getApplicationContext(),
R.style.Theme_BrowserUI_DayNight);
}
@Test
public void colorSchemeParametersAreRetrieved() {
CustomTabColorSchemeParams lightParams =
new CustomTabColorSchemeParams.Builder()
.setToolbarColor(0xff0000ff)
.setSecondaryToolbarColor(0xff00aaff)
.setNavigationBarColor(0xff112233)
.build();
CustomTabColorSchemeParams darkParams =
new CustomTabColorSchemeParams.Builder()
.setToolbarColor(0xffff0000)
.setSecondaryToolbarColor(0xffff8800)
.setNavigationBarColor(0xff332211)
.build();
Intent intent =
new CustomTabsIntent.Builder()
.setColorSchemeParams(COLOR_SCHEME_LIGHT, lightParams)
.setColorSchemeParams(COLOR_SCHEME_DARK, darkParams)
.build()
.intent;
ColorProvider lightProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT)
.getColorProvider();
ColorProvider darkProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_DARK)
.getColorProvider();
assertEquals((int) lightParams.toolbarColor, lightProvider.getToolbarColor());
assertEquals((int) darkParams.toolbarColor, darkProvider.getToolbarColor());
assertEquals((int) lightParams.secondaryToolbarColor, lightProvider.getBottomBarColor());
assertEquals((int) darkParams.secondaryToolbarColor, darkProvider.getBottomBarColor());
assertEquals(lightParams.navigationBarColor, lightProvider.getNavigationBarColor());
assertEquals(darkParams.navigationBarColor, darkProvider.getNavigationBarColor());
}
/* Test the setting the default orientation for Trusted Web Activity and getting the default
* orientation.
*/
@Test
public void defaultOrientationIsSet() {
CustomTabsSession mSession =
CustomTabsSession.createMockSessionForTesting(
new ComponentName(mContext, ChromeLauncherActivity.class));
TrustedWebActivityIntentBuilder twaBuilder =
new TrustedWebActivityIntentBuilder(getLaunchingUrl())
.setScreenOrientation(ScreenOrientation.LANDSCAPE);
Intent intent = twaBuilder.build(mSession).getIntent();
CustomTabIntentDataProvider customTabIntentDataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
ScreenOrientationLockType.LANDSCAPE,
customTabIntentDataProvider.getDefaultOrientation());
twaBuilder =
new TrustedWebActivityIntentBuilder(getLaunchingUrl())
.setScreenOrientation(ScreenOrientation.PORTRAIT);
intent = twaBuilder.build(mSession).getIntent();
customTabIntentDataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
ScreenOrientationLockType.PORTRAIT,
customTabIntentDataProvider.getDefaultOrientation());
}
@Test
public void shareStateDefault_noButtonInToolbar_hasShareInToolbar() {
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE,
CustomTabsIntent.SHARE_STATE_DEFAULT);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
mContext.getResources().getString(R.string.share),
dataProvider.getCustomButtonsOnToolbar().get(0).getDescription());
}
@Test
public void shareStateDefault_buttonInToolbar_hasShareItemInMenu() {
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE,
CustomTabsIntent.SHARE_STATE_DEFAULT)
.putExtra(
CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE,
createActionButtonInToolbarBundle());
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
BUTTON_DESCRIPTION,
dataProvider.getCustomButtonsOnToolbar().get(0).getDescription());
assertTrue(dataProvider.shouldShowShareMenuItem());
}
@Test
public void shareStateDefault_buttonInToolbarAndCustomMenuItems_hasNoShare() {
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE,
CustomTabsIntent.SHARE_STATE_DEFAULT)
.putExtra(
CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE,
createActionButtonInToolbarBundle())
.putExtra(
CustomTabsIntent.EXTRA_MENU_ITEMS,
new ArrayList<>(Collections.singletonList(createMenuItemBundle())));
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
BUTTON_DESCRIPTION,
dataProvider.getCustomButtonsOnToolbar().get(0).getDescription());
assertFalse(dataProvider.shouldShowShareMenuItem());
}
@Test
public void shareStateOn_buttonInToolbar_hasShareItemInMenu() {
ArrayList<Bundle> buttons =
new ArrayList<>(Collections.singleton(createActionButtonInToolbarBundle()));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
BUTTON_DESCRIPTION,
dataProvider.getCustomButtonsOnToolbar().get(0).getDescription());
assertTrue(dataProvider.shouldShowShareMenuItem());
}
@Test
public void googleBottomBarFlagsOff_customButtonWithSupportedId_hasItemsInToolbar() {
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100),
createCustomActionButtonBundleWithId(1)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(0, dataProvider.getCustomButtonsOnGoogleBottomBar().size());
assertEquals(2, dataProvider.getCustomButtonsOnToolbar().size());
}
@Test
@EnableFeatures({ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR})
public void googleBottomBarFlagsOn_customButtonWithSupportedId_hasItemInGoogleBottomBar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100),
createCustomActionButtonBundleWithId(1)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(1, dataProvider.getCustomButtonsOnGoogleBottomBar().size());
assertEquals(1, dataProvider.getCustomButtonsOnToolbar().size());
}
@Test
@EnableFeatures({ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR})
public void googleBottomBarFlagsOn_customButtonWithNonSupportedId_hasItemsInToolbar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(1),
createCustomActionButtonBundleWithId(2)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(0, dataProvider.getCustomButtonsOnGoogleBottomBar().size());
assertEquals(2, dataProvider.getCustomButtonsOnToolbar().size());
}
@Test
@EnableFeatures({ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR})
public void
googleBottomBarFlagsOn_hasExtraGoogleBottomBarButtons_hasSupportedItemsInGoogleBottomBar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
when(connection.hasExtraGoogleBottomBarButtons(any())).thenReturn(true);
ArrayList<Bundle> googleBottomBarButtons =
new ArrayList<>(
Arrays.asList(
createCustomGoogleBottomBarItemBundleWithId(103),
createCustomGoogleBottomBarItemBundleWithId(2)));
when(connection.getGoogleBottomBarButtons(any())).thenReturn(googleBottomBarButtons);
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100),
createCustomActionButtonBundleWithId(1)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(2, dataProvider.getCustomButtonsOnGoogleBottomBar().size());
assertEquals(0, dataProvider.getCustomButtonsOnBottombar().size());
assertEquals(1, dataProvider.getCustomButtonsOnToolbar().size());
assertEquals(3, dataProvider.getAllCustomButtons().size());
}
@Test
@EnableFeatures({
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR,
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR_VARIANT_LAYOUTS
})
public void googleBottomBarFlagsOn_withNoVariantLayout_hasItemsInGoogleBottomBar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
when(connection.hasExtraGoogleBottomBarButtons(any())).thenReturn(true);
ArrayList<Bundle> googleBottomBarButtons =
new ArrayList<>(
Arrays.asList(
createCustomGoogleBottomBarItemBundleWithId(105), // CUSTOM
createCustomGoogleBottomBarItemBundleWithId(2)) // UNSUPPORTED
);
when(connection.getGoogleBottomBarButtons(any())).thenReturn(googleBottomBarButtons);
when(connection.getGoogleBottomBarIntentParams(any()))
.thenReturn(
GoogleBottomBarIntentParams.newBuilder()
.setVariantLayoutType(VariantLayoutType.NO_VARIANT)
.addAllEncodedButton(
// PIH_BASIC, CUSTOM, SEARCH Not checked for this layout
// type
List.of(0, 1, 8, 9))
.build());
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100), // SAVE
createCustomActionButtonBundleWithId(101), // SHARE
createCustomActionButtonBundleWithId(1)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
3, dataProvider.getCustomButtonsOnGoogleBottomBar().size()); // SAVE, SHARE, CUSTOM
assertEquals(0, dataProvider.getCustomButtonsOnBottombar().size());
assertEquals(1, dataProvider.getCustomButtonsOnToolbar().size()); // 1
assertEquals(4, dataProvider.getAllCustomButtons().size());
}
@Test
@EnableFeatures({
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR,
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR_VARIANT_LAYOUTS
})
public void googleBottomBarFlagsOn_withDoubleDeckerLayout_hasItemsInGoogleBottomBar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
when(connection.hasExtraGoogleBottomBarButtons(any())).thenReturn(true);
ArrayList<Bundle> googleBottomBarButtons =
new ArrayList<>(
Arrays.asList(
createCustomGoogleBottomBarItemBundleWithId(105), // CUSTOM
createCustomGoogleBottomBarItemBundleWithId(2)) // UNSUPPORTED
);
when(connection.getGoogleBottomBarButtons(any())).thenReturn(googleBottomBarButtons);
when(connection.getGoogleBottomBarIntentParams(any()))
.thenReturn(
GoogleBottomBarIntentParams.newBuilder()
.setVariantLayoutType(VariantLayoutType.DOUBLE_DECKER)
.addAllEncodedButton(
// PIH_BASIC, CUSTOM, SEARCH Not checked for this layout
// type
List.of(0, 1, 8, 9))
.build());
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100), // SAVE
createCustomActionButtonBundleWithId(101), // SHARE
createCustomActionButtonBundleWithId(1)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
3, dataProvider.getCustomButtonsOnGoogleBottomBar().size()); // SAVE, SHARE, CUSTOM
assertEquals(0, dataProvider.getCustomButtonsOnBottombar().size());
assertEquals(1, dataProvider.getCustomButtonsOnToolbar().size()); // 1
assertEquals(4, dataProvider.getAllCustomButtons().size());
}
@Test
@EnableFeatures({
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR,
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR_VARIANT_LAYOUTS
})
public void googleBottomBarFlagsOn_withSingleDeckerLayout_hasItemsInToolbar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
when(connection.hasExtraGoogleBottomBarButtons(any())).thenReturn(true);
ArrayList<Bundle> googleBottomBarButtons =
new ArrayList<>(
Arrays.asList(
createCustomGoogleBottomBarItemBundleWithId(105), // CUSTOM
createCustomGoogleBottomBarItemBundleWithId(2)) // UNSUPPORTED
);
when(connection.getGoogleBottomBarButtons(any())).thenReturn(googleBottomBarButtons);
when(connection.getGoogleBottomBarIntentParams(any()))
.thenReturn(
GoogleBottomBarIntentParams.newBuilder()
.setVariantLayoutType(VariantLayoutType.SINGLE_DECKER)
.addAllEncodedButton(List.of())
.build());
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100), // SAVE
createCustomActionButtonBundleWithId(101))); // SHARE
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(1, dataProvider.getCustomButtonsOnGoogleBottomBar().size()); // CUSTOM
assertEquals(0, dataProvider.getCustomButtonsOnBottombar().size());
assertEquals(2, dataProvider.getCustomButtonsOnToolbar().size()); // SAVE, SHARE
assertEquals(3, dataProvider.getAllCustomButtons().size());
}
@Test
@EnableFeatures({
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR,
ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR_VARIANT_LAYOUTS
})
public void googleBottomBarFlagsOn_withSingleDeckerWithRightButtonsLayout_hasItemsInToolbar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
when(connection.hasExtraGoogleBottomBarButtons(any())).thenReturn(true);
ArrayList<Bundle> googleBottomBarButtons =
new ArrayList<>(
Arrays.asList(
createCustomGoogleBottomBarItemBundleWithId(105), // CUSTOM
createCustomGoogleBottomBarItemBundleWithId(2)) // UNSUPPORTED
);
when(connection.getGoogleBottomBarButtons(any())).thenReturn(googleBottomBarButtons);
when(connection.getGoogleBottomBarIntentParams(any()))
.thenReturn(
GoogleBottomBarIntentParams.newBuilder()
.setVariantLayoutType(
VariantLayoutType.SINGLE_DECKER_WITH_RIGHT_BUTTONS)
.addAllEncodedButton(List.of(0, 2)) // SHARE
.build());
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100), // SAVE
createCustomActionButtonBundleWithId(101), // SHARE
createCustomActionButtonBundleWithId(1)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(2, dataProvider.getCustomButtonsOnGoogleBottomBar().size()); // SHARE, CUSTOM
assertEquals(0, dataProvider.getCustomButtonsOnBottombar().size());
assertEquals(2, dataProvider.getCustomButtonsOnToolbar().size()); // 1, SAVE
assertEquals(4, dataProvider.getAllCustomButtons().size());
}
@Test
@DisableFeatures({ChromeFeatureList.CCT_GOOGLE_BOTTOM_BAR})
public void googleBottomBarFlagsOff_hasExtraGoogleBottomBarButtons_hasItemsInToolbar() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.shouldEnableGoogleBottomBarForIntent(any())).thenReturn(true);
when(connection.hasExtraGoogleBottomBarButtons(any())).thenReturn(true);
ArrayList<Bundle> googleBottomBarButtons =
new ArrayList<>(
Arrays.asList(
createCustomGoogleBottomBarItemBundleWithId(103),
createCustomGoogleBottomBarItemBundleWithId(2)));
when(connection.getGoogleBottomBarButtons(any())).thenReturn(googleBottomBarButtons);
CustomTabsConnection.setInstanceForTesting(connection);
ArrayList<Bundle> buttons =
new ArrayList<>(
Arrays.asList(
createCustomActionButtonBundleWithId(100),
createCustomActionButtonBundleWithId(1)));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(0, dataProvider.getCustomButtonsOnGoogleBottomBar().size());
assertEquals(0, dataProvider.getCustomButtonsOnBottombar().size());
assertEquals(2, dataProvider.getCustomButtonsOnToolbar().size());
assertEquals(2, dataProvider.getAllCustomButtons().size());
}
@Test
public void shareStateOn_buttonInToolbarAndCustomMenuItems_hasShareItemInMenu() {
ArrayList<Bundle> buttons =
new ArrayList<>(Collections.singleton(createActionButtonInToolbarBundle()));
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON)
.putExtra(CustomTabsIntent.EXTRA_TOOLBAR_ITEMS, buttons)
.putExtra(
CustomTabsIntent.EXTRA_MENU_ITEMS,
new ArrayList<>(Collections.singletonList(createMenuItemBundle())));
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
BUTTON_DESCRIPTION,
dataProvider.getCustomButtonsOnToolbar().get(0).getDescription());
assertTrue(dataProvider.shouldShowShareMenuItem());
}
@Test
public void shareStateOff_noShareItems() {
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_SHARE_STATE,
CustomTabsIntent.SHARE_STATE_OFF);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertTrue(dataProvider.getCustomButtonsOnToolbar().isEmpty());
assertFalse(dataProvider.shouldShowShareMenuItem());
}
@Test
@EnableFeatures(ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES)
public void isAllowedThirdParty_noDefaultPolicy() {
CustomTabIntentDataProvider.DENYLIST_ENTRIES.setForTesting(
"com.dc.joker|com.marvel.thanos");
// If no default-policy is present, it defaults to use-denylist.
assertFalse(
"Entry in denylist should be rejected",
CustomTabIntentDataProvider.isAllowedThirdParty("com.dc.joker"));
assertFalse(
"Entry in denylist should be rejected",
CustomTabIntentDataProvider.isAllowedThirdParty("com.marvel.thanos"));
assertTrue(
"Entry NOT in denylist should be accepted",
CustomTabIntentDataProvider.isAllowedThirdParty("com.dc.batman"));
}
@Test
@EnableFeatures({ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES + "<Study"})
@CommandLineFlags.Add({
"force-fieldtrials=Study/Group",
"force-fieldtrial-params=Study.Group:"
+ "default_policy/use-denylist"
+ "/denylist_entries/com.dc.joker|com.marvel.thanos"
})
public void isAllowedThirdParty_denylist() {
CustomTabIntentDataProvider.THIRD_PARTIES_DEFAULT_POLICY.setForTesting("use-denylist");
CustomTabIntentDataProvider.DENYLIST_ENTRIES.setForTesting(
"com.dc.joker|com.marvel.thanos");
assertFalse(
"Entry in denylist should be rejected",
CustomTabIntentDataProvider.isAllowedThirdParty("com.dc.joker"));
assertFalse(
"Entry in denylist should be rejected",
CustomTabIntentDataProvider.isAllowedThirdParty("com.marvel.thanos"));
assertTrue(
"Entry NOT in denylist should be accepted",
CustomTabIntentDataProvider.isAllowedThirdParty("com.dc.batman"));
}
@Test
@EnableFeatures({ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES + "<Study"})
@CommandLineFlags.Add({
"force-fieldtrials=Study/Group",
"force-fieldtrial-params=Study.Group:"
+ "default_policy/use-allowlist"
+ "/allowlist_entries/com.pixar.woody|com.disney.ariel"
})
public void isAllowedThirdParty_allowlist() {
CustomTabIntentDataProvider.THIRD_PARTIES_DEFAULT_POLICY.setForTesting("use-allowlist");
CustomTabIntentDataProvider.ALLOWLIST_ENTRIES.setForTesting(
"com.pixar.woody|com.disney.ariel");
assertTrue(
"Entry in allowlist should be accepted",
CustomTabIntentDataProvider.isAllowedThirdParty("com.pixar.woody"));
assertTrue(
"Entry in allowlist should be accepted",
CustomTabIntentDataProvider.isAllowedThirdParty("com.disney.ariel"));
assertFalse(
"Entry NOT in allowlist should be rejected",
CustomTabIntentDataProvider.isAllowedThirdParty("com.pixar.syndrome"));
}
@Test
public void testActivityBreakPoint_Default() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("Break points do not match", 840, dataProvider.getActivityBreakPoint());
}
@Test
public void testActivityBreakPoint_Custom() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP, 300);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("Break points do not match", 300, dataProvider.getActivityBreakPoint());
}
@Test
public void testActivityBreakPoint_Negative() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP, -500);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("Break points do not match", 840, dataProvider.getActivityBreakPoint());
}
@Test
public void testInitialActivityHeight_1stParty() {
var intent = new Intent().putExtra(CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_HEIGHT_PX, 50);
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.isFirstParty(any())).thenReturn(true);
CustomTabsConnection.setInstanceForTesting(connection);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(50, dataProvider.getInitialActivityHeight());
}
@Test
public void testInitialActivityWidth_3P_notdenied() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_WIDTH_PX, 50);
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.pixar.woody");
CustomTabsConnection.setInstanceForTesting(connection);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("Width should be 50", 50, dataProvider.getInitialActivityWidth());
}
@Test
public void testInitialActivityWidth_3P_denied() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_WIDTH_PX, 50);
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.dc.joker");
CustomTabsConnection.setInstanceForTesting(connection);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
CustomTabIntentDataProvider.DENYLIST_ENTRIES.setForTesting(
"com.dc.joker|com.marvel.thanos");
assertEquals("Width should be 0", 0, dataProvider.getInitialActivityWidth());
}
@Test
public void partialCustomTabHeightResizeBehavior_Default() {
Intent intent =
new Intent()
.putExtra(EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR, ACTIVITY_HEIGHT_DEFAULT);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertFalse(
"The default height resize behavior should return false",
dataProvider.isPartialCustomTabFixedHeight());
}
@Test
public void partialCustomTabHeightResizeBehavior_Adjustable() {
Intent intent =
new Intent()
.putExtra(
EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR, ACTIVITY_HEIGHT_ADJUSTABLE);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertFalse(
"The adjustable height resize behavior should return false",
dataProvider.isPartialCustomTabFixedHeight());
}
@Test
public void partialCustomTabHeightResizeBehavior_Fixed() {
Intent intent =
new Intent().putExtra(EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR, ACTIVITY_HEIGHT_FIXED);
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertTrue(
"The fixed height resize behavior should return true",
dataProvider.isPartialCustomTabFixedHeight());
}
@Test
public void sideSheetSlideInBehavior() {
// No extra
var dataProvider =
new CustomTabIntentDataProvider(new Intent(), mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Should return ..SLIDE_IN_FROM_SIDE for the default slide-in behavior",
ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_SIDE,
dataProvider.getSideSheetSlideInBehavior());
}
@Test
public void sideSheetPosition() {
// No extra
var dataProvider =
new CustomTabIntentDataProvider(new Intent(), mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Should return ..POSITION_END for the default side sheet position",
ACTIVITY_SIDE_SHEET_POSITION_END,
dataProvider.getSideSheetPosition());
// Default
Intent intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_POSITION,
ACTIVITY_SIDE_SHEET_POSITION_DEFAULT);
dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Should return ..POSITION_END for the default side sheet position",
ACTIVITY_SIDE_SHEET_POSITION_END,
dataProvider.getSideSheetPosition());
// Start
intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_POSITION,
ACTIVITY_SIDE_SHEET_POSITION_START);
dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Should return ..POSITION_START",
ACTIVITY_SIDE_SHEET_POSITION_START,
dataProvider.getSideSheetPosition());
// End
intent =
new Intent()
.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_POSITION,
ACTIVITY_SIDE_SHEET_POSITION_END);
dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Should return ..POSITION_END",
ACTIVITY_SIDE_SHEET_POSITION_END,
dataProvider.getSideSheetPosition());
}
@Test
public void testGetAppIdFromReferrer() {
assertEquals(
"extra.activity.referrer",
CustomTabIntentDataProvider.getAppIdFromReferrer(
buildMockActivity("android-app://extra.activity.referrer")));
assertEquals(
"co.abc.xyz",
CustomTabIntentDataProvider.getAppIdFromReferrer(
buildMockActivity("android-app://co.abc.xyz")));
assertNonPackageUriReferrer("");
assertNonPackageUriReferrer("invalid");
assertNonPackageUriReferrer("android-app://"); // empty host name is invalid.
assertNonPackageUriReferrer(Uri.parse("https://www.one.com").toString());
}
@Test
public void testGetClientPackageName_Session() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.foo.bar");
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(IntentHandler.EXTRA_CALLING_ACTIVITY_PACKAGE, "com.baz.qux");
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
Assert.assertEquals("com.foo.bar", dataProvider.getClientPackageName());
}
@Test
public void testGetClientPackageName_Intent() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn(null);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(IntentHandler.EXTRA_CALLING_ACTIVITY_PACKAGE, "com.foo.bar");
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
Assert.assertEquals("com.foo.bar", dataProvider.getClientPackageName());
}
@Test
public void testGetClientPackageName_None() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn(null);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
CustomTabIntentDataProvider dataProvider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertNull(dataProvider.getClientPackageName());
}
@Test
public void testGetClientPackageNameIdentitySharing() {
Intent intent = new Intent();
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
Assert.assertNull(dataProvider.getClientPackageNameIdentitySharing());
intent.putExtra(IntentHandler.EXTRA_LAUNCHED_FROM_PACKAGE, "com.foo.bar");
var dataProvider2 = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
Assert.assertEquals("com.foo.bar", dataProvider2.getClientPackageNameIdentitySharing());
}
@Test
public void testIsTrustedCustomTab_NoServiceConnection() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn(null);
when(connection.isFirstParty(eq("com.foo.bar"))).thenReturn(true);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
Assert.assertFalse(CustomTabIntentDataProvider.isTrustedCustomTab(intent, null));
intent.putExtra(IntentHandler.EXTRA_CALLING_ACTIVITY_PACKAGE, "com.foo.bar");
Assert.assertTrue(CustomTabIntentDataProvider.isTrustedCustomTab(intent, null));
}
@Test
@DisableFeatures(ChromeFeatureList.CCT_AUTO_TRANSLATE)
public void getTranslateLanguage_autoTranslateFeatureDisabled() {
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.example.foo");
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(CustomTabIntentDataProvider.EXTRA_TRANSLATE_LANGUAGE, "fr");
intent.putExtra(CustomTabIntentDataProvider.EXTRA_AUTO_TRANSLATE_LANGUAGE, "es");
CustomTabIntentDataProvider provider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("fr", provider.getTranslateLanguage());
assertFalse(provider.shouldAutoTranslate());
}
@Test
@EnableFeatures(ChromeFeatureList.CCT_AUTO_TRANSLATE)
public void getTranslateLanguage_autoTranslateExtraMissing() {
CustomTabIntentDataProvider.AUTO_TRANSLATE_ALLOW_ALL_FIRST_PARTIES.setForTesting(false);
CustomTabIntentDataProvider.AUTO_TRANSLATE_PACKAGE_NAME_ALLOWLIST.setForTesting(
"com.example.foo|com.example.bar");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.example.foo");
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(CustomTabIntentDataProvider.EXTRA_TRANSLATE_LANGUAGE, "fr");
CustomTabIntentDataProvider provider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("fr", provider.getTranslateLanguage());
assertFalse(provider.shouldAutoTranslate());
}
@Test
@EnableFeatures(ChromeFeatureList.CCT_AUTO_TRANSLATE)
public void getTranslateLanguage_autoTranslateWithAllowedPackageName() {
CustomTabIntentDataProvider.AUTO_TRANSLATE_ALLOW_ALL_FIRST_PARTIES.setForTesting(false);
CustomTabIntentDataProvider.AUTO_TRANSLATE_PACKAGE_NAME_ALLOWLIST.setForTesting(
"com.example.foo|com.example.bar");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.example.foo");
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(CustomTabIntentDataProvider.EXTRA_TRANSLATE_LANGUAGE, "fr");
intent.putExtra(CustomTabIntentDataProvider.EXTRA_AUTO_TRANSLATE_LANGUAGE, "es");
CustomTabIntentDataProvider provider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("es", provider.getTranslateLanguage());
assertTrue(provider.shouldAutoTranslate());
}
@Test
@EnableFeatures(ChromeFeatureList.CCT_AUTO_TRANSLATE)
public void getTranslateLanguage_autoTranslateWithoutAllowedPackageName() {
CustomTabIntentDataProvider.AUTO_TRANSLATE_ALLOW_ALL_FIRST_PARTIES.setForTesting(false);
CustomTabIntentDataProvider.AUTO_TRANSLATE_PACKAGE_NAME_ALLOWLIST.setForTesting(
"com.example.foo|com.example.bar");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.not.in.allowlist");
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(CustomTabIntentDataProvider.EXTRA_TRANSLATE_LANGUAGE, "fr");
intent.putExtra(CustomTabIntentDataProvider.EXTRA_AUTO_TRANSLATE_LANGUAGE, "es");
CustomTabIntentDataProvider provider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("fr", provider.getTranslateLanguage());
assertFalse(provider.shouldAutoTranslate());
}
@Test
@EnableFeatures(ChromeFeatureList.CCT_AUTO_TRANSLATE)
public void getTranslateLanguage_autoTranslateWithFirstPartyAllowed() {
CustomTabIntentDataProvider.AUTO_TRANSLATE_ALLOW_ALL_FIRST_PARTIES.setForTesting(true);
CustomTabIntentDataProvider.AUTO_TRANSLATE_PACKAGE_NAME_ALLOWLIST.setForTesting(
"com.example.foo|com.example.bar");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.not.in.allowlist");
when(connection.isFirstParty(eq("com.not.in.allowlist"))).thenReturn(true);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(CustomTabIntentDataProvider.EXTRA_TRANSLATE_LANGUAGE, "fr");
intent.putExtra(CustomTabIntentDataProvider.EXTRA_AUTO_TRANSLATE_LANGUAGE, "es");
CustomTabIntentDataProvider provider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("es", provider.getTranslateLanguage());
assertTrue(provider.shouldAutoTranslate());
}
@Test
@EnableFeatures(ChromeFeatureList.CCT_AUTO_TRANSLATE)
public void getTranslateLanguage_autoTranslateWithThirdPartyPackageName() {
CustomTabIntentDataProvider.AUTO_TRANSLATE_ALLOW_ALL_FIRST_PARTIES.setForTesting(true);
CustomTabIntentDataProvider.AUTO_TRANSLATE_PACKAGE_NAME_ALLOWLIST.setForTesting(
"com.example.foo|com.example.bar");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.not.in.allowlist");
when(connection.isFirstParty(eq("com.not.in.allowlist"))).thenReturn(false);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new Intent();
intent.putExtra(CustomTabIntentDataProvider.EXTRA_TRANSLATE_LANGUAGE, "fr");
intent.putExtra(CustomTabIntentDataProvider.EXTRA_AUTO_TRANSLATE_LANGUAGE, "es");
CustomTabIntentDataProvider provider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals("fr", provider.getTranslateLanguage());
assertFalse(provider.shouldAutoTranslate());
}
@Test
public void getSecondaryToolbarSwipeUpPendingIntent() {
Intent intent = new Intent();
var pendingIntent = mock(PendingIntent.class);
intent.putExtra(
CustomTabIntentDataProvider.EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION, pendingIntent);
var provider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(pendingIntent, provider.getSecondaryToolbarSwipeUpPendingIntent());
}
@Test
public void testCanInteractWithBackground() {
Intent intent = new Intent();
CustomTabIntentDataProvider provider =
new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertTrue(
"Background interaction should be enabled by default",
provider.canInteractWithBackground());
intent.putExtra(
CustomTabIntentDataProvider.EXTRA_ENABLE_BACKGROUND_INTERACTION,
BackgroundInteractBehavior.OFF);
provider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertFalse(
"Background interaction should be overridden by the legacy extra",
provider.canInteractWithBackground());
}
@Test
public void testActivityDecorationType_Default() {
// Decoration not set
Intent intent = new CustomTabsIntent.Builder().build().intent;
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Decoration types do not match",
ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW,
dataProvider.getActivitySideSheetDecorationType());
// Decoration set higher than max
intent.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE,
ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER + 1);
var dataProvider2 = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Decoration types do not match",
ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW,
dataProvider2.getActivitySideSheetDecorationType());
}
@Test
public void testActivityDecorationType_Shadow() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE,
ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Decoration types do not match",
ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW,
dataProvider.getActivitySideSheetDecorationType());
}
@Test
public void testActivityDecorationType_DividerLine() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE,
ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Decoration types do not match",
ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER,
dataProvider.getActivitySideSheetDecorationType());
}
@Test
public void testActivityDecorationType_None() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE,
CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Decoration types do not match",
CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE,
dataProvider.getActivitySideSheetDecorationType());
}
@Test
public void testActivityRoundedCornersPosition_Default() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION,
CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_DEFAULT);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Rounded corners positions do not match",
CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE,
dataProvider.getActivitySideSheetRoundedCornersPosition());
}
@Test
public void testActivityRoundedCornersPosition_None() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION,
CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Rounded corners positions do not match",
CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE,
dataProvider.getActivitySideSheetRoundedCornersPosition());
}
@Test
public void testActivityRoundedCornersPosition_Top() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION,
CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_TOP);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertEquals(
"Rounded corners positions do not match",
CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_TOP,
dataProvider.getActivitySideSheetRoundedCornersPosition());
}
@Test
public void testActivityScrollContentResize() {
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(CustomTabIntentDataProvider.EXTRA_ACTIVITY_SCROLL_CONTENT_RESIZE, true);
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
assertTrue("scroll-content-resize was not set", dataProvider.contentScrollMayResizeTab());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void searchInCCT_originValidation() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting(
"com.a.b.c|org.d.e.f");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new CustomTabsIntent.Builder().build().intent;
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
assertTrue(dataProvider.isInteractiveOmniboxAllowed());
when(connection.getClientPackageNameForSession(any())).thenReturn("org.d.e.f");
assertTrue(dataProvider.isInteractiveOmniboxAllowed());
when(connection.getClientPackageNameForSession(any())).thenReturn("com.d.e.f");
assertFalse(dataProvider.isInteractiveOmniboxAllowed());
}
@Test
@DisableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void searchInCCT_allowedPackagesRejectedWithFeatureDisabled() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting(
"com.a.b.c|org.d.e.f");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new CustomTabsIntent.Builder().build().intent;
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
assertFalse(dataProvider.isInteractiveOmniboxAllowed());
when(connection.getClientPackageNameForSession(any())).thenReturn("org.d.e.f");
assertFalse(dataProvider.isInteractiveOmniboxAllowed());
when(connection.getClientPackageNameForSession(any())).thenReturn("com.d.e.f");
assertFalse(dataProvider.isInteractiveOmniboxAllowed());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void searchInCCT_notAllowedInOffTheRecordMode() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new CustomTabsIntent.Builder().build().intent;
var dataProvider =
spy(new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT));
when(dataProvider.isOffTheRecord()).thenReturn(true);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
assertFalse(dataProvider.isInteractiveOmniboxAllowed());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void searchInCCT_notAllowedOnPartialCCTs() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new CustomTabsIntent.Builder().build().intent;
var dataProvider =
spy(new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT));
when(dataProvider.isPartialCustomTab()).thenReturn(true);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
assertFalse(dataProvider.isInteractiveOmniboxAllowed());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void searchInCCT_notAllowedOnAutomotive() {
var shadowPkgMgr = Shadows.shadowOf(mContext.getPackageManager());
shadowPkgMgr.setSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, /* supported= */ true);
assertTrue(BuildInfo.getInstance().isAutomotive);
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
Intent intent = new CustomTabsIntent.Builder().build().intent;
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
assertFalse(dataProvider.isInteractiveOmniboxAllowed());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void addShareOption_conventionalCct_defaultState() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
when(connection.shouldEnableOmniboxForIntent(any())).thenReturn(false);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
Intent intent = new CustomTabsIntent.Builder().build().intent;
// Buttons are initialized as part of the Constructor logic.
// Expect only the share button to be created.
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
var buttons = dataProvider.getCustomButtonsOnToolbar();
assertEquals(1, buttons.size());
var button = buttons.get(0);
assertEquals(CustomButtonParams.ButtonType.CCT_SHARE_BUTTON, button.getType());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void addShareOption_conventionalCct_disabledState() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
when(connection.shouldEnableOmniboxForIntent(any())).thenReturn(false);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_OFF);
// Buttons are initialized as part of the Constructor logic.
// Expect no toolbar buttons.
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
var buttons = dataProvider.getCustomButtonsOnToolbar();
assertEquals(0, buttons.size());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void addShareOption_searchInCct_enabledState() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
when(connection.shouldEnableOmniboxForIntent(any())).thenReturn(true);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_ON);
// Buttons are initialized as part of the Constructor logic.
// Expect only the Open in Browser button, as the Share button is gated by empty Toolbar.
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
var buttons = dataProvider.getCustomButtonsOnToolbar();
assertEquals(1, buttons.size());
assertEquals(
CustomButtonParams.ButtonType.CCT_OPEN_IN_BROWSER_BUTTON, buttons.get(0).getType());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void addOpenInBrowserOption_searchInCct_defaultState() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
when(connection.shouldEnableOmniboxForIntent(any())).thenReturn(true);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
Intent intent = new CustomTabsIntent.Builder().build().intent;
// Buttons are initialized as part of the Constructor logic.
// Since we're simulating an Omnibox-enabled CCT, expect the default button to be the Open
// in Browser.
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
var buttons = dataProvider.getCustomButtonsOnToolbar();
assertEquals(1, buttons.size());
var button = buttons.get(0);
assertEquals(CustomButtonParams.ButtonType.CCT_OPEN_IN_BROWSER_BUTTON, button.getType());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void addOpenInBrowserOption_searchInCct_disabledState() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
when(connection.shouldEnableOmniboxForIntent(any())).thenReturn(true);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabIntentDataProvider.EXTRA_OPEN_IN_BROWSER_STATE,
CustomTabIntentDataProvider.CustomTabsButtonState.BUTTON_STATE_OFF);
// Buttons are initialized as part of the Constructor logic.
// Since we explicitly disabled the Open in Browser button, we should pick the implicitly
// added Share button.
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
var buttons = dataProvider.getCustomButtonsOnToolbar();
assertEquals(1, buttons.size());
var button = buttons.get(0);
assertEquals(CustomButtonParams.ButtonType.CCT_SHARE_BUTTON, button.getType());
}
@Test
@EnableFeatures({ChromeFeatureList.SEARCH_IN_CCT})
public void addOpenInBrowserOption_conventionalCct_enabledState() {
CustomTabIntentDataProvider.OMNIBOX_ALLOWED_PACKAGE_NAMES.setForTesting("com.a.b.c");
CustomTabsConnection connection = Mockito.mock(CustomTabsConnection.class);
CustomTabsConnection.setInstanceForTesting(connection);
when(connection.shouldEnableOmniboxForIntent(any())).thenReturn(false);
when(connection.getClientPackageNameForSession(any())).thenReturn("com.a.b.c");
Intent intent = new CustomTabsIntent.Builder().build().intent;
intent.putExtra(
CustomTabIntentDataProvider.EXTRA_OPEN_IN_BROWSER_STATE,
CustomTabIntentDataProvider.CustomTabsButtonState.BUTTON_STATE_ON);
// Buttons are initialized as part of the Constructor logic.
// Expect only the Open in Browser button, as the Share button is gated by empty Toolbar.
var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT);
var buttons = dataProvider.getCustomButtonsOnToolbar();
assertEquals(1, buttons.size());
assertEquals(
CustomButtonParams.ButtonType.CCT_OPEN_IN_BROWSER_BUTTON, buttons.get(0).getType());
}
private Bundle createActionButtonInToolbarBundle() {
Bundle bundle = new Bundle();
bundle.putInt(CustomTabsIntent.KEY_ID, CustomTabsIntent.TOOLBAR_ACTION_BUTTON_ID);
int iconHeight = mContext.getResources().getDimensionPixelSize(R.dimen.toolbar_icon_height);
bundle.putParcelable(
CustomTabsIntent.KEY_ICON,
Bitmap.createBitmap(iconHeight, iconHeight, Bitmap.Config.ALPHA_8));
bundle.putString(CustomTabsIntent.KEY_DESCRIPTION, BUTTON_DESCRIPTION);
bundle.putParcelable(
CustomTabsIntent.KEY_PENDING_INTENT,
PendingIntent.getBroadcast(
mContext,
0,
new Intent(),
IntentUtils.getPendingIntentMutabilityFlag(true)));
bundle.putBoolean(CustomButtonParamsImpl.SHOW_ON_TOOLBAR, true);
return bundle;
}
private Bundle createCustomActionButtonBundleWithId(int id) {
Bundle bundle = new Bundle();
bundle.putInt(CustomTabsIntent.KEY_ID, id);
int iconHeight = mContext.getResources().getDimensionPixelSize(R.dimen.toolbar_icon_height);
bundle.putParcelable(
CustomTabsIntent.KEY_ICON,
Bitmap.createBitmap(iconHeight, iconHeight, Bitmap.Config.ALPHA_8));
bundle.putString(CustomTabsIntent.KEY_DESCRIPTION, BUTTON_DESCRIPTION);
bundle.putParcelable(
CustomTabsIntent.KEY_PENDING_INTENT,
PendingIntent.getBroadcast(
mContext,
0,
new Intent(),
IntentUtils.getPendingIntentMutabilityFlag(true)));
bundle.putBoolean(CustomButtonParamsImpl.SHOW_ON_TOOLBAR, true);
return bundle;
}
private Bundle createCustomGoogleBottomBarItemBundleWithId(int id) {
Bundle bundle = new Bundle();
bundle.putInt(CustomTabsIntent.KEY_ID, id);
int iconHeight = mContext.getResources().getDimensionPixelSize(R.dimen.toolbar_icon_height);
bundle.putParcelable(
CustomTabsIntent.KEY_ICON,
Bitmap.createBitmap(iconHeight, iconHeight, Bitmap.Config.ALPHA_8));
bundle.putString(CustomTabsIntent.KEY_DESCRIPTION, BUTTON_DESCRIPTION);
bundle.putParcelable(
CustomTabsIntent.KEY_PENDING_INTENT,
PendingIntent.getBroadcast(
mContext,
0,
new Intent(),
IntentUtils.getPendingIntentMutabilityFlag(true)));
bundle.putBoolean(CustomButtonParamsImpl.SHOW_ON_TOOLBAR, false);
return bundle;
}
private Bundle createMenuItemBundle() {
Bundle bundle = new Bundle();
bundle.putString(CustomTabsIntent.KEY_MENU_ITEM_TITLE, "title");
bundle.putParcelable(
CustomTabsIntent.KEY_PENDING_INTENT,
PendingIntent.getBroadcast(
mContext,
0,
new Intent(),
IntentUtils.getPendingIntentMutabilityFlag(true)));
return bundle;
}
protected Uri getLaunchingUrl() {
return Uri.parse("https://www.example.com/");
}
private void assertNonPackageUriReferrer(String referrerStr) {
assertEquals(
referrerStr,
CustomTabIntentDataProvider.getAppIdFromReferrer(buildMockActivity(referrerStr)));
}
private Activity buildMockActivity(String referrer) {
Activity mockActivity = Mockito.mock(Activity.class);
Mockito.doReturn(new Intent()).when(mockActivity).getIntent();
Mockito.doReturn(Uri.parse(referrer)).when(mockActivity).getReferrer();
return mockActivity;
}
}