targetSDK35に移行した時に気をつけたこと

targetSDKを34から35に移行しました。
Edge-to-edge対応を中心として思ったよりも対応することが多かったので、こちらにメモとして残します。

# 1. ライブラリのアップデート

まずは当然targetSDKのアップデート。

```build.gradle
android {
    compileSDKVersion = 35
    defaultConfig {
        targetSdkVersion 35
    }
}
```

その後、targetSDKをアップデートしないと上げられなかったライブラリ関連をアップデートしました。

私のプロジェクトでは

* androidx.activity：1.10〜
* androidx.core：1.15〜
* androidx.recyclerview：1.4〜
* androidx.work：2.10〜

あたりが対象でした。
こちらによるコードの変更点などは特にありませんでした。

# 2. Bitmapの修正

`Bitmap.createBitmap`関数の第3引数に設定するビットマップ設定が以前はnullableだったのですが、今回からnon-nullになったようです。
そのため、デフォルト値を設定しました。

```BitmapSample.kt
val bitmap = createBitmap(size, size, source.config ?: Config.ARGB_8888)
```

デフォルト値としては特になければARGB_8888がさまざまな色彩を表現できるため適切な設定のようです。

https://developer.android.com/reference/android/graphics/Bitmap.Config#ARGB_8888

# 3. Edge-to-edge対応

いよいよEdge-to-edge対応です。
やはりというか、こちらがなかなか大変でした。
私のプロジェクトではXMLビューが残っていたため、それぞれの対応を分けて記載します。

## XMLビュー

結論から言いますと、XMLビューについてはEdge-to-edge対応は諦めました。
なぜなら、以下のページのような問題が発生してしまっていたためです。

https://blog.cybozu.io/entry/2024/10/21/080000

簡単に言えば、システムバー分の余白を設定すると、その部分が白くなってしまい、色の変更ができないという問題です。
`window.statusBarColor`を設定しても、targetSDK35以降ではこのAPIは廃止されてしまっており、34以下でないと効力を発揮しません。
私のプロジェクトでは`window.statusBarColor`を設定しているXMLビューによるActivityが多く、アクションバーの移行もすぐに対応するのが難しい状態でした。
そのため、今回は該当のActivityのテーマに以下を設定し、Edge-to-edgeを無視して従来通りの表示をするようにしました。

```styles.xml
<item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item>
```

ただし上記のページでも言及されている通り、今後廃止される予定のものになります。
現在既存のXMLビューをJetpack Compose化していっている途中のため、急ぎ対応を進めなければならないところです。

## Jetpack Compose

続いてJetpack Composeです。
Material3を使用していれば細かい対応はいらずインセットを自動適用してくれるらしいですが、私のはMaterial2なので個別に対応が必要でした。
私のプロジェクトの多くのページでは、画面のベースとして共通して以下のようなScaffoldを使用しています（一部コードは省いています）

```MyScaffold.kt
@Composable
fun MyScaffold(
    topBar: @Composable () -> Unit,
    content: @Composable () -> Unit
) {
    MaterialTheme {
        Box {
            Scaffold(
                topBar = {
                    topBar()
                }
            ) { innerPadding ->
                Box(modifier = Modifier.padding(innerPadding)) {
                    content()
                }
            }
        }
    }
}
```

余白を設定するには、`Modifier.windowInsetPadding`を使用します。
余白の設定が上部のステータスバーだけなら`WindowInsets.statusBars`、下部のナビゲーションバーだけなら`WindowInsets.navigationBars`、どちらにも設定するなら`WindowInsets.systemBars`を指定します。
設定する箇所はBoxの部分です。
基本的には`WindowInsets.systemBars`を設定してしまって良いとは思いますが、私のプロジェクトではデザイン上の都合で、引数でインセットをもらうようにしました。

```MyScaffold.kt
@Composable
fun MyScaffold(
    insets: WindowInsets,
    topBar: @Composable () -> Unit,
    content: @Composable () -> Unit
) {
    MaterialTheme {
        Box(
            // 下記を追加
            Modifier.windowInsetsPadding(insets)
        ) {
            ...
        }
    }
}
```

ただし、このままだと`window.statusBarColor`で設定するような色が設定されていないので、余白部分が白くなってしまいます。
そのため、Boxに背景色も適用するようにしました。
```MyScaffold.kt
@Composable
fun MyScaffold(
    @ColorRes colorRes: Int,
    insets: WindowInsets,
    topBar: @Composable () -> Unit,
    content: @Composable () -> Unit
) {
    MaterialTheme {
        Box(
            Modifier
                // 先に背景色を適用
                .background(colorResource(colorRes))
                .windowInsetsPadding(insets)
        ) {
            ...
        }
    }
}
```

基本はこれで対応完了です！

ただし場合によってはやはりレイアウトが崩れているところもあると思います。
なのでこの後は人力で全ての画面をチェックしました。。。（UIテストがあれば楽だったのかもしれません）
上記のMyScaffoldを適用していない画面だったり、LazyColumnのリストが画面いっぱいに表示されている画面だったりは、個別に`Modifier.windowInsetsPadding`を必要なコンポーザブルに適用する必要がありました。

全画面をエミュレータだったり実機でチェックしてレイアウトに大きな崩れがないことを確認して、対応完了としました！
以上になります。