Android クイック設定タイル開発 基礎の基礎の基礎

# はじめに
- 簡単なAndroidアプリを作りたく、クイック設定タイル(以下,タイル)を使おうとしました。
- 基礎練習として、デフォルトのタイルの並びに自作タイルを追加し、自作タイルの適当なライフサイクルでログを出力するだけのアプリを製作しました。
- クイック設定タイル開発については、その筋の人々にとってあまりにも簡単すぎるのか、それともクイック設定タイルを作ろうなんて奴はいないのか、ググっても情報が少なく、はまったポイントがあったため、備忘録と情報共有を兼ねて記事にします。
- ド初心者向けのタイルのHello Worldのつもりです。

# クイック設定タイルとは
Androidスマホを上からスワイプしたときに降りてくる、通知の上に並んでいるボタンたちのことです。
設定アプリを開かずに、素早くWi-Fiの接続先を選択したり、BluetoothをON/OFFしたりできるあれです。

# 製作手順
1. TileServiceを継承したクラスを作ります。
1. AndroidManifest.xmlにて、1で作ったクラスをTileの仲間に入れてくれるようにお願いします。

以上、たった2ステップでとりあえず動くものができます。

# TileServiceを継承したクラス
```Kotlin:BrightnessSwitchTileService.kt
class BrightnessSwitchTileService: TileService() {
    override fun onClick() {
        super.onClick()
        Log.d("BSTS","onClick")
    }

    override fun onStartListening() {
        super.onStartListening()
        Log.d("BSTS","onStartListening")
        val qsTile = getQsTile()
        qsTile.state = Tile.STATE_ACTIVE
        qsTile.updateTile()
    }

    override fun onStopListening() {
        super.onStopListening()
        Log.d("BSTS","onStopListening")
    }
}
```

**簡単な説明**
- クラス名 *BrightnessSwitchTileServise*
最終的に、画面輝度を変更するタイルを作りたいのでこの名前にしましたが任意です。
<br></br>
- *onClick*
タップ時に呼ばれます。
<br></br>
- *onStartListening*
上からスワイプして、タイルが降りてきたときに呼ばれます。
<br></br>
- *onStopListening*
タイルが画面から消えた時に呼ばれます。
<br></br>
- *qsTile*
名前は任意です。 *getQsTile()* でタイルそのものを取得し、プロパティを読み書きして、タイルの状態を変更できます。
プロパティは、state(タップできる、かつ、設定ON・タップできる、かつ、設定OFF・タップできない、の3通り)の他にも、label(表示される文字)やicon(そのままアイコン)などが変更可能です。
プロパティを変更しただけでは、タイルそのものは変わらず、 *updateTile()* を呼ぶことで反映されます。

**実装時のおおまかな流れ**
Bluetoothのタイルを例に説明します。
1. *onStartListening* 内で、タイルの初期設定を行う。(このアプリにBluetoothのON/OFFを切り替える権限があるかチェック。権限があればさらに現在ONかOFFかチェック。結果に合わせてstateをset。)
1. *onStartListening* 内で、タイルタップによって変更される状態(BluetoothがONかOFFか)を監視して、変わったときにタイルのstateを変更するやつ(ContentObserverで調べてください)を起動する。
1. *onClick* で、タップした時の動作を指定する。(BluetoothのON/OFF切り替え処理)
1. *stopListening* 内で、状態が変わったときにタイルのstateを変更するやつを停止する。

以上

# AndroidManifest.xmlの修正
*AndroidManifest.xml* に作ったTileServiceを追加します。
```xml:AndroidManifest.xml
        <service
            android:name=".BrightnessSwitchTileService"
            android:exported="true"
            android:label="@string/default_tile_label"
            android:icon="@drawable/baseline_light_mode_24"
            android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
            <intent-filter>
                <action android:name="android.service.quicksettings.action.QS_TILE" />
            </intent-filter>
        </service>
```

nameは作ったTileServiceのクラス名です。

私はここでしばらくはまりました。
ポイントは **exported** です。
Android Developersにタイルを作る場合の大まかな手順があり、このブロックもほぼ丸コピですが、コピー元には **exported** がないのです。
Andoroid 12より、この場合 **exported** が必須となっており、明記しない場合はコンパイル不能、falseではコンパイルは通るが実行すると *java.lang.SecurityException: Not allowed to bind to service Intent* を吐いて落ちるという挙動となります。明記が必須となったことは知っていましたが、 **exported** で指定するところの「他のアプリから起動できる/できない」とはどういうことか、を理解していなかったため、falseにしていました。
タイルは、「タイルを管理する他のアプリ」がこの自作サービスを起動する形のため、 **exported** が **true** でなくてはならないというオチでした。

# 動作確認
ここまで来たらとりあえず動くと思います。
タイルを追加、タップしてログが出ることを確認しましょう。

# 参考文献
- [TileService | Android Developers](https://developer.android.com/reference/kotlin/android/service/quicksettings/TileService)
- [Tile | Android Developers](https://developer.android.com/reference/kotlin/android/service/quicksettings/Tile)