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