Jetpack Compose, ExoPlayer3, HLSでAndroid TV用のアプリを作成した

# はじめに
いままでAndroidの開発したことなかったけど勉強したらできたので記録を残しておく。
今回ターゲットはAndroid 10(APILevel 29)で、この方法でできました。
# ソースとできたもの

ソース
https://gitlab.com/mkiuchi/exoplayer-demo

できたもの
<iframe width="560" height="315" src="https://www.youtube.com/embed/DtZ8qquX6lM?si=JQbrcx0PSOoqh4Tg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# やったこと順番に

1. [Android Studio入れる](https://developer.android.com/studio)
1. ["Compose を用いた Android アプリ開発の基礎"](https://developer.android.com/courses/android-basics-compose/course) を全部やる
1. [Android TV スタートガイド - TVアプリの作成を開始する](https://developer.android.com/training/tv/get-started)
1. 作ってみてハマって直して動かす

# 役に立ったこと

## ["Compose を用いた Android アプリ開発の基礎"](https://developer.android.com/courses/android-basics-compose/course) は最高のトレーニング

今まで受けてきたトレーニングの中でピカイチ。GoogleのAndroidチームは最高の仕事をしたと思う。まじで上から順番に、ラボや演習も横着せずに全部やったほうがいい。
特に勉強になったのが ["関心の分離" と "単方向データパターン"](https://www.youtube.com/watch?v=AfCzIEwt_i4)。JetPack ComposeとReactは作り方が似てるので、この考え方はReactにも応用できるかも知れない。

["インターネットからデータを取得する"](https://developer.android.com/courses/pathways/android-basics-compose-unit-5-pathway-1)からコルーチンとリポジトリ層が出てくるので頭から煙が出てくる。このあたりから私は順番にやらずに回答コードを見ながら解説を追いかけるようにした。

私の場合は土日がっつり+平日夜ちょこちょこで、だいたい1ヶ月くらいかかった。

# ハマったこと

## JetPack Compose + Exoplayer3 + HLS の作例がほとんどないのでよくわからなかった

自分としては上のコードになった。たぶんいろいろやり方があるのかもしれない。

ボイラープレートから変更してるファイルは以下のもの。
- AndroidManifest.xml
    - `<uses-permission android:name="android.permission.INTERNET" />` と `android:usesCleartextTraffic="true"` の追加
- build.gradle.kts(:app)
    - ExoPlayer 関連のモジュールの追加
- MainActivity.kt

ぐぐるとViewをXMLで作って`findById`で読み込んで・・・という例が多いが、結論としては必要なかった。

## 動画は再生されてるっぽいが音だけ出て画面が真っ黒or真っ白になって出てこない

Root Composableがデフォルトだと Surface になってるのだがそれだとうまくいかなかった。以下のように Box に変更したら動いた。

```kotlin:修正前
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ExoplayerdemoTheme {
                Surface (
                    modifier = Modifier.fillMaxSize(),
                    shape = RectangleShape
                ) {
                    Greeting(
                        modifier = Modifier
                            .background(Color.Transparent)
                    )
                }
            }
        }
    }

```
```kotlin:修正後
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ExoplayerdemoTheme {
                Box (
                    modifier = Modifier.fillMaxSize()
                        .background(Color.Black),
                ) {
                    Greeting(
                        modifier = Modifier
                            .background(Color.Transparent)
                    )
                }
            }
        }
    }

```

[Surface](https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary?hl=ja#Surface(androidx.compose.ui.Modifier,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.foundation.BorderStroke,androidx.compose.ui.unit.Dp,kotlin.Function0)) と [Box](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Box(androidx.compose.ui.Modifier)) の違いはよくわからない。（Material Themeに対応してるかどうかの違いのようだけど。まだ Android 力が足りない）

## HLSの再生には追加パッケージが必要

ドキュメントの通り。

https://developer.android.com/media/media3/exoplayer/hls

パッケージない場合だと [MPEG Dash](https://en.wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP) の対応になるみたい

# さいごに

Android開発は昔よりも簡単になった気がする