EmojiCompatでAndroid絵文字サポート

## [Emoji Compatibility](https://developer.android.com/guide/topics/ui/look-and-feel/emoji-compat)

[EmojiCompat](https://developer.android.com/reference/android/support/text/emoji/EmojiCompat)サポートライブラリを使って、古い端末(Android 4.4までサポート)でもtofu ☐ にならないようにする事ができます。

### :smile: EmojiCompatの仕組み

- `EmojiCompat` で使用する絵文字用のフォントはダウンロード又はアプリにバンドルして使える
- `EmojiCompat`は、指定されたCharSequenceの絵文字を識別し、必要に応じて
それらを `EmojiSpans` に置き換えて、最後に絵文字をレンダリングする

![image1.png](https://qiita-image-store.s3.amazonaws.com/0/299271/880e184d-1622-efba-aff9-e924bf95dc33.png)

今回は絵文字用のフォントをダウンロードして使えるようにしたいと思います。

###  :computer:環境構築
****

`app/build.gradle` に以下を追加します。

```gradle
implementation 'com.android.support:support-emoji:28.0.0'
```

事前に以下の準備を行います。

1. [Downloadable Fonts](https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts)を使用してフォントをダウンロードできるようにする
    - AndroidStudioを使ってダウンロードフォントを指定する
        - layout editorから適当なtextviewを選択
        - サイドメニューの「textAppearance」>「fontFamily」で「More Fonts.」を選択
<img width="246" alt="image2.png" src="https://qiita-image-store.s3.amazonaws.com/0/299271/c9bd0e79-78f4-2c1f-ec81-83672d229d70.png">


        - 「Noto」で検索し、Font Nameが`noto_color_emoji_compat`のフォントを検索
            <img width="424" alt="image3.png" src="https://qiita-image-store.s3.amazonaws.com/0/299271/e0153e9a-ff0c-21fb-cbd8-024ab928c96e.png">

        - `Create downloadable font` を選択し「OK」
        - すると `res/font` に以下のようなxmlと`res/values`配下に認証情報とプリロード用のファイルが作成される

            ```xml
            <?xml version="1.0" encoding="utf-8"?>
            <font-family xmlns:app="http://schemas.android.com/apk/res-auto"
                    app:fontProviderAuthority="com.google.android.gms.fonts"
                    app:fontProviderPackage="com.google.android.gms"
                    app:fontProviderQuery="Noto Color Emoji Compat"
                    app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
            </font-family>
            ```
2. 準備ができたので、`Application`クラスの`onCreate`でDownloadable Fontsを使う為にリクエストを送る実装を追加

    ```kotlin
            val fontRequest = FontRequest(
                "com.google.android.gms.fonts",
                "com.google.android.gms",
                "Noto Color Emoji Compat",
                R.array.com_google_android_gms_fonts_certs
            )
            val config = FontRequestEmojiCompatConfig(applicationContext, fontRequest)
            EmojiCompat.init(config)
    ```
### :pencil: 実装
****

#### 簡単なサンプル

まずは絵文字をログに出力させてみたいと思います。

```kotlin
       // Downloadable Fontsを使う為に準備
        val fontRequest = FontRequest(
            "com.google.android.gms.fonts",
            "com.google.android.gms",
            "Noto Color Emoji Compat",
            R.array.com_google_android_gms_fonts_certs
        )
        val config = FontRequestEmojiCompatConfig(applicationContext, fontRequest)
        EmojiCompat.init(config)
        // 初期化が終わったタイミングで絵文字をログに出力
        EmojiCompat.get().registerInitCallback(object : EmojiCompat.InitCallback() {
            override fun onInitialized() {
                super.onInitialized()

                val text = EmojiCompat.get().process("\uD83E\uDD23")
                Log.d(TAG, "emoji: $text") 
            }
        })
```
出力結果
![image4.png](https://qiita-image-store.s3.amazonaws.com/0/299271/0f8a1f47-1a49-2363-e417-826292714623.png)
※AndroidStudioのLogcatではMacの絵文字が表示されるので注意

#### [EmojiAppCompatTextView](https://developer.android.com/reference/android/support/text/emoji/widget/EmojiAppCompatTextView) を使う

今度は絵文字用にカスタムされたViewを使って絵文字を表示したいと思います。
レイアウトファイルで`TextView`の代わりに`EmojiTextView`を使います。

```xml
    <androidx.emoji.widget.EmojiTextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
```
あとは通常のTextViewと同じように絵文字も含んだ文字列を設定します。


```kotlin
textView.text = "\uD83E\uDD23"
```
![image5.png](https://qiita-image-store.s3.amazonaws.com/0/299271/f32c6f6a-d03a-e9f0-f81e-8f2485502fe4.png)
ちゃんと表示されました :sparkles: 

###  :bomb: バッドノウハウ
****

`IllegalStateException: Not initialized yet` が出る場合。

```
     Caused by: java.lang.IllegalStateException: Not initialized yet
        at androidx.core.util.Preconditions.checkState(Preconditions.java:131)
        at androidx.emoji.text.EmojiCompat.process(EmojiCompat.java:742)
        at androidx.emoji.text.EmojiCompat.process(EmojiCompat.java:702)
        at androidx.emoji.text.EmojiCompat.process(EmojiCompat.java:666)
        at androidx.emoji.text.EmojiCompat.process(EmojiCompat.java:634)
```
初期化されてない状態で使おうとしているので `registerInitCallback` で初期化後に
処理を行うようにする。