pasko | 50653f2 | 2015-10-16 12:01:49 | [diff] [blame] | 1 | # Using Custom Tabs |
| 2 | |
| 3 | ## Summary |
| 4 | |
| 5 | This presents a few example applications using Custom Tabs, and a possible usage |
| 6 | of the APIs, both intent-based and the the background service. It covers UI |
| 7 | customization, setting up callbacks, pre-warming, pre-fetching, and lifecycle |
| 8 | management. Here we assume that Chrome's implementation of Custom Tabs is used. |
| 9 | Note that this feature is in no way specific to Chrome, but slight differences |
| 10 | may exist with other implementations. |
| 11 | |
| 12 | ### Overview |
| 13 | |
| 14 | In particular, this covers: |
| 15 | |
| 16 | * UI customization: |
| 17 | * Toolbar color |
| 18 | * Action button |
| 19 | * Custom menu items |
| 20 | * Custom in/out animations |
| 21 | * Navigation awareness: the browser delivers callbacks to the application for |
| 22 | navigations in the Custom Tab. |
| 23 | * Performance optimizations: |
| 24 | * Pre-warming of the Browser in the background, without stealing resources |
| 25 | from the application |
| 26 | * Providing a likely URL in advance to the browser, which may perform |
| 27 | speculative work, speeding up page load time. |
| 28 | |
| 29 | These features are enabled through two mechanisms: |
| 30 | |
| 31 | * Adding extras to the `ACTION_VIEW` intent sent to the browser. |
| 32 | * Connecting to a bound service in the target browser. |
| 33 | |
| 34 | ### Code Organization |
| 35 | |
| 36 | To get full benefits of the Custom Tabs APIs, it is recommended to use the |
| 37 | [Android Support Library](https://developer.android.com/tools/support-library/index.html). |
| 38 | |
| 39 | The code in this repository is organised in four parts: |
| 40 | |
| 41 | * `demos/`: This module contains sample implementations for Chrome Custom Tabs using the Android |
| 42 | Support Library. Feel free to re-use the classes withing this module. |
| 43 | * `shared/`: Shared code between the `Application` and `demos` modules. Feel free to |
| 44 | re-use the classes within this directory, which are only provided as a convenience. |
| 45 | In particular,`CustomTabsHelper` can be re-used. This code is not required to use Custom Tabs. |
| 46 | * `customtabs/`: Code within this directory is in the package |
| 47 | `android.support.customtabs`. This contains code analog to the Android Support library, but with |
| 48 | the latest version of Chrome Custom Tabs, enabling features that may still not be available on |
| 49 | the Android Support Library. API is subject to changes and this code should only be used if you |
| 50 | want to test the latest features. It is recommended to copy the code as-is to your project and |
| 51 | remove the Android Support Library for Chrome Custom Tabs from the `build.gradle` file. |
| 52 | * `Application/`: Example application code, in the package |
| 53 | `org.chromium.customtabsclient`. This code uses the latest version of the Chrome Custom Tabs, |
| 54 | contained in the module `customtabs`. |
| 55 | |
| 56 | ## UI Customization |
| 57 | |
| 58 | UI customization is done through the methods exposed by |
| 59 | `CustomTabsIntent.Builder`. |
| 60 | |
| 61 | **Example:** |
| 62 | ```java |
| 63 | CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); |
| 64 | builder.setSession(session); |
| 65 | builder.setToolbarColor(Color.BLUE); |
| 66 | // Application exit animation, Chrome enter animation. |
| 67 | builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); |
| 68 | // vice versa |
| 69 | builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right); |
| 70 | |
| 71 | CustomTabsIntent customTabsIntent = builder.build(); |
| 72 | customTabsIntent.launchUrl(this, packageName, url); |
| 73 | ``` |
| 74 | |
| 75 | In this example, no UI customization is done, aside from the animations and the |
| 76 | toolbar color. The general usage is: |
| 77 | |
| 78 | 1. Create an instance of `CustomTabsIntent.Builder` |
| 79 | 2. Build the UI using the methods of `CustomTabsIntent.Builder` |
| 80 | 3. Call `CustomTabsIntent.Builder.build()` |
| 81 | 4. Call `CustomTabsIntent.launchUrl()` |
| 82 | |
| 83 | The communication between the custom tab activity and the application is done |
| 84 | via pending intents. For each interaction leading back to the application (menu |
| 85 | items and action button), a |
| 86 | [`PendingIntent`](http://developer.android.com/reference/android/app/PendingIntent.html) |
| 87 | must be provided, and will be delivered upon activation of the corresponding UI |
| 88 | element. |
| 89 | |
| 90 | ## Navigation |
| 91 | |
| 92 | The hosting application can elect to get notifications about navigations in a |
| 93 | Custom Tab. This is done using a callback extending |
| 94 | `android.support.customtabs.CustomTabsCallback`, that is: |
| 95 | |
| 96 | ```java |
| 97 | void onNavigationEvent(int navigationEvent, Bundle extras); |
| 98 | ``` |
| 99 | |
| 100 | This callback is set when a `CustomTabsSession` object is created, through |
| 101 | `CustomTabsSession.newSession()`. It thus has to be set: |
| 102 | |
| 103 | * After binding to the background service |
| 104 | * Before launching a URL in a custom tab |
| 105 | |
| 106 | The two events are analogous to `WebViewClient.onPageStarted()` and |
| 107 | `WebViewClient.onPageFinished()`, respectively (see |
| 108 | [WebViewClient](http://developer.android.com/reference/android/webkit/WebViewClient.html)). |
| 109 | |
| 110 | ## Optimization |
| 111 | |
| 112 | **WARNING:** The browser treats the calls described in this section only as |
| 113 | advice. Actual behavior may depend on connectivity, available memory and other |
| 114 | resources. |
| 115 | |
| 116 | The application can communicate its intention to the browser, that is: |
| 117 | * Warming up the browser |
| 118 | * Indicating a likely navigation to a given URL |
| 119 | |
| 120 | In both cases, communication with the browser is done through a bound background |
| 121 | service. This binding is done by |
| 122 | `CustomTabClient.bindCustomTabsService()`. After the service is connected, the |
| 123 | client has access to a `CustomTabsClient` object, valid until the service gets |
| 124 | disconnected. This client can be used in these two cases: |
| 125 | |
| 126 | * **Warmup**: Warms up the browser to make navigation faster. This is expected |
| 127 | to create some CPU and IO activity, and to have a duration comparable to a |
| 128 | normal Chrome startup. Once started, Chrome will not use additional |
| 129 | resources. This is triggered by `CustomTabsClient.warmup()`. |
| 130 | * **Hint about a likely future navigation:** Indicates that a given URL may be |
| 131 | loaded in the future. Chrome may perform speculative work to speed up page |
| 132 | load time. The application must call `CustomTabsClient.warmup()` first. This |
| 133 | is triggered by `CustomTabsSession.mayLaunchUrl()`. |
| 134 | |
| 135 | **Example:** |
| 136 | ```java |
| 137 | // Binds to the service. |
| 138 | CustomTabsClient.bindCustomTabsService(context, packageName, new CustomTabsServiceConnection() { |
| 139 | @Override |
| 140 | public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) { |
| 141 | // mClient is now valid. |
| 142 | mClient = client; |
| 143 | } |
| 144 | |
| 145 | @Override |
| 146 | public void onServiceDisconnected(ComponentName name) { |
| 147 | // mClient is no longer valid. This also invalidates sessions. |
| 148 | mClient = null; |
| 149 | } |
| 150 | }); |
| 151 | |
| 152 | // With a valid mClient. |
| 153 | mClient.warmup(0); |
| 154 | |
| 155 | // With a valid mClient. |
| 156 | CustomTabsSession session = mClient.newSession(new CustomTabsCallback()); |
| 157 | session.mayLaunchUrl(Uri.parse("https://www.google.com"), null, null); |
| 158 | |
| 159 | // Shows the Custom Tab |
| 160 | builder.build().launchUrl(context, packageName, Uri.parse("https://www.google.com")); |
| 161 | ``` |
| 162 | |
| 163 | **Tips** |
| 164 | |
| 165 | * If possible, issue the `warmup` call in advance to reduce waiting when the |
| 166 | custom tab activity is started. |
| 167 | * If possible, advise Chrome about the likely target URL in advance, as the |
| 168 | loading optimization can take time (requiring network traffic, for instance). |