CustomTabs in support v23
With the release of v23 and Android Marshmallow, Google introduced CustomTabs as separate library in reversion 23. To be honest, at first was confused and wasn’t sure, that term “custom tabs” is somehow related to UI widget. After some googling, I found what was the story behind it, well it was completely unrelated to my first assumption.
I bet every developer has encounter a segment of application flow when the user has to open a link. No matter if you have a separate Activity that wraps a WebView or you start Intent that will handle opening an url in or out of your domain, it still looks like the user is leaving your app for another app, so tragic it doesn’t look like your app anymore. :(
With the introduction of CustomTabs, you can open web content within the context of your application, powered up by Chrome browser. CustomTabs will allow you style the look and feel of a chrome tab with the colors of your application, custom transition animation when you exit or enter a tab, inflating action menu items and action buttons. Beside the styling, bounding to Chrome service, CustomTabs will give you full chrome web capabilities that probably couldn’t be achieved with standard WebView.
I wrote a simplified helper of the one that is in the demos. I’ll walk you through the implementation under the hood and the changes I made with regards the demo. [gist is in the code snippets]
Under the helper’s hood
First things first, we need to resolve if our installed Chrome version supports CustomTabs. String (sPackageNameToUse)that holds the package name that will be used to start explicit intent when we call the method that opens the tab.
public SimpleCustomChromeTabsHelper(Activity context) {
mContext = context;
sPackageNameToUse = getPackageNameToUse(mContext);
}
As I mentioned the term “Chrome service”, this is indeed Service and we need to bind to beforehand.
public void prepareUrl(String url) {
if (sPackageNameToUse == null && getPackageNameToUse(mContext) == null){
return;
}
if (mCustomTabClient == null || mCustomTabSession == null) {
CustomTabsClient.bindCustomTabsService(mContext, sPackageNameToUse, mCustomTabConnection);
} else {
mCustomTabSession.mayLaunchUrl(Uri.parse(url), null, null);
}
}
Once we have established connection to the CustomTabService, we should (not obligatory) prepare the internals of the service:
private CustomTabsServiceConnection mCustomTabConnection = new CustomTabsServiceConnection() { @Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) {
mCustomTabClient = customTabsClient;
mCustomTabSession = mCustomTabClient.newSession(mCallback);
mCustomTabClient.warmup(WARM_UP_ASYNC);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mCustomTabClient = null;
}
};
Calling warmUp() will prepare chrome service asynchronously of your current application context and asynchronously from the application itself. This warms up the browser and pre-fetches the content, so time before the tab comes visible to the user is short and the experience is seamless. When it comes to considering the CustomTabs as being part of your application, our application needs to be aware of the navigation events that occur when browsing the content, as you may guess navigation events such as page started loading, finished loading etc, are dispatched through CustomTabsCallback.
I wanted to isolate the UI options from the overall opening logic and introduced CustomTabsUiBuilder which is replica of the CustomTabIntent.Builder, it is decoupled of the CustomTabsSession argument unlike the builder I mentioned. The helper later will build internally a CustomTabIntent out of the CustomTabsUiBuilder instance.
Usage:
- Initialize it when your activity context is alive
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preview);
mCustomTabHelper = new SimpleCustomChromeTabsHelper(this); }
2. Once instantiated, we should call prepareUrl():
mCustomTabHelper.prepareUrl(mProduct.getRedirectUrl());
Which will bind to the Chrome service, if not previously bound, or will just notify Chrome service that we might be opening that link in the future. CustomTabSession instance can be (re)used to open or prepare multiple url.
3. Open the url
mCustomTabHelper.openUrl(mProduct.getRedirectUrl());
The overloaded method of openUrl() is using CustomTabsUiBuilder, as last argument.
4. Fallback when there is no CustomTabs availability
mCustomTabHelper.setFallback(new SimpleCustomChromeTabHelper.CustomTabFallback(){
@Override
public void onCustomTabsNotAvailableFallback(){//Start external browser or your WebView activity
}
});
Pre-requisites
I’ve been too excited to jump straight telling you about the code before mentioning the prerequisites. I’m running Chrome Dev version along stable one. If I choose the stable one, I’m not able to use CustomTabs at all. As Google suggests, CustomTabs will only work on Chrome 45 and beta versions of Chrome.
Demo
Short demo video from my application: https://youtu.be/fnIZwuJXjHI
Inspiration for this post here.