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      */
launchUrl(Context context, Uri url)261     public void launchUrl(Context context, Uri url) {
262         intent.setData(url);
263         ContextCompat.startActivity(context, intent, startAnimationBundle);
264     }
265 
CustomTabsIntent(Intent intent, Bundle startAnimationBundle)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          */
Builder()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          */
Builder(@ullable CustomTabsSession session)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          */
setToolbarColor(@olorInt int color)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          */
enableUrlBarHiding()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          */
setCloseButtonIcon(@onNull Bitmap icon)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          */
setShowTitle(boolean showTitle)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          */
addMenuItem(@onNull String label, @NonNull PendingIntent pendingIntent)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          */
addDefaultShareMenuItem()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          */
setActionButton(@onNull Bitmap icon, @NonNull String description, @NonNull PendingIntent pendingIntent, boolean shouldTint)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          */
setActionButton(@onNull Bitmap icon, @NonNull String description, @NonNull PendingIntent pendingIntent)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
addToolbarItem(int id, @NonNull Bitmap icon, @NonNull String description, PendingIntent pendingIntent)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          */
setSecondaryToolbarColor(@olorInt int color)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          */
setSecondaryToolbarViews(@onNull RemoteViews remoteViews, @Nullable int[] clickableIDs, @Nullable PendingIntent pendingIntent)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          */
setInstantAppsEnabled(boolean enabled)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          */
setStartAnimations( @onNull Context context, @AnimRes int enterResId, @AnimRes int exitResId)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          */
setExitAnimations( @onNull Context context, @AnimRes int enterResId, @AnimRes int exitResId)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          */
build()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      */
getMaxToolbarItems()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      */
setAlwaysUseBrowserUI(Intent intent)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      */
shouldAlwaysUseBrowserUI(Intent intent)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