Home | History | Annotate | Download | only in customtabs
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.browser.customtabs;
     18 
     19 import android.app.Activity;
     20 import android.app.PendingIntent;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Color;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.view.View;
     28 import android.widget.RemoteViews;
     29 
     30 import androidx.annotation.AnimRes;
     31 import androidx.annotation.ColorInt;
     32 import androidx.annotation.NonNull;
     33 import androidx.annotation.Nullable;
     34 import androidx.core.app.ActivityOptionsCompat;
     35 import androidx.core.app.BundleCompat;
     36 import androidx.core.content.ContextCompat;
     37 
     38 import java.util.ArrayList;
     39 
     40 /**
     41  * Class holding the {@link Intent} and start bundle for a Custom Tabs Activity.
     42  *
     43  * <p>
     44  * <strong>Note:</strong> The constants below are public for the browser implementation's benefit.
     45  * You are strongly encouraged to use {@link CustomTabsIntent.Builder}.</p>
     46  */
     47 public final class CustomTabsIntent {
     48 
     49     /**
     50      * Indicates that the user explicitly opted out of Custom Tabs in the calling application.
     51      * <p>
     52      * If an application provides a mechanism for users to opt out of Custom Tabs, this extra should
     53      * be provided with {@link Intent#FLAG_ACTIVITY_NEW_TASK} to ensure the browser does not attempt
     54      * to trigger any Custom Tab-like experiences as a result of the VIEW intent.
     55      * <p>
     56      * If this extra is present with {@link Intent#FLAG_ACTIVITY_NEW_TASK}, all Custom Tabs
     57      * customizations will be ignored.
     58      */
     59     private static final String EXTRA_USER_OPT_OUT_FROM_CUSTOM_TABS =
     60             "android.support.customtabs.extra.user_opt_out";
     61 
     62     /**
     63      * Extra used to match the session. This has to be included in the intent to open in
     64      * a custom tab. This is the same IBinder that gets passed to ICustomTabsService#newSession.
     65      * Null if there is no need to match any service side sessions with the intent.
     66      */
     67     public static final String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
     68 
     69     /**
     70      * Extra that changes the background color for the toolbar. colorRes is an int that specifies a
     71      * {@link Color}, not a resource id.
     72      */
     73     public static final String EXTRA_TOOLBAR_COLOR =
     74             "android.support.customtabs.extra.TOOLBAR_COLOR";
     75 
     76     /**
     77      * Boolean extra that enables the url bar to hide as the user scrolls down the page
     78      */
     79     public static final String EXTRA_ENABLE_URLBAR_HIDING =
     80             "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
     81 
     82     /**
     83      * Extra bitmap that specifies the icon of the back button on the toolbar. If the client chooses
     84      * not to customize it, a default close button will be used.
     85      */
     86     public static final String EXTRA_CLOSE_BUTTON_ICON =
     87             "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
     88 
     89     /**
     90      * Extra (int) that specifies state for showing the page title. Default is {@link #NO_TITLE}.
     91      */
     92     public static final String EXTRA_TITLE_VISIBILITY_STATE =
     93             "android.support.customtabs.extra.TITLE_VISIBILITY";
     94 
     95     /**
     96      * Don't show any title. Shows only the domain.
     97      */
     98     public static final int NO_TITLE = 0;
     99 
    100     /**
    101      * Shows the page title and the domain.
    102      */
    103     public static final int SHOW_PAGE_TITLE = 1;
    104 
    105     /**
    106      * Bundle used for adding a custom action button to the custom tab toolbar. The client should
    107      * provide a description, an icon {@link Bitmap} and a {@link PendingIntent} for the button.
    108      * All three keys must be present.
    109      */
    110     public static final String EXTRA_ACTION_BUTTON_BUNDLE =
    111             "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
    112 
    113     /**
    114      * List<Bundle> used for adding items to the top and bottom toolbars. The client should
    115      * provide an ID, a description, an icon {@link Bitmap} for each item. They may also provide a
    116      * {@link PendingIntent} if the item is a button.
    117      */
    118     public static final String EXTRA_TOOLBAR_ITEMS =
    119             "android.support.customtabs.extra.TOOLBAR_ITEMS";
    120 
    121     /**
    122      * Extra that changes the background color for the secondary toolbar. The value should be an
    123      * int that specifies a {@link Color}, not a resource id.
    124      */
    125     public static final String EXTRA_SECONDARY_TOOLBAR_COLOR =
    126             "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
    127 
    128     /**
    129      * Key that specifies the {@link Bitmap} to be used as the image source for the action button.
    130      *  The icon should't be more than 24dp in height (No padding needed. The button itself will be
    131      *  48dp in height) and have a width/height ratio of less than 2.
    132      */
    133     public static final String KEY_ICON = "android.support.customtabs.customaction.ICON";
    134 
    135     /**
    136      * Key that specifies the content description for the custom action button.
    137      */
    138     public static final String KEY_DESCRIPTION =
    139             "android.support.customtabs.customaction.DESCRIPTION";
    140 
    141     /**
    142      * Key that specifies the PendingIntent to launch when the action button or menu item was
    143      * clicked. The custom tab will be calling {@link PendingIntent#send()} on clicks after adding
    144      * the url as data. The client app can call {@link Intent#getDataString()} to get the url.
    145      */
    146     public static final String KEY_PENDING_INTENT =
    147             "android.support.customtabs.customaction.PENDING_INTENT";
    148 
    149     /**
    150      * Extra boolean that specifies whether the custom action button should be tinted. Default is
    151      * false and the action button will not be tinted.
    152      */
    153     public static final String EXTRA_TINT_ACTION_BUTTON =
    154             "android.support.customtabs.extra.TINT_ACTION_BUTTON";
    155 
    156     /**
    157      * Use an {@code ArrayList<Bundle>} for specifying menu related params. There should be a
    158      * separate {@link Bundle} for each custom menu item.
    159      */
    160     public static final String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
    161 
    162     /**
    163      * Key for specifying the title of a menu item.
    164      */
    165     public static final String KEY_MENU_ITEM_TITLE =
    166             "android.support.customtabs.customaction.MENU_ITEM_TITLE";
    167 
    168     /**
    169      * Bundle constructed out of {@link ActivityOptionsCompat} that will be running when the
    170      * {@link Activity} that holds the custom tab gets finished. A similar ActivityOptions
    171      * for creation should be constructed and given to the startActivity() call that
    172      * launches the custom tab.
    173      */
    174     public static final String EXTRA_EXIT_ANIMATION_BUNDLE =
    175             "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
    176 
    177     /**
    178      * Boolean extra that specifies whether a default share button will be shown in the menu.
    179      */
    180     public static final String EXTRA_DEFAULT_SHARE_MENU_ITEM =
    181             "android.support.customtabs.extra.SHARE_MENU_ITEM";
    182 
    183     /**
    184      * Extra that specifies the {@link RemoteViews} showing on the secondary toolbar. If this extra
    185      * is set, the other secondary toolbar configurations will be overriden. The height of the
    186      * {@link RemoteViews} should not exceed 56dp.
    187      * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
    188      */
    189     public static final String EXTRA_REMOTEVIEWS =
    190             "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
    191 
    192     /**
    193      * Extra that specifies an array of {@link View} ids. When these {@link View}s are clicked, a
    194      * {@link PendingIntent} will be sent, carrying the current url of the custom tab as data.
    195      * <p>
    196      * Note that Custom Tabs will override the default onClick behavior of the listed {@link View}s.
    197      * If you do not care about the current url, you can safely ignore this extra and use
    198      * {@link RemoteViews#setOnClickPendingIntent(int, PendingIntent)} instead.
    199      * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
    200      */
    201     public static final String EXTRA_REMOTEVIEWS_VIEW_IDS =
    202             "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
    203 
    204     /**
    205      * Extra that specifies the {@link PendingIntent} to be sent when the user clicks on the
    206      * {@link View}s that is listed by {@link #EXTRA_REMOTEVIEWS_VIEW_IDS}.
    207      * <p>
    208      * Note when this {@link PendingIntent} is triggered, it will have the current url as data
    209      * field, also the id of the clicked {@link View}, specified by
    210      * {@link #EXTRA_REMOTEVIEWS_CLICKED_ID}.
    211      * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
    212      */
    213     public static final String EXTRA_REMOTEVIEWS_PENDINGINTENT =
    214             "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
    215 
    216     /**
    217      * Extra that specifies which {@link View} has been clicked. This extra will be put to the
    218      * {@link PendingIntent} sent from Custom Tabs when a view in the {@link RemoteViews} is clicked
    219      * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
    220      */
    221     public static final String EXTRA_REMOTEVIEWS_CLICKED_ID =
    222             "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
    223 
    224     /**
    225      * Extra that specifies whether Instant Apps is enabled.
    226      */
    227     public static final String EXTRA_ENABLE_INSTANT_APPS =
    228             "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
    229 
    230     /**
    231      * Key that specifies the unique ID for an action button. To make a button to show on the
    232      * toolbar, use {@link #TOOLBAR_ACTION_BUTTON_ID} as its ID.
    233      */
    234     public static final String KEY_ID = "android.support.customtabs.customaction.ID";
    235 
    236     /**
    237      * The ID allocated to the custom action button that is shown on the toolbar.
    238      */
    239     public static final int TOOLBAR_ACTION_BUTTON_ID = 0;
    240 
    241     /**
    242      * The maximum allowed number of toolbar items.
    243      */
    244     private static final int MAX_TOOLBAR_ITEMS = 5;
    245 
    246     /**
    247      * An {@link Intent} used to start the Custom Tabs Activity.
    248      */
    249     @NonNull public final Intent intent;
    250 
    251     /**
    252      * A {@link Bundle} containing the start animation for the Custom Tabs Activity.
    253      */
    254     @Nullable public final Bundle startAnimationBundle;
    255 
    256     /**
    257      * Convenience method to launch a Custom Tabs Activity.
    258      * @param context The source Context.
    259      * @param url The URL to load in the Custom Tab.
    260      */
    261     public void launchUrl(Context context, Uri url) {
    262         intent.setData(url);
    263         ContextCompat.startActivity(context, intent, startAnimationBundle);
    264     }
    265 
    266     private CustomTabsIntent(Intent intent, Bundle startAnimationBundle) {
    267         this.intent = intent;
    268         this.startAnimationBundle = startAnimationBundle;
    269     }
    270 
    271     /**
    272      * Builder class for {@link CustomTabsIntent} objects.
    273      */
    274     public static final class Builder {
    275         private final Intent mIntent = new Intent(Intent.ACTION_VIEW);
    276         private ArrayList<Bundle> mMenuItems = null;
    277         private Bundle mStartAnimationBundle = null;
    278         private ArrayList<Bundle> mActionButtons = null;
    279         private boolean mInstantAppsEnabled = true;
    280 
    281         /**
    282          * Creates a {@link CustomTabsIntent.Builder} object associated with no
    283          * {@link CustomTabsSession}.
    284          */
    285         public Builder() {
    286             this(null);
    287         }
    288 
    289         /**
    290          * Creates a {@link CustomTabsIntent.Builder} object associated with a given
    291          * {@link CustomTabsSession}.
    292          *
    293          * Guarantees that the {@link Intent} will be sent to the same component as the one the
    294          * session is associated with.
    295          *
    296          * @param session The session to associate this Builder with.
    297          */
    298         public Builder(@Nullable CustomTabsSession session) {
    299             if (session != null) mIntent.setPackage(session.getComponentName().getPackageName());
    300             Bundle bundle = new Bundle();
    301             BundleCompat.putBinder(
    302                     bundle, EXTRA_SESSION, session == null ? null : session.getBinder());
    303             mIntent.putExtras(bundle);
    304         }
    305 
    306         /**
    307          * Sets the toolbar color.
    308          *
    309          * @param color {@link Color}
    310          */
    311         public Builder setToolbarColor(@ColorInt int color) {
    312             mIntent.putExtra(EXTRA_TOOLBAR_COLOR, color);
    313             return this;
    314         }
    315 
    316         /**
    317          * Enables the url bar to hide as the user scrolls down on the page.
    318          */
    319         public Builder enableUrlBarHiding() {
    320             mIntent.putExtra(EXTRA_ENABLE_URLBAR_HIDING, true);
    321             return this;
    322         }
    323 
    324         /**
    325          * Sets the Close button icon for the custom tab.
    326          *
    327          * @param icon The icon {@link Bitmap}
    328          */
    329         public Builder setCloseButtonIcon(@NonNull Bitmap icon) {
    330             mIntent.putExtra(EXTRA_CLOSE_BUTTON_ICON, icon);
    331             return this;
    332         }
    333 
    334         /**
    335          * Sets whether the title should be shown in the custom tab.
    336          *
    337          * @param showTitle Whether the title should be shown.
    338          */
    339         public Builder setShowTitle(boolean showTitle) {
    340             mIntent.putExtra(EXTRA_TITLE_VISIBILITY_STATE,
    341                     showTitle ? SHOW_PAGE_TITLE : NO_TITLE);
    342             return this;
    343         }
    344 
    345         /**
    346          * Adds a menu item.
    347          *
    348          * @param label Menu label.
    349          * @param pendingIntent Pending intent delivered when the menu item is clicked.
    350          */
    351         public Builder addMenuItem(@NonNull String label, @NonNull PendingIntent pendingIntent) {
    352             if (mMenuItems == null) mMenuItems = new ArrayList<>();
    353             Bundle bundle = new Bundle();
    354             bundle.putString(KEY_MENU_ITEM_TITLE, label);
    355             bundle.putParcelable(KEY_PENDING_INTENT, pendingIntent);
    356             mMenuItems.add(bundle);
    357             return this;
    358         }
    359 
    360         /**
    361          * Adds a default share item to the menu.
    362          */
    363         public Builder addDefaultShareMenuItem() {
    364             mIntent.putExtra(EXTRA_DEFAULT_SHARE_MENU_ITEM, true);
    365             return this;
    366         }
    367 
    368         /**
    369          * Sets the action button that is displayed in the Toolbar.
    370          * <p>
    371          * This is equivalent to calling
    372          * {@link CustomTabsIntent.Builder#addToolbarItem(int, Bitmap, String, PendingIntent)}
    373          * with {@link #TOOLBAR_ACTION_BUTTON_ID} as id.
    374          *
    375          * @param icon The icon.
    376          * @param description The description for the button. To be used for accessibility.
    377          * @param pendingIntent pending intent delivered when the button is clicked.
    378          * @param shouldTint Whether the action button should be tinted.
    379          *
    380          * @see CustomTabsIntent.Builder#addToolbarItem(int, Bitmap, String, PendingIntent)
    381          */
    382         public Builder setActionButton(@NonNull Bitmap icon, @NonNull String description,
    383                 @NonNull PendingIntent pendingIntent, boolean shouldTint) {
    384             Bundle bundle = new Bundle();
    385             bundle.putInt(KEY_ID, TOOLBAR_ACTION_BUTTON_ID);
    386             bundle.putParcelable(KEY_ICON, icon);
    387             bundle.putString(KEY_DESCRIPTION, description);
    388             bundle.putParcelable(KEY_PENDING_INTENT, pendingIntent);
    389             mIntent.putExtra(EXTRA_ACTION_BUTTON_BUNDLE, bundle);
    390             mIntent.putExtra(EXTRA_TINT_ACTION_BUTTON, shouldTint);
    391             return this;
    392         }
    393 
    394         /**
    395          * Sets the action button that is displayed in the Toolbar with default tinting behavior.
    396          *
    397          * @see CustomTabsIntent.Builder#setActionButton(
    398          * Bitmap, String, PendingIntent, boolean)
    399          */
    400         public Builder setActionButton(@NonNull Bitmap icon, @NonNull String description,
    401                 @NonNull PendingIntent pendingIntent) {
    402             return setActionButton(icon, description, pendingIntent, false);
    403         }
    404 
    405         /**
    406          * Adds an action button to the custom tab. Multiple buttons can be added via this method.
    407          * If the given id equals {@link #TOOLBAR_ACTION_BUTTON_ID}, the button will be placed on
    408          * the toolbar; if the bitmap is too wide, it will be put to the bottom bar instead. If
    409          * the id is not {@link #TOOLBAR_ACTION_BUTTON_ID}, it will be directly put on secondary
    410          * toolbar. The maximum number of allowed toolbar items in a single intent is
    411          * {@link CustomTabsIntent#getMaxToolbarItems()}. Throws an
    412          * {@link IllegalStateException} when that number is exceeded per intent.
    413          *
    414          * @param id The unique id of the action button. This should be non-negative.
    415          * @param icon The icon.
    416          * @param description The description for the button. To be used for accessibility.
    417          * @param pendingIntent The pending intent delivered when the button is clicked.
    418          *
    419          * @see CustomTabsIntent#getMaxToolbarItems()
    420          * @deprecated Use
    421          * CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
    422          */
    423         @Deprecated
    424         public Builder addToolbarItem(int id, @NonNull Bitmap icon, @NonNull String description,
    425                 PendingIntent pendingIntent) throws IllegalStateException {
    426             if (mActionButtons == null) {
    427                 mActionButtons = new ArrayList<>();
    428             }
    429             if (mActionButtons.size() >= MAX_TOOLBAR_ITEMS) {
    430                 throw new IllegalStateException(
    431                         "Exceeded maximum toolbar item count of " + MAX_TOOLBAR_ITEMS);
    432             }
    433             Bundle bundle = new Bundle();
    434             bundle.putInt(KEY_ID, id);
    435             bundle.putParcelable(KEY_ICON, icon);
    436             bundle.putString(KEY_DESCRIPTION, description);
    437             bundle.putParcelable(KEY_PENDING_INTENT, pendingIntent);
    438             mActionButtons.add(bundle);
    439             return this;
    440         }
    441 
    442         /**
    443          * Sets the color of the secondary toolbar.
    444          * @param color The color for the secondary toolbar.
    445          */
    446         public Builder setSecondaryToolbarColor(@ColorInt int color) {
    447             mIntent.putExtra(EXTRA_SECONDARY_TOOLBAR_COLOR, color);
    448             return this;
    449         }
    450 
    451         /**
    452          * Sets the remote views displayed in the secondary toolbar in a custom tab.
    453          *
    454          * @param remoteViews   The {@link RemoteViews} that will be shown on the secondary toolbar.
    455          * @param clickableIDs  The IDs of clickable views. The onClick event of these views will be
    456          *                      handled by custom tabs.
    457          * @param pendingIntent The {@link PendingIntent} that will be sent when the user clicks on
    458          *                      one of the {@link View}s in clickableIDs. When the
    459          *                      {@link PendingIntent} is sent, it will have the current URL as its
    460          *                      intent data.
    461          * @see CustomTabsIntent#EXTRA_REMOTEVIEWS
    462          * @see CustomTabsIntent#EXTRA_REMOTEVIEWS_VIEW_IDS
    463          * @see CustomTabsIntent#EXTRA_REMOTEVIEWS_PENDINGINTENT
    464          * @see CustomTabsIntent#EXTRA_REMOTEVIEWS_CLICKED_ID
    465          */
    466         public Builder setSecondaryToolbarViews(@NonNull RemoteViews remoteViews,
    467                 @Nullable int[] clickableIDs, @Nullable PendingIntent pendingIntent) {
    468             mIntent.putExtra(EXTRA_REMOTEVIEWS, remoteViews);
    469             mIntent.putExtra(EXTRA_REMOTEVIEWS_VIEW_IDS, clickableIDs);
    470             mIntent.putExtra(EXTRA_REMOTEVIEWS_PENDINGINTENT, pendingIntent);
    471             return this;
    472         }
    473 
    474         /**
    475          * Sets whether Instant Apps is enabled for this Custom Tab.
    476 
    477          * @param enabled Whether Instant Apps should be enabled.
    478          */
    479         public Builder setInstantAppsEnabled(boolean enabled) {
    480             mInstantAppsEnabled = enabled;
    481             return this;
    482         }
    483 
    484         /**
    485          * Sets the start animations.
    486          *
    487          * @param context Application context.
    488          * @param enterResId Resource ID of the "enter" animation for the browser.
    489          * @param exitResId Resource ID of the "exit" animation for the application.
    490          */
    491         public Builder setStartAnimations(
    492                 @NonNull Context context, @AnimRes int enterResId, @AnimRes int exitResId) {
    493             mStartAnimationBundle = ActivityOptionsCompat.makeCustomAnimation(
    494                     context, enterResId, exitResId).toBundle();
    495             return this;
    496         }
    497 
    498         /**
    499          * Sets the exit animations.
    500          *
    501          * @param context Application context.
    502          * @param enterResId Resource ID of the "enter" animation for the application.
    503          * @param exitResId Resource ID of the "exit" animation for the browser.
    504          */
    505         public Builder setExitAnimations(
    506                 @NonNull Context context, @AnimRes int enterResId, @AnimRes int exitResId) {
    507             Bundle bundle = ActivityOptionsCompat.makeCustomAnimation(
    508                     context, enterResId, exitResId).toBundle();
    509             mIntent.putExtra(EXTRA_EXIT_ANIMATION_BUNDLE, bundle);
    510             return this;
    511         }
    512 
    513         /**
    514          * Combines all the options that have been set and returns a new {@link CustomTabsIntent}
    515          * object.
    516          */
    517         public CustomTabsIntent build() {
    518             if (mMenuItems != null) {
    519                 mIntent.putParcelableArrayListExtra(CustomTabsIntent.EXTRA_MENU_ITEMS, mMenuItems);
    520             }
    521             if (mActionButtons != null) {
    522                 mIntent.putParcelableArrayListExtra(EXTRA_TOOLBAR_ITEMS, mActionButtons);
    523             }
    524             mIntent.putExtra(EXTRA_ENABLE_INSTANT_APPS, mInstantAppsEnabled);
    525             return new CustomTabsIntent(mIntent, mStartAnimationBundle);
    526         }
    527     }
    528 
    529     /**
    530      * @return The maximum number of allowed toolbar items for
    531      * {@link CustomTabsIntent.Builder#addToolbarItem(int, Bitmap, String, PendingIntent)} and
    532      * {@link CustomTabsIntent#EXTRA_TOOLBAR_ITEMS}.
    533      */
    534     public static int getMaxToolbarItems() {
    535         return MAX_TOOLBAR_ITEMS;
    536     }
    537 
    538     /**
    539      * Adds the necessary flags and extras to signal any browser supporting custom tabs to use the
    540      * browser UI at all times and avoid showing custom tab like UI. Calling this with an intent
    541      * will override any custom tabs related customizations.
    542      * @param intent The intent to modify for always showing browser UI.
    543      * @return The same intent with the necessary flags and extras added.
    544      */
    545     public static Intent setAlwaysUseBrowserUI(Intent intent) {
    546         if (intent == null) intent = new Intent(Intent.ACTION_VIEW);
    547         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    548         intent.putExtra(EXTRA_USER_OPT_OUT_FROM_CUSTOM_TABS, true);
    549         return intent;
    550     }
    551 
    552     /**
    553      * Whether a browser receiving the given intent should always use browser UI and avoid using any
    554      * custom tabs UI.
    555      *
    556      * @param intent The intent to check for the required flags and extras.
    557      * @return Whether the browser UI should be used exclusively.
    558      */
    559     public static boolean shouldAlwaysUseBrowserUI(Intent intent) {
    560         return intent.getBooleanExtra(EXTRA_USER_OPT_OUT_FROM_CUSTOM_TABS, false)
    561                 && (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0;
    562     }
    563 }
    564