ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-06-01中級

targetSdk 36 で edge-to-edge が必須になった壁紙アプリを Antigravity で移行した運用メモ

targetSdk を 36(Android 16)に上げた途端、壁紙アプリのプレビュー画面のツールバーがステータスバーに食い込みました。opt-out が効かなくなった edge-to-edge 強制対応を、Antigravity のエージェントで複数アプリ横断に進めた数週間の運用メモです。

Android 16edge-to-edgetargetSdk 36Antigravity338個人開発89

targetSdk を 36 に上げてビルドし、いつもの壁紙プレビュー画面を開いた瞬間、上部のツールバーがステータスバーの時計に重なっていました。これまで windowOptOutEdgeToEdgeEnforcementtrue にして逃げていた箇所が、Android 16 では効かなくなっていたのです。

私は2014年から個人で iOS / Android のアプリを運用していて、壁紙や癒し系のアプリが中心です。累計5,000万ダウンロードと言うと数だけは大きく聞こえますが、その実態は「同じような構造のアプリを何本も並行で抱えている」という状態で、OS のこうした仕様変更が来るたびに、全アプリを一斉に直す作業が発生します。今回の edge-to-edge 強制は、まさにその典型でした。この記事は手順の解説というより、複数アプリを横断して直すときに Antigravity のエージェントをどこで使い、どこで自分の手で確かめたか、という運用の記録です。

なぜ「あとで」では済まないのか

Android 15(API 35)の時点で、targetSdk 35 のアプリはデフォルトで edge-to-edge になっていました。ただしこのときは windowOptOutEdgeToEdgeEnforcementtrue にすれば従来の挙動に戻せたので、私は正直この opt-out に頼って先送りしていました。

Android 16(API 36)で状況が変わります。targetSdk 36 のアプリでは、この属性が deprecated かつ無効化され、edge-to-edge を拒否できなくなりました。挙動を正確に書くと、targetSdk 36 のアプリでも「Android 15 の実機で動いている間」は opt-out がまだ効きますが、「Android 16 の実機」では無効になります。つまり端末が更新されれば、こちらが何もしなくても全画面化が降ってくるということです。

先送りできないと判断した理由はもう一つあります。壁紙アプリのプレビュー画面は、本来 edge-to-edge と相性が良いのです。画像を画面の隅まで見せたいのに、システムバーの下に余白を作っていたのは、むしろ機会損失でした。「いつか対応する課題」ではなく「今やると体験が良くなる改善」だと捉え直したことで、ようやく手が動きました。

まず opt-out を全アプリから消す

最初の作業は、逃げ道だった windowOptOutEdgeToEdgeEnforcement を全部消すことです。ここで Antigravity の Agent Mode に「リポジトリ横断でこの属性を使っている箇所を列挙して」と頼みました。themes.xml だけでなく、一部のアプリでは Activity 個別のテーマで上書きしていたので、自分の記憶だけでは取りこぼすところでした。

<!-- 削除前: これに頼っていた -->
<style name="Theme.WallpaperApp" parent="Theme.Material3.DayNight.NoActionBar">
    <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
 
<!-- 削除後: 属性ごと取り除く -->
<style name="Theme.WallpaperApp" parent="Theme.Material3.DayNight.NoActionBar">
    <!-- edge-to-edge を受け入れる前提に切り替える -->
</style>

合わせて、android:statusBarColorandroid:navigationBarColor も targetSdk 36 では deprecated です。色で塗って隠すという発想自体を捨て、バーは透過させてコンテンツを下に敷く、という方針に切り替えました。

insets を「受け取る側」に書き換える

opt-out を消すと、当然あちこちでコンテンツがシステムバーに潜り込みます。ここからが本題で、各画面が insets(システムバーの占有領域)を受け取って自分でパディングを当てる必要があります。

View ベースの古い Activity では、enableEdgeToEdge() を呼んだ上で、ルートビューに inset リスナーを付けるのが確実でした。

import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
 
class PreviewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_preview)
 
        // ツールバーだけは必ずシステムバーを避ける。
        // 壁紙そのものは全画面のまま、操作 UI に上下パディングを当てる。
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.toolbar)) { v, insets ->
            val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(v.paddingLeft, bars.top, v.paddingRight, v.paddingBottom)
            insets
        }
    }
}

ここでつまずいたのは、fitsSystemWindows="true" を XML に残したまま inset リスナーも書いてしまい、パディングが二重に入って逆に余白が広がった画面があったことです。Android 16 環境では fitsSystemWindows の指定は外し、insets の処理は一箇所に寄せるのが結局いちばん読みやすくなりました。

Jetpack Compose に移行済みの新しいアプリでは、もっと素直でした。ScaffoldinnerPadding を内側に渡すか、全画面の壁紙レイヤーには当てず、操作レイヤーにだけ safeDrawingPadding() を当てます。

Box(Modifier.fillMaxSize()) {
    // 壁紙は画面の隅まで描く(ここには insets を当てない)
    WallpaperImage(modifier = Modifier.fillMaxSize())
 
    // 操作 UI はシステムバーを避ける
    PreviewControls(
        modifier = Modifier
            .align(Alignment.TopCenter)
            .safeDrawingPadding(),
    )
}

「画像には当てず、UI にだけ当てる」というこの線引きが、壁紙アプリにおける edge-to-edge 対応の勘どころだと感じています。全部に safeDrawingPadding() を付けると、せっかくの全画面プレビューが従来と変わらなくなってしまいます。

Antigravity をどこで使い、どこで使わなかったか

複数アプリを横断する作業で、Antigravity のエージェントが効いたのは「機械的に拾える対象の洗い出し」でした。opt-out 属性の列挙、statusBarColor の使用箇所の特定、insets リスナーが付いていない Activity の候補出しまでは、エージェントに任せると確実で速かったです。

一方で、最終的なパディングの当たり具合は、必ず実機とエミュレータの両方で自分の目で確認しました。insets は端末によって(特にジェスチャーナビとボタンナビで)値が変わるので、コードが正しく見えても見た目がずれることがあります。宮大工だった祖父たちは、図面が合っていても最後は必ず手で触れて確かめていたそうですが、この種の仕上げの確認だけは人がやるべき部分だと、画面を一枚ずつ見ながら思いました。エージェントに任せる範囲と、自分で確かめる範囲を分けておくことが、横断作業では一番の時短になります。

これから取り組むこと

いまは壁紙アプリ群の対応を一通り終え、3辺ジェスチャーナビ端末での表示確認を実機で詰めている段階です。次は、同じ insets 処理が各アプリにコピペで散らばっているのを、共通モジュールに切り出して一本化したいと考えています。OS の仕様変更が来るたびに全アプリを直す今の構造そのものを、少しずつ軽くしていくのが当面の目標です。

同じように複数アプリを抱えて targetSdk 36 への移行を控えている方は、まず opt-out の使用箇所を横断で洗い出すところから始めると、作業量の見通しが立てやすいと思います。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

アプリ開発2026-07-05
自己デバッグのエージェントに、本番の広告を叩かせない — AdMob 無効トラフィックを断つ三層の遮断
Antigravity 2.0 の実ブラウザ自己デバッグが本番の AdMob 広告を描画すると、無効なトラフィックとして計上されます。テスト広告の強制・ネットワーク遮断・preflight ゲートの三層で、広告に触れない検証環境を作る手順を、実測値とともにまとめました。
アプリ開発2026-07-01
音を出すアプリのリリース前チェックを、音声描画とエージェントで仕組み化する
6/26 の更新で Antigravity が音声ファイルを描画できるようになりました。癒し系アプリで音を扱ってきた経験から、音量・クリップ・長さ・無音の事前チェックをエージェントの検証ループに組み込む設計を、実際のスクリプトとともに紹介します。
アプリ開発2026-06-21
Play Store の密度分割で、特定の端末だけ内蔵壁紙が消えた — drawable と nodpi の境界設計
App Bundle の密度分割は、密度別に分けてはいけない画像まで分けてしまうと、特定の密度バケットの端末からだけリソースを取りこぼします。bundletool で再現し、drawable-nodpi への移動か密度分割の無効化で直すまでを、判断基準ごと残しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →