Chrome Custom Tabs の基本的な使い方


先日 *support library v23* がリリースされましたが、新しく *Custom Tabs* というライブラリが追加されていました。これはGoogle I/O の時に *Chrome Custom Tabs* として発表されていたもので、今回リリースされるまですっかり忘れていました…。

* [Chrome Custom Tabs](https://developer.chrome.com/multidevice/android/customtabs)

このCustom Tabsですが、アプリとChromeブラウザとの連携をよりスムーズにするためのAPIです。

今までは自アプリからURLをオープンする場合、別アプリのChrome（などのブラウザ）が表示されていました。このAPIを使用することで、自分のアプリの1画面のようにChromeを表示することができます。

イメージは次のようになります。

![video](http://i.imgur.com/dp5z4lm.gif)

アプリ内の画面内のリンクをクリックし、Chromeでページを表示していますが、アプリ内のアクティビティを起動するような感じで立ち上がりました。また、切り替わってもテーマカラーが一貫した色になっています。

アプリ内でWebブラウザと一部連携する際、Custom Tabsを使用することで没入感を損なわずに遷移することができます。
（遷移のアニメーションをカスタマイズできるので、スライドアニメーションなどを使用するとより良くなるかもしれません）

このような単純な連携以外にもChromeのサービスにアクセスできるようです。

上記のリンクをハンドルする以外にもOAuth認証にも使えるのでは? と試してみたので、その結果も含め使い方をまとめておきたいと思います。

2015/9/7 追記
GithubのトレンドにCustom Tabsを使用したサンプルが上がっておりました。こちらも参考になるかと思います。

* [Tabby](https://github.com/hitherejoe/Tabby)

## Custom TabsでChromeと連携

以下はChromeとの連携の流れになります。Intentベースで簡単に実装できます。
※ 連携のためには **Chrome v45** 以上(2015/08/17現在 beta版 or Dev版)が端末にインストールされている必要があります。

### ライブラリ導入

dependencesに次の行を追加します。

``` groovy
compile 'com.android.support:customtabs:23.2.1'
```

* [Support Library v23.2.1]
(http://developer.android.com/intl/ja/tools/support-library/features.html#custom-tabs)

2015/09/07 v23.0.1からminSDkVersionが15になりました。

### CustomTabsIntent

Chromeと連携するには通常のアクティビティ遷移と同じくIntentを使用します。
その際、ツールバーの外観やアクションなどの情報を設定することで、遷移先のChromeをカスタマイズすることができます。そのためのユーティリティクラス `CustomTabsIntent.Builder` がライブラリに用意されていますので使用すると簡単です。

#### Builderの設定項目

|メソッド|補足|
|-------|---|
|#setShowTitle(boolean)| webページのタイトルを表示するかどうか|
|#setToolbarColor(int)|ツールバーの色|
|#setStartAnimations(Context, int, int)| Chrome表示時のアニメーション|
|#setExitAnimations(Context, int, int| Chrome非表示時のアニメーション|
|#setCloseButtonIcon(Bitmap)| 閉じるボタンのアイコン。デフォルトでは「×」ボタン|
|#setActionButton(Bitmap, String, PendingIntent)|ツールバー上アクション。アイコンはw24xh24-48dp|
|#addMenuItem(String, PendingIntent)|メニュー内のアクション。複数可|
|#enableUrlBarHiding()|スクロールに合わせてツールバーを非表示にする。但し、 **ボトムツールバーは固定**|
|#addDefaultShareMenuItem()|オーバーフローメニューに共有アクションを追加|
|#addToolbarItem(int, Bitmap, String, PendingIntent)|ボトムツールバーにアイテムを追加。第一引数のIdは0-5まで指定可能(ToolbarItemは最大5つまで)。|
|#setSecondaryToolbarColor(@ColorInt Int)|ボトムツールバーの色|

#### ツールバーの外観

アプリに合わせて変える際ツールバーの外観を変える場合は次のようになります。上記のgifのようにChromeに切り替わってもプライマリーカラーが使用されるようになります。

```java
final CustomTabsIntent tabsIntent = new CustomTabsIntent.Builder()
        .setShowTitle(true)
        .setToolbarColor(ContextCompat.getColor(getActivity(), R.color.primary))
        .setStartAnimations(getActivity(), R.anim.slide_in_right, R.anim.slide_out_left)
        .setExitAnimations(getActivity(), android.R.anim.slide_in_left, android.R.anim.slide_out_right);
        .build();
        
// Chromeの起動        
tabsIntent.launchUrl(getActivity(), uri);
```

最後の `#lauchUrl(Activity, Uri)` を呼ぶとChromeが起動します。対応Chromeがインストールされていない場合は、それ以外のブラウザが起動します。

#### ツールバーのアイコン

次にクローズボタンと共有アクションボタンを追加してみます。

``` java
final Intent intent = new Intent(Intent.ACTION_SEND)
                            .setType("text/plain")
                            .putExtra(Intent.EXTRA_TEXT, uri.toString());
final PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, 0);
// アイコンはマテリアルアイコン(24dp)
final Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_action_share);

final CustomTabsIntent tabsIntent = new CustomTabsIntent.Builder()
        .setCloseButtonIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back))
        .setActionButton(icon, getString(R.string.action_share), pendingIntent)
        .build();
```

結果は次のようになります。クローズボタンを変更するとアプリ内の一画面のようになりますね。
![Imgur](http://i.imgur.com/HoeEjSH.jpg)

#### ツールバーのメニュー

最後に折りたたみメニュー内にアクションを追加します。

```java
// pendingIntentは使い回し
final CustomTabsIntent tabsIntent = new CustomTabsIntent.Builder()
        .addMenuItem(getString(R.string.action_share), pendingIntent)
        .build();
```

結果は次のようになります。
![Imgur](http://i.imgur.com/f8XJvDs.jpg)


#### ボトムツールバーのカスタマイズ

v23.2.0からボトムツールバーにアイテムを追加できるようになりました。また、それに合わせてボトムツールバーの色も指定できるになっています。
追加できるアイテムは最大5個までです。それ以上を追加すると例外をスローします。

```java
// pendingIntentは使い回し
final CustomTabsIntent tabsIntent = new CustomTabsIntent.Builder()
        .setSecondaryToolbarColor(ContextCompat.getColor(getContext(), android.R.color.holo_red_dark))
        .addToolbarItem(1, icon, "Share", pendingIntent)
        .addToolbarItem(2, icon, "Share", pendingIntent)
        .addToolbarItem(3, icon, "Share", pendingIntent)
        .addToolbarItem(4, icon, "Share", pendingIntent)
        .addToolbarItem(5, icon, "Share", pendingIntent)                                
        .build();
```

結果は次のようになります。

![Imgur](http://i.imgur.com/WTkFSRt.jpg)

#### 補足

<del>ExitアニメーションはChrome画面で戻るボタンやクローズボタンを押した場合は適用されません。</del>
2015/09/07 AndroidM Preview3の場合アニメーションが適用されないが正しいです。
Android5.x端末でアニメーションすることを確認できました。

アクションやメニューは連携前にPendingIntentを作る仕様上表示ページのURLに対する処理に限定されそうです。

`CustomTabsIntent` は内部で `Intent#putExtra(...)` を呼んでいるだけなので、ドキュメントやソースコードを見ればキーと値がわかりますので、使用しなくでもChromeとの連携はできますが、使用したほうが簡単に実装できると思います。

## プリフェッチ

Custom Tabs起動前にプリフェッチすることでページの表示を高速化することができます。
プリフェッチの方法は以下の記事を参照下さい。

* [Chrome Custom Tabs を試してみる -プリフェッチ編-](http://qiita.com/droibit/items/d7607bbeeba77c502e94)


## OAuth認証

前置きが長くなりました。
Custom Tabsを使用してOAuthを試した結果ですが **上手くいきませんでした…。** 

作成したサンプルアプリでは、PocketとGithubのOAuthを試しました。
ともに初回の認証は成功するのですが、二回目以降の認証が失敗するという結果でした。

ソースコードはGithubにあります。

* [Github](https://github.com/droibit/customtabsoauth)

※ 動作させる際はPocketとGithubのAPIを使用するためのAPIキーは自身のものを使用していただく必要があります。


#### 認証結果

次の画像はPocketのOAuthのものになります。認証が成功し、AccessTokenとユーザ名が取れました（結果の表示はユーザ名のみですが）。

[初回認証](http://i.imgur.com/ZFjAR2u.jpg)

二回目以降だとリクエストトークン取得後、ブラウザでアカウント認証する際、遷移先のChromeの画面が真っ白なままという状態でした。
既に認証済みになっている状態で、再度認証しようとしているので何か上手くいかないのではないかと思います…。

Chromeアプリを使用したOAuthの場合は、二回目以降もリダイレクト用コールバックURIが呼ばれていたのですが、`CustomTabsIntent` 経由の場合うまく行かないようですね。

WebViewだとセキュリティ上よろしくないので、OAuthの際はChromeを使用しておりイケるかもと思っていたのですが、現在道半ばです。上手く行く方法を探して行きたいと思います。


当初の目的は未達成ですが Custom Tabs の連携方法がわかったので簡単にですがまとめてみました。

> Written with [StackEdit](https://stackedit.io/).