Pixel Tablet のエミュレータで自分の壁紙アプリを分割画面に置いた瞬間、横に間延びした桜の写真が目に入りました。縦 9:16 で美しく見えるよう選んだ一枚です。胸のあたりが少しざわつきました。
Android 16 から、最小幅 600dp 以上の大画面では、アプリが指定した画面の向き・アスペクト比・リサイズ制限をシステムが無視するようになっています。Android 16 には互換性のための暫定オプトアウトが残されていましたが、この夏に Pixel から配信が見込まれる Android 17 で、その猶予も終わります。
個人開発で運営している Android の壁紙アプリは4本。どれも「縦固定」を前提に長く育ててきました。iOS 側では先日、10年前の課金コードを StoreKit 2 へ移し替える作業を済ませたばかりですが、今度は Android 側の番のようです。
タブレットやフォールドからのインストールは決して多数派ではありません。それでも Play Console の統計を眺めると、少しずつ、確実に増えています。目を逸らすには Android 17 の配信は近すぎると感じ、今週、Antigravity のエージェントと棚卸しから始めました。
Android 17 で何が無視されるようになるのか
対象は最小幅 600dp 以上のディスプレイです。スマートフォンサイズの画面では従来どおり動きます。無視されるのは、おおまかに言えば「アプリが画面の形を決めつけるための指定」の全般です。
- マニフェストの
android:screenOrientation="portrait"などの向き固定 android:resizeableActivity="false"によるリサイズ拒否android:maxAspectRatio/android:minAspectRatioのアスペクト比制限- 実行時の
setRequestedOrientation()呼び出し
Android 16 では、マニフェストに互換プロパティを書くことで従来の挙動を一時的に維持できました。
<application>
<!-- Android 16 の暫定オプトアウト。Android 17 では効力を失う予定です -->
<property
android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"
android:value="true" />
</application>私はこのプロパティを「時間を買うための一行」と捉えています。入れておけば当面の表示の乱れは防げますが、期限付きです。買った時間のうちに本体を直す前提で4本すべてに入れ、同じ日に棚卸しを始めました。
「壁紙は縦で使うもの」という前提のどこが壊れるのか
コードを直す前に、壊れる場所を具体的に知る必要があります。壁紙アプリの場合、影響はレイアウトの乱れだけではありませんでした。
最初に挙がったのがプレビューの信頼性です。壁紙アプリのプレビューは「設定したらこう見えます」という約束ですが、横長ウィンドウいっぱいに centerCrop で描画すると、実際にホーム画面へ設定した結果とまったく違う絵になります。レイアウトとしては正常に見えるのに、約束だけが静かに破られる。これが一番怖い壊れ方でした。
次に切り出し計算です。古いコードは Display.getSize() で得た画面サイズから切り出し範囲を計算していました。分割画面ではこの値がウィンドウサイズになり、壁紙のターゲット寸法とずれます。
残りは比較的素直な問題です。固定2列のグリッド一覧が大画面で間延びすること。フォールドの開閉で Activity が再生成され、スクロール位置と選択状態が消えること。
棚卸しを Antigravity エージェントに任せた手順
4本ぶんのリポジトリを順に開いて目視する気力はなかったため、検出はエージェントに任せました。依頼した内容は3点です。
- マニフェストの
screenOrientation/resizeableActivity/maxAspectRatio指定の一覧化 Display.getSize()・defaultDisplay・ハードコードされた 9:16 計算の検出- それぞれに「制限が無視されたとき何が起きるか」の推測コメントを添えること
エージェントが実行していたのは、要するにこの2行に相当する走査です。
grep -rn "screenOrientation\|resizeableActivity\|maxAspectRatio" \
--include=AndroidManifest.xml .
grep -rn "defaultDisplay\|getSize(\|9f / 16f" \
--include="*.kt" --include="*.java" app/src/結果は4本合計で、縦固定の Activity が 14、getSize() 依存の計算が 6 箇所、アスペクト比のハードコードが 3 箇所。一覧が揃うまで 40 分かかりませんでした。
ただ、ここで手応えと一緒に限界も見えます。検出と推測まではエージェントの得意分野ですが、「どの画面を直し、どの画面は固定のままで許容するか」は、結局 1 件ずつ私が画面を思い浮かべて決めることになりました。たとえば設定画面の縦固定は実害がないため残すと判断し、プレビューと切り出しは作り直すことにしました。推測コメントは、その判断を始めるための下書きとしてちょうどよい粒度でした。
プレビューの「正しさ」をウィンドウの形から切り離す
今回の対応で一番考えたのはここです。直し方は2つありえました。ウィンドウの形に合わせてプレビューも変形させるか、ウィンドウとは独立した「壁紙としての見え方」を描き続けるか。
私は後者を選びました。壁紙のプレビューが約束すべきなのはウィンドウ内での見栄えではなく、ホーム画面に設定された後の姿だからです。
実装の軸は、基準寸法をウィンドウではなく WallpaperManager から取ることです。
val wm = WallpaperManager.getInstance(context)
val metrics = windowManager.currentWindowMetrics
// 壁紙のターゲット寸法。ウィンドウの大きさとは独立に決まります
val targetW = wm.desiredMinimumWidth
.takeIf { it > 0 } ?: metrics.bounds.width()
val targetH = wm.desiredMinimumHeight
.takeIf { it > 0 } ?: metrics.bounds.height()
val targetAspect = targetW.toFloat() / targetHプレビューはこのアスペクト比の枠で描画し、ウィンドウにはレターボックスで収めます。Compose なら中央寄せの Box に aspectRatio を渡すだけで成立します。
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
AsyncImage(
model = photo.url,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.aspectRatio(targetAspect)
)
}切り出し範囲も同じ targetW / targetH から計算するため、分割画面で操作してもプレビューと設定結果が一致します。横長ウィンドウでは左右に余白が生まれますが、「設定後の姿を正しく見せる」ためのコストとしては安いものだと考えています。
検証を一回きりにしない
直した直後は誰でも動作確認をします。問題は、次のリリースでまた乱れていないかです。
検証はエミュレータのリサイズ可能デバイスを使い、代表的なウィンドウ状態を adb で再現してスクリーンショットを残す、という素朴な形にしました。
adb shell wm size 1080x2424 # スマートフォン相当
adb exec-out screencap -p > phone.png
adb shell wm size 2208x1840 # フォールド展開相当
adb exec-out screencap -p > unfolded.png
adb shell wm size resetこの往復はエージェントに任せ、画像の比較は私が目で見ています。私自身、差分検知までの自動化も考えましたが、壁紙アプリの「正しさ」は最終的に絵の印象なので、ここに人間が残るのは妥当だと感じています。
フォールド開閉の再生成では、選択中の画像が消える退行が一つ見つかりました。rememberSaveable への置き換えで解消しています。棚卸しの一覧になかった問題が検証で出てくるあたり、静的な走査と実機相当の検証は別物だと改めて思います。
次のアクション
同じ状況の方は、まず手元のリポジトリで先ほどの grep 2行を実行してみてください。縦固定の Activity がいくつあるか数字で見えるだけで対応の規模感が定まり、気持ちが軽くなります。そのうえで暫定オプトアウトで時間を買い、プレビューのような「アプリの約束」に関わる画面から順に直していく。私の場合、この順番で迷いが消えました。
縦固定は「画面の形を考えなくていい自由」を長いあいだ与えてくれました。その期限が来ただけだと捉えています。画面の形が増えることは、壁紙アプリにとって本来、見せ場が増えることでもあるはずです。同じように縦前提のアプリを抱えている方の参考になれば幸いです。