ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-05-14上級

AndroidがOSを卒業した日 — AppFunctions APIで壁紙アプリをGemini Intelligenceにつないだ実装記録

2026-05-12発表のAndroid AppFunctions APIでアプリをGemini Intelligenceに接続する実装を、累計5,000万DLの壁紙アプリ群で実際に試した記録。設計判断・Kotlin実装・Antigravity活用ワークフローを解説します。

Android27AppFunctionsGemini IntelligenceFirebase AI LogicAntigravity338個人開発89

2026年5月12日、Antigravityを開いて作業していたとき、Androidチームからメールが届きました。件名は「The Android Show: Developer's Cut」。最初は流そうと思いましたが、本文の冒頭で手が止まりました。

「Android is transitioning from an operating system to an intelligence system.」

OSからIntelligence Systemへ。この一文が何を意味するのか、しばらく考え込みました。2014年からAndroidアプリを作り続けてきた私にとって、このプラットフォームはいつも「ユーザーが直接操作するもの」という前提の上に成立していました。その前提が変わると言っています。

累計5,000万ダウンロードを超えた壁紙アプリ群を抱える身として、この変化を「あとで対応すればいい」と流せる気がしませんでした。今日は、実際にAppFunctions APIを触ってみた翌日の記録をまとめます。

AndroidがOSからIntelligence Systemに変わった意味

発表の中核にあるのは「Gemini Intelligence」と呼ばれる新しいAI統合レイヤーです。Androidデバイスがユーザーのニーズを先読みし、アプリが適切なタイミングでその体験を届けられるようにする——という設計思想です。

開発者向けに、2つの統合パスが用意されています。

パス1: Task Automation(コード変更なし) Geminiが既存のアプリ画面を直接ナビゲートして、ユーザーの代わりにタスクをこなします。フードデリバリーへの注文やライドシェアの予約などが例として挙げられており、アプリ側でのコード変更は不要です。

パス2: AppFunctions API(開発者が制御) 開発者がアプリの機能を「ツール」として明示的に定義し、自然言語の説明と共にOSに公開します。Geminiはそのツールを発見し、適切なタイミングで呼び出します。

興味深いのは、Googleが公式ブログで「in an MCP-like fashion(MCPに近い形で)」という言葉を使ったことです。AIコーディングで馴染み深いModel Context Protocolのコンセプトを、Androidプラットフォームに適用したと理解できます。

2つの統合パスと個人開発者の現実

「どちらを選ぶか」ではなく「どちらが自分のアプリの価値観に合うか」という問いで考えた方がしっくりきます。

Task Automation が向いているケース:

  • アプリが標準的なNavigation Componentで実装されており、画面遷移がシンプル
  • ユーザーが繰り返す操作パターンが明確(例: 毎朝特定のカテゴリから壁紙を選ぶ)
  • 開発リソースが限られており、まず様子を見たい

AppFunctions APIが向いているケース:

  • アプリが複数のビジネスロジックを持ち、画面遷移だけでは意図が伝わりにくい
  • 「気分で壁紙を選ぶ」のように、抽象的な入力からアプリ内操作につなぎたい
  • AIエコシステムへの統合に長期的な投資価値を感じている

壁紙アプリの場合、「今日の気分に合わせた壁紙をGeminiに選んでもらいたい」という用途には、Task Automationでは限界があります。「気分」という抽象的な入力から「壁紙カテゴリの絞り込み」「お気に入りフォルダへの保存」というビジネスロジックを呼び出す必要があるからです。この場合、AppFunctions APIの方が正しい選択です。

AppFunctions APIの仕組み — 「名前付きツール」の設計思想

AppFunctions APIの本質は、アプリの機能を「名前と説明を持つツール」として定義することです。

MCPのツール定義と非常に近い感覚があります。関数名・引数・返り値・自然言語による説明を組み合わせて、AIが「何をできるか」を理解できる形で公開します。

発表ではKakaoTalkがEarly Access Programの参加企業として挙げられており、「send messages」「initiate voice calls」という2つのAppFunctionを公開しているとのことです。すでに25のアプリのユースケースがデバイス上でローカル実行されているという数字も示されています。

AppFunction の構造:
  - id: アプリのパッケージ名を含む一意の識別子
  - description: Geminiが理解するための自然言語の説明
  - parameters: 型と意味の定義(具体的な例を含める)
  - 実装: Kotlinの suspend fun

このうち descriptionparameters の質が、Geminiがこの関数を正しく使えるかどうかを直接左右します。

Kotlinで実装するAppFunctions最小構成

EAP(Early Access Program)のα版APIと、AndroidのApp Actionsで培われてきたアノテーションベースのパターンをもとに、壁紙アプリ向けの最小構成を組みました。

まず build.gradle.kts に依存関係を追加します。

// build.gradle.kts(app モジュール)
dependencies {
    implementation("androidx.appfunctions:appfunctions:1.0.0-alpha01")
    implementation("com.google.firebase:firebase-ai:25.0.0")
    kapt("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha01")
}

次に、AppFunctionを定義します。壁紙アプリの場合、「気分に合わせて壁紙を選んで適用する」機能を公開します。

// WallpaperAppFunctions.kt
import androidx.appfunctions.AppFunction
import androidx.appfunctions.AppFunctionContext
import androidx.appfunctions.AppFunctionResult
 
@AppFunction(
    id = "com.example.wallpaper.SET_WALLPAPER_BY_MOOD",
    description = "Set device wallpaper based on user's current mood or preference. " +
        "Supports moods like 'calm', 'energetic', 'nature', 'minimal', and 'seasonal'. " +
        "Use this when the user wants to change their wallpaper based on how they feel.",
    parameters = [
        AppFunction.Parameter(
            name = "mood",
            description = "The mood or theme for wallpaper selection. " +
                "Examples: calm, nature, minimal, energetic, space, warm, seasonal",
            type = String::class
        ),
        AppFunction.Parameter(
            name = "setImmediately",
            description = "Whether to apply the wallpaper right away (true) " +
                "or show a preview screen first (false). Default is false.",
            type = Boolean::class,
            required = false
        )
    ]
)
class SetWallpaperByMoodFunction(
    private val wallpaperRepository: WallpaperRepository,
    private val wallpaperManager: WallpaperManager
) : AppFunction {
 
    override suspend fun execute(
        context: AppFunctionContext,
        parameters: Map<String, Any?>
    ): AppFunctionResult {
        val mood = parameters["mood"] as? String
            ?: return AppFunctionResult.error("mood parameter is required")
        val setImmediately = parameters["setImmediately"] as? Boolean ?: false
 
        // 気分に合う壁紙をリポジトリから取得
        val wallpaper = wallpaperRepository.findByMood(mood)
            ?: return AppFunctionResult.error("「${mood}」に合う壁紙が見つかりませんでした")
 
        return if (setImmediately) {
            // 即時適用
            wallpaperManager.setWallpaper(wallpaper.bitmap)
            AppFunctionResult.success(
                message = "壁紙を「${wallpaper.title}」(${mood}テーマ)に変更しました",
                data = mapOf("wallpaperId" to wallpaper.id, "title" to wallpaper.title)
            )
        } else {
            // プレビュー画面への遷移
            AppFunctionResult.deepLink(
                uri = "wallpaperapp://preview/${wallpaper.id}",
                message = "「${wallpaper.title}」のプレビューを開きます"
            )
        }
    }
}

このコードで最もこだわったのは descriptionparameters の書き方です。「Supports moods like 'calm', 'energetic'...」という具体的な列挙が、GeminiがこのFunctionをいつ呼ぶかの判断精度を上げます。MCPのツール説明を書くときと同じ感覚です。

AppFunctionをManifestに登録します。

<!-- AndroidManifest.xml -->
<application>
    <meta-data
        android:name="androidx.appfunctions.APP_FUNCTIONS"
        android:resource="@xml/app_functions" />
</application>
<!-- res/xml/app_functions.xml -->
<app-functions>
    <app-function
        android:id="com.example.wallpaper.SET_WALLPAPER_BY_MOOD"
        android:enabled="true"
        android:allowedCallers="com.google.android.apps.assistant" />
</app-functions>

allowedCallerscom.google.android.apps.assistant を指定することで、Geminiアシスタントからのみこの関数が呼び出せるようになります。セキュリティの観点から、EAP期間中はこの設定が推奨されています。

Firebase AI Logic × Gemini 2.5 Proでアプリ内体験を拡張する

AppFunctionsでGeminiにアプリを「操作させる」のとは別に、アプリ内でGeminiを使って「ユーザー体験を向上させる」アプローチもあります。Firebase AI Logicを使ったアプリ内AI機能の実装です。

壁紙アプリの場合、「この壁紙に合う時間帯は?」「今日の天気に合ったカテゴリを教えて」のような自然言語インタラクションを追加できます。

// AIWallpaperAdvisor.kt
import com.google.firebase.ai.FirebaseAI
import com.google.firebase.ai.GenerativeModel
import com.google.firebase.ai.type.GenerationConfig
import com.google.firebase.ai.type.content
 
class AIWallpaperAdvisor {
 
    private val model: GenerativeModel by lazy {
        FirebaseAI.getInstance().generativeModel(
            modelName = "gemini-2.5-pro",
            generationConfig = GenerationConfig.builder()
                .maxOutputTokens(512)
                .temperature(0.4f)  // 壁紙提案は低めの温度で安定した回答を得る
                .build(),
            systemInstruction = content {
                text(
                    """
                    あなたは壁紙アプリのAIアドバイザーです。
                    ユーザーの気分・時間帯・天気をもとに、
                    おすすめの壁紙カテゴリと設定理由を提案してください。
                    回答は必ず日本語で、2文以内に簡潔にまとめてください。
                    カテゴリ候補: nature, minimal, space, warm, cool, abstract, city, art, seasonal
                    """.trimIndent()
                )
            }
        )
    }
 
    // 気分・時間帯からカテゴリ推薦
    suspend fun recommendCategory(
        mood: String,
        timeOfDay: String,
        weather: String = "不明"
    ): RecommendationResult {
        return try {
            val prompt = buildString {
                append("現在の気分: $mood\n")
                append("時間帯: $timeOfDay\n")
                append("天気: $weather\n")
                append("この状況に最もふさわしい壁紙カテゴリを1つ選び、理由を1文で説明してください。")
                append("形式: カテゴリ名|理由")
            }
 
            val response = model.generateContent(content { text(prompt) })
            val text = response.text?.trim() ?: return RecommendationResult.Fallback
 
            val parts = text.split("|")
            if (parts.size >= 2) {
                RecommendationResult.Success(
                    category = parts[0].trim(),
                    reason = parts[1].trim()
                )
            } else {
                RecommendationResult.Fallback
            }
        } catch (e: Exception) {
            RecommendationResult.Fallback
        }
    }
}
 
sealed class RecommendationResult {
    data class Success(val category: String, val reason: String) : RecommendationResult()
    object Fallback : RecommendationResult()
}

ここで temperature = 0.4f に設定している理由があります。壁紙の提案は「創造的な発散」よりも「文脈に合った安定した提案」の方が価値があります。毎回違うカテゴリが返ってくると、ユーザーが「いつもと違う」と感じて混乱します。アプリの種類によって適切な温度は異なりますが、ユーティリティ系は低めが安定します。

また、systemInstruction で「2文以内に簡潔に」と指定しているのは、UIの表示スペースに収まるためです。AIの回答がUIから溢れてトランケートされると、回答の質が高くても体験が損なわれます。

Antigravityで加速するAndroid Intelligence System対応

AppFunctions APIはα版なので、まだドキュメントが薄い部分があります。こういうときにAntigravityが効きます。AGENTS.mdに以下のコンテキストを追加することで、Antigravityがプロジェクトの意図を正確に把握した上でコードを書いてくれます。

# AGENTS.md(Android Intelligence System対応プロジェクト用)
 
## プロジェクトの目的
壁紙アプリにGemini Intelligence対応を追加する。
AppFunctions APIを用い、ユーザーが自然言語で壁紙を操作できるようにする。
 
## 技術スタック
- Android 17 / Kotlin 2.x / Jetpack Compose
- AppFunctions API alpha(androidx.appfunctions:1.0.0-alpha01)
- Firebase AI Logic SDK(Gemini 2.5 Pro)
- EAP段階のAPIのため、公式ドキュメントはgoo.gle/eap-af参照
 
## AppFunctions 設計原則
- description と parameter説明は英語で記述(Geminiの理解精度が高い)
- parameter の description に必ず具体的な値の例を含める
- エラーメッセージと success.message は日本語(エンドユーザー向け)
- setImmediately のデフォルト値は false にする(壁紙の誤適用防止)
- deepLinkはプレビュー遷移にのみ使用し、確認不要なアクションは直接実行する
 
## Firebase AI Logic の原則
- temperature は 0.3〜0.5 の範囲で設定する(ユーティリティアプリのため)
- systemInstruction で必ず返答の文字数・言語・フォーマットを指定する
- レスポンステキストはUIに表示する前にtrim()とサニタイズを行う
 
## 既知の制約(2026-05-14時点)
- AppFunctions はAndroid 17以上でのみ動作確認済み
- allowedCallers は現在 com.google.android.apps.assistant のみ
- EAP登録URL: goo.gle/eap-af

このAGENTS.mdを設置した状態でAntigravityに「AppFunctionを追加して」と依頼すると、descriptionを英語で書き、setImmediatelyのデフォルトをfalseにし、エラーメッセージを日本語にする——という設計方針を守ったコードを生成してくれます。

α版APIへの対応で特に効果的なのが、AGENTS.mdの「既知の制約」セクションです。Antigravityは最新のα APIを学習していないケースもあるため、EAPドキュメントから引用したコード断片をAGENTS.mdに貼り付けておくと、正しいAPIの使い方でコードが生成されます。

実装後の動作確認には、AntigravityのAgentKit機能でテストシナリオを自動実行する方法が有効です。

// AppFunctionsテスト(Antigravity AgentKit連携想定)
@Test
fun `気分がcalmのとき適切な壁紙が選ばれること`() = runTest {
    // Given
    val mockRepo = mockk<WallpaperRepository> {
        coEvery { findByMood("calm") } returns WallpaperFixtures.calmWallpaper
    }
    val function = SetWallpaperByMoodFunction(mockRepo, mockk(relaxed = true))
 
    // When
    val result = function.execute(
        context = AppFunctionContext.forTest(),
        parameters = mapOf("mood" to "calm", "setImmediately" to true)
    )
 
    // Then
    assertThat(result).isInstanceOf(AppFunctionResult.Success::class.java)
    assertThat((result as AppFunctionResult.Success).message).contains("calm")
}

5,000万DLのアプリ群を持つ個人開発者の設計判断

10年以上アプリを作り続けてきた立場で、新しいAPIが出たときに「全部のアプリに即対応」が現実的でないことは分かっています。今回のAndroid Intelligence System対応も、まず優先度を整理しました。

優先度高: メインの壁紙アプリ 毎日使われるアプリなのでGemini Intelligenceとの連携価値が高いです。「朝に自動で壁紙を変える」用途はTask Automationで対応可能で、「気分や天気に合わせた選定」にはAppFunctions APIが有効です。まずTask Automationで様子を見て、3ヶ月後にAppFunctionsを追加する計画を立てています。

優先度中: 癒し系サウンドアプリ 「仕事モードの音楽を流して」のような自然言語操作は価値があります。壁紙との連携(Visual + Sound)でシナジーが生まれる可能性もあります。AppFunctions EAPに登録して情報収集しています。

優先度低: 引き寄せ系アプリ このカテゴリのアプリは、ユーザーの自発的な行動が価値の核心にあります。AI自動化との相性を慎重に見極める必要があります。今は静観の判断です。

ここで大切だと感じるのは「なぜ対応しないかを言語化すること」です。新技術だからといって全ての機能にAIを組み込む必要はありません。

祖父たちは宮大工として一つ一つの部材に向き合い、「どこを機械に任せ、どこを手で仕上げるか」を職人の目で判断していました。その感覚が私にも受け継がれているのかもしれません——どこをAIに委ね、どこに人間の手触りを残すかを、アプリごとに丁寧に判断したいと思っています。

今から取れる3つのアクション

Google I/O 2026は来週(5月19〜20日)です。そこでさらに詳細な開発者向け情報が出ると予想されます。今すぐ始められることを整理します。

1. Task Automationの動作確認(今週) コード変更不要です。Google Pixel または Samsung Galaxyの最新機種を持っている方は、Gemini Intelligence(Android 17プレビュー)でアプリを操作してみてください。アプリがどの程度自動操作されるかを先に体感することで、AppFunctions APIで何を補完すべきかが見えてきます。

2. AppFunctions EAPへの登録(今週) goo.gle/eap-af からEarly Access Programへの参加申請が可能です。EAP登録後に詳細なAPIドキュメントと開発者サポートへのアクセスが得られます。承認まで数日かかる場合があります。

3. AntigravityのAGENTS.mdを整備する(来週) Google I/O 2026の発表後、具体的なAPIリファレンスが公開されたタイミングで、AGENTS.mdにその内容を追記します。α版APIはドキュメントが急速に更新されるため、AGENTS.mdを「生きたコンテキスト」として継続的に更新していく運用が効果的です。

Android Intelligence Systemへの対応は、一度で完成させるものではなく、プラットフォームの成熟とともに育てていくものだと思います。来週のGoogle I/O 2026でさらに何が発表されるか、一緒に見届けましょう。

シェア

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

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

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

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

関連記事

アプリ開発2026-06-21
Play Store の密度分割で、特定の端末だけ内蔵壁紙が消えた — drawable と nodpi の境界設計
App Bundle の密度分割は、密度別に分けてはいけない画像まで分けてしまうと、特定の密度バケットの端末からだけリソースを取りこぼします。bundletool で再現し、drawable-nodpi への移動か密度分割の無効化で直すまでを、判断基準ごと残しました。
アプリ開発2026-04-18
AntigravityでアートアプリをiOS/Androidに同時リリースした話 — 企画・開発・審査の全記録
アーティストである自分のポートフォリオアプリをAntigravityで作り、iOS/Android同時リリースした実体験。技術だけでなくコンセプト設計から審査通過まで全部記録しました。
アプリ開発2026-07-14
AdMob を載せた画面のスクリーンショットテストを、広告の非決定性から守る
広告つきの画面はスクリーンショットテストが毎回赤くなり、やがて誰も差分を見なくなります。AdMob バナーの非決定性を封じ、レイアウトの崩れだけを検知に残す設計を、Compose の実装例と Antigravity での差分トリアージまで含めてまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →