Imagine the following scenario: you have been tasked with displaying 3rd party content within an app you are developing. Do you launch a browser or do you build a web view?

This is not an easy question to answer.

Launching a browser is relatively easy to set up but comes at a cost: it takes the user out of the app via a heavy transition and there is no way to customize the browser to match your app design. The alternative option, using a web view, creates more development overhead, as developers have to implement features already accessible in a browser; however, it keeps the user in-app.

So what do you do? Browser or web view? Would you believe me if I told you that there is a potential third option?

There is and Google's Chrome Custom Tabs support library will change the way you solve this problem.


What is the Chrome Custom Tabs Support Library?

Chrome Custom Tabs is a support library for the Android platform that is meant to seamlessly bridge the gap from app to web. Out-of-the-box Chrome Custom Tabs offers the following features:

  • Customize the Chrome UI to reflect your app's brand by setting a toolbar color and a toolbar action button, adding action items to the overflow menu, and applying transition animations.
  • Performance optimizations by pre-warming the browser in the background and providing a likely URL to speed up page load time.
  • All of the current features available in Chrome such as a shared cookie jar and permission model, data saver (if enabled), and autocomplete.
  • Callbacks to the application when external navigation occurs.

If it sounds convincing, that's because it is. Here is a short clip that showcases the power of Chrome Custom Tabs vs. launching a browser vs. a web view implementation.

Chrome Custom Tabs vs launching a browser vs a web view implementation
Credit: Google

How does Chrome Custom Tabs actually work?

Behind the scenes, Chrome Custom Tabs is a glorified ACTION_VIEW Intent, which launches Chrome on top of your application and exposes a ChromeTabService that you can bind to. When you customize the UI, such as coloring the Chrome toolbar, you are passing an android.support.customtabs.extra.TOOLBAR_COLOR extra via an intent that only Chrome can utilize.

While not necessary, binding the ChromeTabService to your activity provides the opportunity to add performance optimizations noted previously. In addition, it also can check whether Chrome Custom Tabs is fully supported on the device.

It is important to note that if the user does not have Chrome installed on their phone or does not have version 45 of Chrome installed on their phone, the support library will launch a default browser or ask the user to select one if there is no default. Otherwise a web view will need to be utilized.


How can I use Chrome Custom Tabs in my app?

I have written a small demo applications that allows you to customize a Chrome Tab after selecting a website. Please follow along to learn more about the support library.


Before we start

First things first: you need to add the Chrome Custom Tabs support library to your gradle file: com.android.support:customtabs:23.0.1.


Custom Tabs The Easy Way

If your application has no need to add performance optimizations setting up custom tabs is a breeze. Launching a custom tab works very similarly to how you start an Activity, however, instead of using an intent you need to use a CustomTabIntent. Google has provided a CustomTabIntent.Builder and it is strongly encouraged that you use it. So, lets create one:

CustomTabsIntent.Builder intentBuilder = new Builder();

Now, you are freely able to customize your chrome tab using the public methods that are currently available. Showing the toolbar title or changing the toolbar color is as simple as:

intentBuilder.setShowTitle(mDisplayTitleCheckbox.isChecked());
intentBuilder.setToolbarColor(Color.Blue);

Adding start and exit animations works as follows:

intentBuilder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left);
intentBuilder.setExitAnimations(this, android.R.anim.slide_in_left, android.R.anim.slide_out_right);

You are able to customize the close button icon by providing your own Bitmap.

intentBuilder.setCloseButtonIcon(mActionBackIcon);

Adding an ActionButton works similarly to how a Notification Action is defined: a PendingIntent.Once your PendingIntent is created, you set it in the builder by passing the intent, a description, and a bitmap.

Intent actionIntent = new Intent(Intent.ACTION_DIAL);
actionIntent.setData(Uri.parse("tel:18001234567"));

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, actionIntent, 0);
intentBuilder.setActionButton(mColorToolbarCheckbox.isChecked() ? mActionCallLightIcon : mActionCallIcon, "Call", pendingIntent);

Adding overflow menu items to your chrome tab also requires creating a PendingIntent of your choice. The only difference is that you use:

intentBuilder.addMenuItem(getString(R.string.activity_custom_tab_share_website), menuIntent);

Once you are done customizing your chrome tab, simply call build() in your intent builder to return a CustomTabIntent. Launching your chrome tab is as easy as:

CustomTabsIntent customTabsIntent = buildCustomTabsIntent();
customTabsIntent.launchUrl(this, Uri.parse(mWebsiteUrl));

Optimizing Chrome Custom Tabs

As I noted previously, Chrome Custom Tabs has some built-in performance optimizations that you can enable: pre-warm the browser in a background thread and provide a URL to speed up page load time. In order to do this, you will need to use the CustomTabClient, which creates a CustomTabService. If you are familiar with Service's in Android, then you know that you need to bind the Service to your Activity. The CustomTabService is no different. To bind simply call CustomTabsClient.bindCustomTabsService(ctx, PKG_NAME_CHROME, this) passing in a Context, the Chrome package name, and a CustomTabServiceConnection, an abstract ServiceConnection.

In order to decouple the custom tab logic and service connection callbacks from the Activity, I created a CustomTabServiceController. This class extends CustomTabServiceConnection and implements the two methods required:

public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient)
public void onServiceDisconnected(ComponentName componentName)

Once the CustomTabService has been bound to your activity, onCustomTabsServiceConnected gets called and passes back an initialized CustomTabClient. If you want to warm up Chrome to make the page load faster, use warmup(). This method is asynchronous and returns a boolean to indicate whether the request was successful. After this is done, you are now able to start a new CustomTabSession. The method newSession() is used to link mayLaunchUrl(), the ACTION_VIEW intent that Chrome Tabs uses to launch, and custom chrome tab you created together. A CustomTabsCallback is provided here in case you need to handle navigation events that happen in your custom tab. Last but not least, use the mayLaunchUrl() method to notify Chrome of a likely navigation to a URL or a list of URLs if necessary. It also returns a boolean to notify whether your custom tab will be launched. If false, the user does not have the latest version of Chrome available and you will need to decide whether you want to launch Chrome externally or use a web view as a fallback. Please note that as a best practice, call warmup() before mayLaunchUrl().

@Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) {

 if (customTabsClient != null) {
 customTabsClient.warmup(0L);

 // Create a new session
 mCustomTabsSession = customTabsClient.newSession(null);

 // Let the session know that it may launch a URL soon
 if (!TextUtils.isEmpty(mWebsiteUrl)) {
 Uri uri = Uri.parse(mWebsiteUrl);
 if (uri != null && mCustomTabsSession != null) {

 // If this returns true, custom tabs will work,
 // otherwise, you need another alternative if you don't want the user
 // to be launched out of the app by default
 mCustomTabsSession.mayLaunchUrl(uri, null, null);
 }
 }
 }
}

Back in my CustomTabActivity, I instantiate my CustomTabServiceController and immediately bind the service.

mCustomTabServiceController = new CustomTabServiceController(this, mWebsiteUrl);
mCustomTabServiceController.bindCustomTabService();

And, as always, don't forget to unbind the CustomTabService.

@Override
protected void onDestroy() {
 super.onDestroy();

 // Unbind the custom tabs service
 mCustomTabServiceController.unbindCustomTabService();
}

Closing Thoughts

Chrome Custom Tabs is a powerful support library that delivers its promise to seamlessly bridge the gap between app and web. It is relatively easy to set up (with or without using the performance optimizations) and being able to customize Chrome's UI is remarkable. While there are still benefits to launching a browser or build a web view, there is now finally another option to help you decide what works best for your app.

The sample application can be found here