// 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.browserservices.intents;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_POSITION_END;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE;
import static androidx.browser.customtabs.CustomTabsIntent.CLOSE_BUTTON_POSITION_DEFAULT;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.widget.RemoteViews;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsIntent.CloseButtonPosition;
import androidx.browser.customtabs.CustomTabsSessionToken;
import androidx.browser.trusted.TrustedWebActivityDisplayMode;
import androidx.browser.trusted.sharing.ShareData;
import androidx.browser.trusted.sharing.ShareTarget;
import org.chromium.chrome.browser.flags.ActivityType;
import org.chromium.components.embedder_support.util.Origin;
import org.chromium.device.mojom.ScreenOrientationLockType;
import org.chromium.net.NetId;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/** Base class for model classes which parse incoming intent for customization data. */
public abstract class BrowserServicesIntentDataProvider {
// The type of UI for Custom Tab to use.
@IntDef({
CustomTabsUiType.DEFAULT,
CustomTabsUiType.MEDIA_VIEWER,
CustomTabsUiType.INFO_PAGE,
CustomTabsUiType.READER_MODE,
CustomTabsUiType.MINIMAL_UI_WEBAPP,
CustomTabsUiType.OFFLINE_PAGE,
CustomTabsUiType.AUTH_TAB
})
@Retention(RetentionPolicy.SOURCE)
public @interface CustomTabsUiType {
int DEFAULT = 0;
int MEDIA_VIEWER = 1;
int INFO_PAGE = 2;
int READER_MODE = 3;
int MINIMAL_UI_WEBAPP = 4;
int OFFLINE_PAGE = 5;
int READ_LATER = 6;
int AUTH_TAB = 7;
}
// The type of Disclosure for TWAs to use.
@IntDef({
TwaDisclosureUi.DEFAULT,
TwaDisclosureUi.V1_INFOBAR,
TwaDisclosureUi.V2_NOTIFICATION_OR_SNACKBAR
})
@Retention(RetentionPolicy.SOURCE)
public @interface TwaDisclosureUi {
int DEFAULT = -1;
int V1_INFOBAR = 0;
int V2_NOTIFICATION_OR_SNACKBAR = 1;
}
@IntDef({
ACTIVITY_SIDE_SHEET_SLIDE_IN_DEFAULT,
ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_BOTTOM,
ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_SIDE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ActivitySideSheetSlideInBehavior {}
// The type of Profile and UI that is used by the custom tab.
@IntDef({
CustomTabProfileType.REGULAR,
CustomTabProfileType.INCOGNITO,
CustomTabProfileType.EPHEMERAL
})
@Retention(RetentionPolicy.SOURCE)
public @interface CustomTabProfileType {
// The normal user profile.
int REGULAR = 0;
// An off-the-record profile with incognito UI.
int INCOGNITO = 1;
// An off-the-record profile without references to incognito mode.
int EPHEMERAL = 2;
}
/**
* Side sheet's default slide-in behavior. Same as {@link
* ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_SIDE}.
*/
public static final int ACTIVITY_SIDE_SHEET_SLIDE_IN_DEFAULT = 0;
/** Side sheet's slide-in behavior defined for bottom-to-up animation. */
public static final int ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_BOTTOM = 1;
/** Side shset's slide-in behavior for side-wise animation. */
public static final int ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_SIDE = 2;
/**
* @return The type of the Activity;
*/
public abstract @ActivityType int getActivityType();
/**
* @return the Intent this instance was created with.
*/
public @Nullable Intent getIntent() {
return null;
}
/**
* @return The session specified in the intent, or null.
*/
public @Nullable CustomTabsSessionToken getSession() {
return null;
}
/**
* @return The keep alive service intent specified in the intent, or null.
*/
public @Nullable Intent getKeepAliveServiceIntent() {
return null;
}
/**
* @return Whether chrome should animate when it finishes. We show animations only if the client
* app has supplied the correct animation resources via intent extra.
*/
public boolean shouldAnimateOnFinish() {
return false;
}
/**
* @return The package name of the client app. This is used for a workaround in order to
* retrieve the client's animation resources.
*/
public @Nullable String getClientPackageName() {
return null;
}
/**
* @return The package name of the client app provided via identity sharing API introduced in
* Android U.
*/
public @Nullable String getClientPackageNameIdentitySharing() {
return null;
}
/**
* @return The resource id for enter animation, which is used in {@link
* Activity#overridePendingTransition(int, int)}.
*/
public int getAnimationEnterRes() {
return 0;
}
/**
* @return The resource id for exit animation, which is used in
* {@link Activity#overridePendingTransition(int, int)}.
*/
public int getAnimationExitRes() {
return 0;
}
/** Checks whether or not the Intent is from Chrome or other trusted first party. */
public boolean isTrustedIntent() {
return false;
}
/**
* @return The URL that should be used from this intent.
* Must be called only after native has loaded.
*/
public @Nullable String getUrlToLoad() {
return null;
}
/**
* @return Whether url bar hiding should be enabled in the custom tab.
*/
public boolean shouldEnableUrlBarHiding() {
return true;
}
/**
* @return Whether scroll on content view may drag/resize the custom tab.
*/
public boolean contentScrollMayResizeTab() {
return false;
}
/**
* @return ColorProvider to be used.
*/
public abstract @NonNull ColorProvider getColorProvider();
/**
* @return ColorProvider when the system is in light mode.
*/
public @NonNull ColorProvider getLightColorProvider() {
return getColorProvider();
}
/**
* @return ColorProvider when the system is in dark mode.
*/
public @NonNull ColorProvider getDarkColorProvider() {
return getColorProvider();
}
/**
* @return The drawable of the icon of close button shown in the custom tab toolbar.
*/
public @Nullable Drawable getCloseButtonDrawable() {
return null;
}
/**
* @return The title visibility state for the toolbar.
*/
public int getTitleVisibilityState() {
return CustomTabsIntent.NO_TITLE;
}
/**
* @return Whether the default share item should be shown in the menu.
*/
public boolean shouldShowShareMenuItem() {
return false;
}
/**
* @return The params for the custom buttons that show on the toolbar.
*/
public List<CustomButtonParams> getCustomButtonsOnToolbar() {
return Collections.emptyList();
}
/**
* @return The list of params representing the buttons on the bottombar.
*/
public List<CustomButtonParams> getCustomButtonsOnBottombar() {
return Collections.emptyList();
}
/**
* @return The list of params representing the custom buttons on the Google Bottom Bar.
*/
public List<CustomButtonParams> getCustomButtonsOnGoogleBottomBar() {
return Collections.emptyList();
}
/**
* @return The {@link RemoteViews} to show on the bottom bar, or null if the extra is not
* specified.
*/
public @Nullable RemoteViews getBottomBarRemoteViews() {
return null;
}
/**
* @return A array of {@link View} ids, of which the onClick event is handled by the Activity.
*/
@Nullable
public int[] getClickableViewIDs() {
return null;
}
/**
* @return The {@link PendingIntent} that is sent when the user clicks on the remote view.
*/
public @Nullable PendingIntent getRemoteViewsPendingIntent() {
return null;
}
/**
* @return The {@link PendingIntent} that is sent when the user swipes up from the secondary
* (bottom) toolbar.
*/
public @Nullable PendingIntent getSecondaryToolbarSwipeUpPendingIntent() {
return null;
}
/**
* Gets params for all custom buttons, which is the combination of
* {@link #getCustomButtonsOnBottombar()} and {@link #getCustomButtonsOnToolbar()}.
*/
public List<CustomButtonParams> getAllCustomButtons() {
return Collections.emptyList();
}
/**
* @return Titles of menu items that were passed from client app via intent.
*/
public List<String> getMenuTitles() {
return Collections.emptyList();
}
/**
* @return Whether or not the Activity is being launched by an intent fired by Chrome itself.
*/
public boolean isOpenedByChrome() {
return false;
}
public @CustomTabsUiType int getUiType() {
return CustomTabsUiType.DEFAULT;
}
/**
* @return URL that should be loaded in place of the URL in {@link Intent#getData()}.
*/
public @Nullable String getMediaViewerUrl() {
return null;
}
/**
* @return Whether to enable the embedded media experience.
*/
public boolean shouldEnableEmbeddedMediaExperience() {
return false;
}
public boolean isFromMediaLauncherActivity() {
return false;
}
/**
* @return Whether there should be a star button in the menu.
*/
public boolean shouldShowStarButton() {
return true;
}
/**
* @return Whether there should be a download button in the menu.
*/
public boolean shouldShowDownloadButton() {
return true;
}
/**
* @return Whether the Activity uses an off-the-record profile.
*/
public boolean isOffTheRecord() {
switch (getCustomTabMode()) {
case CustomTabProfileType.EPHEMERAL:
case CustomTabProfileType.INCOGNITO:
return true;
case CustomTabProfileType.REGULAR:
return false;
}
assert false; // NOTREACHED
return false;
}
/**
* @return Whether the Activity is a regular, incognito or ephemeral custom tab.
*/
public @CustomTabProfileType int getCustomTabMode() {
return CustomTabProfileType.REGULAR;
}
/**
* @return Whether the Activity should attempt to display a Trusted Web Activity.
*/
public final boolean isTrustedWebActivity() {
return getActivityType() == ActivityType.TRUSTED_WEB_ACTIVITY;
}
/**
* @return Whether the Activity is either a Webapp or a WebAPK activity.
*/
public final boolean isWebappOrWebApkActivity() {
return getActivityType() == ActivityType.WEBAPP
|| getActivityType() == ActivityType.WEB_APK;
}
/**
* @return Whether the Activity is a WebAPK activity.
*/
public final boolean isWebApkActivity() {
return getActivityType() == ActivityType.WEB_APK;
}
/** Returns {@link TrustedWebActivityDisplayMode} supplied in the intent. */
public @Nullable TrustedWebActivityDisplayMode getTwaDisplayMode() {
return null;
}
/** Returns {@link ScreenOrientationLockType} supplied in the intent. */
public int getDefaultOrientation() {
return ScreenOrientationLockType.DEFAULT;
}
/**
* @return The component name of the module entry point, or null if not specified.
*/
public @Nullable ComponentName getModuleComponentName() {
return null;
}
/**
* @return The resource identifier for the dex that contains module code. {@code 0} if no dex
* resource is provided.
*/
public @Nullable String getModuleDexAssetName() {
return null;
}
/**
* @return Additional origins associated with a Trusted Web Activity client app.
*/
@Nullable
public List<String> getTrustedWebActivityAdditionalOrigins() {
return null;
}
/**
* @return All origins associated with a TrustedWebActivity client app, including the initially
* loaded origin.
*/
@Nullable
public Set<Origin> getAllTrustedWebActivityOrigins() {
return null;
}
/**
* @return ISO 639 code of target language the page should be translated to. This method
* requires native.
*/
public @Nullable String getTranslateLanguage() {
return null;
}
/**
* @return Whether or not the page should be automatically translated into the target language
* indicated by {@link getTranslateLanguage()}.
*/
public boolean shouldAutoTranslate() {
return false;
}
/**
* Returns {@link ShareTarget} describing the share target, or null if there is no associated
* share target.
*/
public @Nullable ShareTarget getShareTarget() {
return null;
}
/** Returns {@link ShareData} if there is data to be shared, and null otherwise. */
public @Nullable ShareData getShareData() {
return null;
}
/** Returns {@link WebappExtras} if the intent targets a webapp, and null otherwise. */
public @Nullable WebappExtras getWebappExtras() {
return null;
}
/** Returns {@link WebApkExtras} if the intent targets a WebAPK, and null otherwise. */
public @Nullable WebApkExtras getWebApkExtras() {
return null;
}
/**
* @return Whether the bottom bar should be shown.
*/
public final boolean shouldShowBottomBar() {
return !getCustomButtonsOnBottombar().isEmpty() || getBottomBarRemoteViews() != null;
}
/**
* Searches for the toolbar button with the given {@code id} and returns its index.
* @param id The ID of a toolbar button to search for.
* @return The index of the toolbar button with the given {@code id}, or -1 if no such button
* can be found.
*/
public final int getCustomToolbarButtonIndexForId(int id) {
List<CustomButtonParams> toolbarButtons = getCustomButtonsOnToolbar();
for (int i = 0; i < toolbarButtons.size(); i++) {
if (toolbarButtons.get(i).getId() == id) return i;
}
return -1;
}
/**
* @return The {@link CustomButtonParams} (either on the toolbar or bottom bar) with the given
* {@code id}, or null if no such button can be found.
*/
public final @Nullable CustomButtonParams getButtonParamsForId(int id) {
List<CustomButtonParams> customButtonParams = getAllCustomButtons();
for (CustomButtonParams params : customButtonParams) {
// A custom button params will always carry an ID. If the client calls updateVisuals()
// without an id, we will assign the toolbar action button id to it.
if (id == params.getId()) return params;
}
return null;
}
/**
* @return See {@link #getUiType()}.
*/
public final boolean isMediaViewer() {
return getUiType() == CustomTabsUiType.MEDIA_VIEWER;
}
/**
* @return If the Activity is an info page.
*/
public final boolean isInfoPage() {
return getUiType() == CustomTabsUiType.INFO_PAGE;
}
public @TwaDisclosureUi int getTwaDisclosureUi() {
return TwaDisclosureUi.DEFAULT;
}
@Nullable
public int[] getGsaExperimentIds() {
return null;
}
/**
* @return Whether the intent is for partial custom tabs bottom sheet.
*/
public boolean isPartialHeightCustomTab() {
return false;
}
/**
* @return Whether the intent is for partial custom tabs side sheet.
*/
public boolean isPartialWidthCustomTab() {
return false;
}
/**
* @return Whether the intent is partial custom tabs side sheet or bottom sheet.
*/
public boolean isPartialCustomTab() {
return false;
}
/**
* @return The value in pixels of the initial height of the Activity. It will return 0 if there
* is no value set.
*/
public @Px int getInitialActivityHeight() {
return 0;
}
/**
* @return The value in pixels of the initial width of the Activity. It will return 0 if there
* is no value set.
*/
public @Px int getInitialActivityWidth() {
return 0;
}
/**
* @return The value in pixels of the breakpoint where Side Sheets behave as Bottom Sheets.
* It will return 0 if there is no value set.
*/
public int getActivityBreakPoint() {
return 0;
}
/**
* @return An int representing the side sheet decoration type for the Activity.
*/
public int getActivitySideSheetDecorationType() {
return ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW;
}
/**
* @return An int representing the side sheet rounded corner position for the Activity
*/
public int getActivitySideSheetRoundedCornersPosition() {
return ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE;
}
/** Returns the {@link CloseButtonPosition}. */
public @CloseButtonPosition int getCloseButtonPosition() {
return CLOSE_BUTTON_POSITION_DEFAULT;
}
/**
* If {@code true} the App Menu will not be shown. If {@code false} it will be left to the
* Activity to decide.
*/
public boolean shouldSuppressAppMenu() {
return false;
}
/** Returns the partial custom tab toolbar corner radius. */
public @Px int getPartialTabToolbarCornerRadius() {
return 0;
}
/** Returns false as by default PCCT is resizable. */
public boolean isPartialCustomTabFixedHeight() {
return false;
}
/**
* @return true, as by default having a PCCT launched still allows interaction with the
* background application
*/
public boolean canInteractWithBackground() {
return false;
}
/** Return false since by default side panel does not show maximize button. */
public boolean showSideSheetMaximizeButton() {
return false;
}
/** Return the default behavior. */
public int getSideSheetSlideInBehavior() {
return ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_SIDE;
}
/** Return the default position. */
public int getSideSheetPosition() {
return ACTIVITY_SIDE_SHEET_POSITION_END;
}
/** Return whether calling package should be allowed to present an interactive Omnibox. */
public boolean isInteractiveOmniboxAllowed() {
return false;
}
/**
* Return the target network that should be used from this intent, the default value to be used
* when a network has not been explicitly set via intent.
*/
public long getTargetNetwork() {
return NetId.INVALID;
}
/** Return {@code true} if the service was launched for authentication. */
public boolean isAuthTab() {
return false;
}
/** Return the redirect scheme for AuthTab. */
public String getAuthRedirectScheme() {
return null;
}
}