mcpField
A
android-emulator-skill

android-emulator-skill

github.com/fluxxion82テスト・品質1184 回インストール5 日前に更新
リポジトリ

android-emulator-skill について

Android Emulator Skillは、アクセシビリティベースのナビゲーションと構造化されたUIデータを使い、Androidアプリの構築・テスト・自動化を行うスクリプト群です。画面座標に頼らず、エミュレーターやアプリのライフサイクルを扱う開発・テストに向いています。

主な機能

  1. 01UI階層による画面の検査
  2. 02アプリの起動、インストール、管理
  3. 03エミュレーターの起動、作成、終了
  4. 04AVDのデータ消去または削除
  5. 05機械可読なJSON結果の出力

活用シーン

  1. 01テスト中にアプリのUI要素をマッピング
  2. 02アプリを起動してディープリンクを開く
  3. 03テスト用AVDの作成または起動
  4. 04テスト実行間にエミュレーターのデータをリセット

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/fluxxion82/android-emulator-skill --skill android-emulator-skill

インストール後、Claude Code で/android-emulator-skillを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
examples/
login_flow_example.sh
scripts/
accessibility_audit.py
android_health_check.sh
anr_watcher.py
app_launcher.py
app_state_capture.py
appearance.py
avd.py
build_and_test.py
container.py
crash_triage.py
device_list.py
emulator_boot.py
scripts/common/
__init__.py
adb_exec.py
anr_pipeline.py
anr_sessions.py
batch.py
cache_utils.py
cli.py
device_utils.py
emu_console.py
env_config.py
hierarchy.py
log_lifecycle.py
logcat.py
run_as.py
screenshot_utils.py
sdk_tools.py

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

Android Emulator スキル

ピクセル座標ではなく、アクセシビリティを活用したナビゲーションと構造化データを使って、Android アプリケーションを構築、テスト、自動化します。

これらのスクリプトの呼び出し方

スクリプトはこのファイルと同じ階層の scripts/ にあります。単なる相対パスではなく、スキルディレクトリを起点とするパスを使用してください。スキルがプラグインとしてインストールされ、プロジェクト内で作業している場合、python scripts/foo.py はファイルが見つからないというエラーになります。

SKILL_DIR=/path/to/skills/android-emulator-skill
python3 "$SKILL_DIR/scripts/screen_mapper.py" --json

以下の例では、$SKILL_DIR がそのように設定されているものとします。

クイックスタート

# 1. Launch app
python3 "$SKILL_DIR/scripts/app_launcher.py" --launch com.example.app

# 2. Map screen to see elements
python3 "$SKILL_DIR/scripts/screen_mapper.py"

# 3. Tap button (add --scroll-to-find if it may be below the fold)
python3 "$SKILL_DIR/scripts/navigator.py" --find-text "Login" --tap

# 4. Enter text
python3 "$SKILL_DIR/scripts/navigator.py" --find-type EditText --enter-text "[email protected]"

# 5. Run accessibility audit
python3 "$SKILL_DIR/scripts/accessibility_audit.py"

すべてのスクリプトは、詳細なオプションを確認するための --help と、機械可読な出力を得るための --json をサポートしています。

スクリプト(v0.7.0)

実装済み(32 スクリプト)
コアユーティリティ(common/ 内の 11 モジュール)
  1. common/device_utils.py - ADB コマンドの構築とデバイスの検出

  2. common/screenshot_utils.py - スクリーンショットの撮影と処理

  3. common/cache_utils.py - 段階的開示キャッシュシステム

    common/ 内には、さらに次のものがあります。adb_exec.py(すべての adb 呼び出しに対する唯一の制約付きエントリーポイント。対処方法を示す型付きエラーを返します)、hierarchy.py(UI 階層を取得する唯一の方法。一時ファイルを使わないため、同時実行時に互いの画面を読み取ることはありません)、emu_console.py(adb emu は失敗しても終了コード 0 を返します)、logcat.py(adb logcat の argv を構築し、期間を解析する唯一の場所。4 つすべてのログリーダーで共有されます)、sdk_tools.py(SDK バイナリを解決します。SDK ルートの PATH 上で emulator を裸の名前で指定すると、<sdk>/emulator ディレクトリに当たって PermissionError が発生します)、env_config.py、anr_pipeline.py、anr_sessions.py。

アプリ管理(1 スクリプト)
  1. app_launcher.py - アプリのライフサイクル管理
    • パッケージ名でアプリを起動します。--launch は アクティビティが表示されるまで待機し(am start -W)、システムから Status: ok 以外が報告された場合は失敗します。そのため、次のコマンドでは、まだ前面にあった画面ではなく、アプリの画面がマッピングされます。メッセージには、起動したアクティビティと所要時間が表示されます。
    • アプリを終了します
    • APK をインストール/アンインストールします
    • ディープリンクで移動します。--open-url は --launch と同じように待機し、インテントの解決先がない場合は失敗します。URL をシステムに渡しただけなのに成功したように報告することはありません。
    • インストール済みのパッケージを一覧表示します
    • アプリの状態を確認します
    • オプション:--launch、--terminate、--install、--uninstall、--open-url、--list、--state、--json
デバイスのライフサイクル(5 スクリプト) ✓ 完了
  1. emulator_boot.py - オプションで準備完了を確認しながらエミュレーターを起動します

    • AVD 名を指定して起動します
    • タイムアウトを指定してデバイスの準備完了を待機します
    • 一括起動操作に対応します
    • ヘッドレスモードに対応します
    • --list-avds は、エミュレーターが実行されていて、このホストに AVD が定義されていない場合に限り、空のリスト(No AVDs found または {"avds": []})を報告して終了コード 0 で終了します。emulator バイナリが見つからない場合、または実行に失敗した場合は、検索場所を示すエラーとなり、終了コード 1 で終了します(--json の場合は {"error": ...})。「AVD がない」と「検索できなかった」は別の結果です。--all も同様に失敗を報告します。
    • 接続中のエミュレーターのうち、どれか 1 つでも使用している AVD を特定できない場合(device 以外の状態、またはコンソールが応答しない場合)、あるいはエミュレーターを一覧表示できない場合は、起動を拒否します。対象がこの AVD の可能性があり、同じ AVD のインスタンスを 2 つ起動するとユーザーデータが破損するためです。拒否メッセージにはシリアルと状態が表示され、古いエミュレーターのプロセスを終了するよう案内します。応答しないコンソールは、コンソール経由では終了できません。
    • 失敗は要求された出力形式で返されます。--json の場合は {"error": ...}、それ以外の場合は標準エラー出力にメッセージを表示し、常にゼロ以外の終了コードで終了します。
    • オプション:--avd、--wait-ready、--timeout、--headless、--list-avds、--json
  2. emulator_shutdown.py - エミュレーターを安全にシャットダウン

    • シリアル番号を指定してシャットダウンします。対象はエミュレーターのみです。emulator-NNNN 形式でないシリアル番号は、adb コマンドを実行する前に拒否されるため、接続中のスマートフォンやタブレットの電源を切ることはありません
    • --name AVD を指定すると、AVD をシリアル番号に解決します。エミュレーターの照会に失敗した場合、またはエミュレーターを一覧表示できなかった場合は、その事実を報告してゼロ以外の終了コードで終了します。誰も照会できていないデバイスについて、確実に否定できたかのように「No running emulator found」と報告することはありません
    • 失敗は、指定されたモードで応答します。--json の場合は {"error": ...}、それ以外の場合は標準エラー出力にメッセージを出力し、常にゼロ以外の終了コードで終了します
    • シャットダウン完了の検証は任意です
    • 一括シャットダウン操作(実行中のエミュレーターのみ)
    • オプション:--serial、--name、--verify、--timeout、--all、--json
  3. emulator_create.py ⭐ NEW - AVD を動的に作成

    • デバイスの種類と API レベルを指定して作成します
    • 利用可能なデバイス定義を一覧表示します
    • 利用可能なシステムイメージを一覧表示します
    • オプション:--device、--api、--name、--abi、--variant、--list-devices、--list-images、--json
  4. emulator_delete.py ⭐ NEW - AVD を完全に削除

    • AVD 名を指定して削除するか、--all/--old N を指定して一括削除します(--yes を指定すると確認プロンプトを省略します)
    • 利用可能な AVD を一覧表示します
    • どのモードでも、avdmanager が見つからない場合や動作しない場合は、cmdline-tools による対処方法を示して終了コード 1 で報告します。終了コード 0 で「No AVDs deleted」と報告することはありません
    • --old N は <name>.avd の更新日時(mtime)を基準に並べ替えます。AVD のディレクトリが見つからない場合は、古い順に並べ替えて削除するのではなく、順位付け全体が失敗し(終了コード 1)、処理を終了します。AVD のホームディレクトリは $ANDROID_AVD_HOME、それが設定されていない場合は $ANDROID_SDK_HOME/.android/avd、それも設定されていない場合は ~/.android/avd です
    • N は 1 以上である必要があります。--old 0 は何も残さないため、使用方法エラー(終了コード 2)として拒否され、代わりに --all --yes を指定するよう案内します
    • 失敗時、--json では {"error": ...} を返します。一括処理では、要約を維持し、いずれかの AVD で失敗した場合は error も併せて返します
    • オプション:--name、--all、--old、--yes、--list、--json、--verbose
  5. emulator_erase.py ⭐ NEW - AVD を初期状態にリセット

    • AVD を削除せずにユーザーデータを消去します
    • AVD の設定は保持します
    • --all で一括消去します。--verify を指定すると、消去が反映されるまでディスク上の AVD をポーリングします。ポーリングは --timeout(デフォルトは 90 秒、ANDROID_EMU_ERASE_TIMEOUT)で打ち切られます
    • 接続中のエミュレーターがすべて特定され、その中にこの AVD が含まれていないことを確認できない限り、消去を拒否します。コンソールへの照会に失敗した場合、またはエミュレーターの状態がまだ device でない場合は「不明」として扱い、シリアル番号、状態、対処方法を示して拒否します。--force を指定すると消去を強行します
    • どの失敗も、指定されたモードで応答します。--json では標準出力に {"error": ...} を出力し、それ以外の場合は標準エラー出力にメッセージを出力します。常にゼロ以外の終了コードで終了します。--all では AVD ごとの要約を維持し、いずれかの AVD で失敗した場合は error も併せて返します
    • スナップショットは保持されます:消去に成功すると「snapshots kept; use snapshot.py --delete <name> to remove them」と表示されます。これは、後から snapshot.py --load を実行すると、初期状態が元に戻される可能性があるためです
    • オプション:--name、--all、--force、--verify、--timeout、--list、--json、--verbose
ビルドと開発(2 個のスクリプト)✓ 完了
  1. build_and_test.py - Gradle のビルド/テストを段階的な情報開示で自動化します(gradle/ サブパッケージを利用)

    • デフォルトでは 1 行の概要と結果 ID を表示し、必要に応じて詳細を確認できます
    • JUnit XML を解析して成功/失敗の件数と失敗したテスト名を取得し、Gradle/Kotlin/Java のエラーと警告を抽出します
    • オプション:--project、--module/-p、--variant、--clean、--test、--suite、--get-errors/--get-warnings/--get-log <ID>、--list-builds、--verbose、--json
  2. log_monitor.py ⭐ 新機能 - logcat をリアルタイムで監視します

    • アプリのパッケージでフィルタリングします
    • 深刻度(error/warning/info/debug)でフィルタリングします
    • スマートな重複排除
    • 期間指定モードまたは追従モードを利用できます。--duration / --last には単位 (30s、5m、1h)が必要です。単位のない数値は使用法エラーとして拒否されます(終了コード 2)
    • ログをファイルに保存します
    • オプション:--app、--serial、--severity、--follow、--duration、--output、--clear、--verbose、--json
ナビゲーションと操作(4 個のスクリプト)
  1. screen_mapper.py - 現在の画面を分析し、操作可能な要素を一覧表示します

    • 一覧表示はデフォルトの動作であり、モードではありません。--list はありません。 コマンドをそのまま実行すると概要が表示され、--verbose を指定すると要素ごとの詳細に展開され、--hints を指定するとナビゲーションの候補が追加されます。
    • デフォルトの出力では、操作可能なコントロールがすべて名前付きで表示されます。種類ごとに 1 行(Button:、Control:、CheckBox:、EditText: など)で、種類ごとの上限があります。これらの名前は navigator.py --find-text が受け付ける名前と同じなので、クイックスタートの手順 2 から手順 3 にそのまま進めます。以前は件数のみが表示され、Compose の画面では名前は --verbose/--json の場合にしか表示されませんでした。
    • テキストを持たないコントロールは、サブツリーまたは行から取得したキャプションを名前として使用します。リソース ID がある場合は、ID のみが名前として使用されます (com.android.settings:id/search_action_bar は search_action_bar と表示されます)。これは navigator が表示する名前であり、--find-id が受け付ける ID です。Compose のテストタグはすでに ID のみなので、どちらのツールキットでも同じ形式で名前が表示されます。
    • --hints を指定すると、入力済みの次の実行コマンドが表示されます。
    • オプション:--serial/-s、--verbose/-v、--hints、--json
  2. navigator.py - 要素を意味的に検索して操作する

    • テキスト、種類、リソース ID で検索
    • タップ、テキスト入力、境界の取得
    • あいまい一致に対応
    • --find-text は、画面レポートに出力された任意の名前に一致します。要素自体のテキストや content-desc、ラベルのないコントロールから復元されたキャプション、またはリソース ID(単独形式または完全修飾形式で、全体一致)が対象です。キャプションに一致した場合は、そのキャプションを持つコントロール(キャプションの内側にあるコントロール、または同じ行の隣にあるコントロール)が解決先となるため、右に 143px ずれた場所ではなくチェックボックスがタップされます。所有元のコントロールはタップ可能(clickable/long-clickable/checkable)でなければなりません。スクロールコンテナーも操作可能ですが、その中のキャプションが指す対象ではなく、そこに解決すると画面中央がタップされてしまいます。そのようなコントロールを持たない受動的なラベルだけに一致する名前は、タップせずに拒否されます。
    • --tap と --enter-text には対象の指定が必須です。--find-text、--find-exact、--find-type、--find-id、または明示的な --tap-at x,y のいずれかを指定してください。指定がない場合は使用法エラー(終了コード 2)となり、デバイスには何も送信されません。
    • --max-scrolls の上限は 50 です(ANDROID_EMU_MAX_SCROLLS も同様です)。また、--scroll-to-find による検索には実時間の制限(ANDROID_EMU_SCROLL_SEARCH_DEADLINE、デフォルトは 120s。--help を参照)があり、画面ダンプの回数も制限されます。各スクロールの実行時に、進行状況を示す行が stderr に出力されます。
    • --json を指定した場合、--tap/--enter-text の成功時には tapped_at: [x, y] が報告されます。これはデバイスに送信された座標なので、文章を解析せずにタップ位置を確認できます。使用法エラーを含むすべての失敗では {"error": ...} が出力され、ゼロ以外の終了コードで終了します。
    • --scroll-to-find は、画面に表示されていない範囲も検索します。これを指定しない場合、検索対象は表示中の画面だけで、「見つかりませんでした」と「対象が 2 行下にある」を区別できません。デフォルトの動作では、その違いを示すようになりました:(searched 1 screen; this screen scrolls -- retry with --scroll-to-find)。意図的に、スクロールはオプトインです。スワイプは読み取りではないため、検索が暗黙にスクロールすると、3 画面先にあるボタンに対して --find-text "Save is visible" が成功してしまいます。
    • オプション:--find-text、--find-type、--find-id、--tap、--enter-text、--list、--scroll-to-find、--scroll-direction {down,up}、--max-scrolls N、--serial、--json、--verbose
  3. gesture.py - スワイプ、スクロール、長押しを実行する

    • --scroll {up,down} はコンテンツが動く方向を指定し、--swipe は指の動きを指定します。両者は逆向きです。(これらを混同するのは実際の不具合でした。--scroll down でリストが上方向へ戻り、成功と報告されていました。)
    • 方向を指定したスワイプ
    • スワイプ座標のカスタム指定
    • スクロールと長押し
    • オプション:--swipe、--from-edge、--duration、--long-press、--scroll、--serial、--json
  4. keyboard.py - テキスト入力とハードウェアボタン

    • --type でテキストを入力します。--text はありません。このフラグは push_notification.py 用のもので、ここで使うとターンを無駄にします。--delay N は、入力のたびにデバウンスが発生するフィールドに対して、1文字ずつ入力します。
    • 特殊キー(--key enter。--count N で繰り返し)とハードウェアボタン (--button back)を押します。--keys a,b はキーを順番に押します。--key と --button は同じ名前テーブルを共有しているため、どちらでもどちらの名前も使えます。
    • --clear N は N 文字を削除します。--hide-keyboard / --dismiss は ソフトウェアキーボードを閉じます。どちらも最初に dumpsys input_method を読み取り、IME が実際に表示されている場合にのみ BACK を押します。BACK は「キーボードを隠す」キーではなく、 キーボードが表示されていない状態で押すと現在の画面から移動します。キーボードが表示されていない場合は 「No keyboard shown」と報告し、キーイベントを送信せずに終了コード 0 で終了します。IME の状態を 読み取れない場合は、推測せずに終了コード 1 で終了します。何があっても BACK を押すには、 明示的に指定します:--button back。(状態は mInputShown から読み取り、 バックアップとして mIsInputViewShown を使用します。どちらも API 33 と API 35 で計測済みです。)
    • オプション:--type、--delay、--key、--count、--keys、--button、 --clear、--hide-keyboard、--dismiss、--serial/-s、--json
テストと分析(4 スクリプト)✓ 完了
  1. accessibility_audit.py ⭐ 新規 - WCAG 準拠チェック

    • コンテンツ説明の欠落:uiautomator のプロパティに基づく操作可能なコントロール(クラス名ではありません)で、 コントロール自体にもサブツリーにも説明テキストがない場合は、重大な問題です。以前のクラス名による判定では、 コントロールが単なる android.view.View である Compose 画面では、まったく検出されませんでした。
    • タッチターゲットのサイズを検証
    • EditText のヒントをチェック
    • 画像のアクセシビリティをチェック
    • 重大度(critical/warning/info)で分類
    • レポートを保存(JSON + Markdown)
    • オプション:--serial、--output、--verbose、--json
  2. visual_diff.py - スクリーンショットを比較して視覚的な変更を確認

    • ピクセル単位で比較
    • 差異を強調表示
    • 差分画像を生成
    • 使用方法:visual_diff.py BASELINE CURRENT [--output DIR] [--threshold N] [--json]
    • 注:2 つの画像は位置引数です。--json は完全なレポートを出力するようになったため、 このスクリプトも他のすべてのスクリプトと同じ仕様を維持しています。 --details は、この変更より前に作成された呼び出し元向けに、非推奨のエイリアスとして引き続き使用できます。
    • オプション:--output、--threshold、--json、--details
  3. test_recorder.py - テストの実行を手順ごとに記録

    • セッションベースで、各手順につき 1 プロセスです。--test-name、 --output、--inline はありません。--start NAME でセッションを開始し、その ID を出力します。 以降の各 --step "description" は現在の画面をセッションに記録し、 --stop は report.md + manifest.json を書き込みます。--step と --stop は 最新のセッションをデフォルトで対象にします。--session ID を指定して特定のセッションを対象にすることもできます。
    • 各手順でスクリーンショットと UI 階層を記録し、必要に応じて --screen、--state、--assert も記録します(--assert-failed を指定すると失敗としてマークされます)。
    • セッションは、指定した出力ディレクトリではなく ~/.android-emulator-skill に保存されます。 --list / --get-details ID で取得し、--clear [--older-than 24h] で削除します。
    • オプション:--start、--step、--stop、--list、--get-details、 --clear、--session、--serial、--app-name、--size、--screen、 --state、--assert、--assert-failed、--failed、--older-than、 --verbose、--json
  4. app_state_capture.py ⭐ 新規 - デバッグ用の完全なスナップショット

    • スクリーンショット + UI 階層 + ログ + アプリ情報をキャプチャ
    • タイムスタンプ付きスナップショットを作成
    • オールインワンのデバッグ成果物
    • オプション:--package、--output、--serial、--logs、--no-logs、--screenshot-size、--json
高度なテストと権限(3 スクリプト)✓ 完了
  1. privacy_manager.py ⭐ 新規 - アプリの権限管理

    • 権限の付与/取り消し
    • アプリの権限の一覧表示
    • 20 種類以上の権限に対応
    • 一括操作
    • オプション:--grant、--revoke、--list、--package、--serial、--list-permissions、--json
  2. status_bar.py ⭐ 新規 - ステータスバーの制御

    • バッテリー残量と充電状態の設定
    • Wi-Fi/モバイルの電波強度の設定
    • 時刻表示の設定(スクリーンショットの一貫性を保つため)
    • デモモードに対応
    • オプション:--preset、--battery、--charging、--wifi、--mobile、--time、--reset、--serial、--json
    • これらはすべて SystemUI の デモモードを操作し、ステータスバーの 描画内容を変更します。アプリがシステムから取得する値は変更されません(BatteryManager が引き続き実際の残量を報告します)。
  3. push_notification.py - 通知シェードに通知を投稿し、投稿された内容を読み取ります

    • v0.6.0 で adb で実際にできることに合わせて範囲を見直しました。アプリの通知を送信するとは謳わなくなりました。--post は cmd notification post 経由で投稿し、その通知の所有者はテスト対象アプリではなく com.android.shell(チャンネル shell_cmd)です。そのため、アプリ独自のチャンネル、レシーバー、表示処理はテストされません。それでも、NotificationListenerService、通知シェード UI、または通知に反応するエージェントを操作する方法として利用できます。--tag はタイトルとキー、--text は本文です。
    • --list は、パッケージを問わず実際に投稿された通知を読み取ります。コマンドが終了コード 0 を返したかではなく、通知が存在することを確認できます。--expect-package PKG を指定すると、この読み取り結果が終了ステータスに反映されます。
    • --grant-permission/--revoke-permission は、--package で指定したアプリの POST_NOTIFICATIONS(API 33 以降)を切り替え、実行時権限の処理経路をテストできます。
    • オプション:--post、--list、--grant-permission、--revoke-permission、--tag、--text、--no-verify、--expect-package、--package、--serial、--json、--verbose
    • ⚠️ アプリ独自の FCM ハンドラーには通知を配信できません。c2dm レシーバーはシェルユーザーではなく Play services が保持する権限で保護されているため、adb では配信する方法がありません。代わりに FCM 経由で送信してください。また、チャンネルの一覧表示もできません。cmd notification に list channels サブコマンドがないため、以前の --list-channels は機能しません。
検出とデバイスの状態(14 スクリプト)⭐ 新規
  1. android_health_check.sh - 環境(ANDROID_HOME、adb、emulator、avdmanager、sdkmanager、java、Python 3.12 以降、Pillow)を確認し、接続済みデバイスと AVD を一覧表示します。adb が見つからない場合は、0 以外の終了コードで終了します。

  2. device_list.py - 接続済みデバイス(adb devices -l)と定義済み AVD(emulator -list-avds)を段階的に詳細表示します。

    • 終了コード 0 でインベントリが空の場合は、ツールが実行され、該当するものが見つからなかったことを意味します。adb または emulator が見つからないか、実行に失敗した場合は、対処方法とともに終了コード 1 で終了します(--json では {"error": ...})。例外は avdmanager です。これはターゲット/ABI の詳細情報を追加するだけなので、見つからなくても失敗ではなく、stderr(JSON では warnings)に警告が出力されます。
    • オプション:--get-details、--device-type/--name、--json
  3. emulator_selector.py - 最適な AVD を提案します(実行中 → 最近使用 → 最新 API → 一般的なモデルの順にランク付け)。一覧表示または起動もできます。

    • AVD がないホストでは何もランク付けせず、終了コード 0 で終了します。emulator バイナリが見つからない場合や失敗する場合は、空のランキングではなく対処方法を示して終了コード 1 で終了します
    • 使用中の AVD を特定できないエミュレーターには「現在実行中」のボーナスは付きませんが、その状態とともに一覧表示されます(--json では unidentified_emulators、それ以外では stderr に警告)。これにより、ランキングが不完全なのに完全であるかのように示すことはありません。エミュレーターをまったく一覧表示できない場合でも、ディスク上の AVD は実在するためランキングは実行され、その旨が示されます(--json では warnings)
    • オプション:--suggest、--list、--boot NAME、--headless、--count、--json、--verbose
  4. localization_audit.py - res/values*/strings.xml を監査し、ロケールごとのキーの欠落やプレースホルダーの不一致を検出します。ソースとの相互参照も任意で行えます。

    • オプション:--res DIR、--source DIR、--locale CODE、--strict、--json、--verbose
  5. appearance.py - ダーク/ライトモード(cmd uimode night)とフォントサイズ(settings put system font_scale)を制御します。ロケールの設定はベストエフォートです。

    • オプション:--theme {light,dark}、--text-size {small,default,large,xl}、--font-scale、--locale、--reset、--serial、--json
  6. location.py - adb emu geo fix を使ってエミュレーター上の GPS をシミュレートします(固定座標、都市のプリセット、GPX ルートの再生)。エミュレーター専用です。

    • オプション:--lat/--lng、--city、--gpx FILE、--interval/--speed、--clear、--list-cities、--serial、--json
  7. container.py ⭐ 新規 - adb shell run-as を使ってデバッグ可能なアプリのサンドボックスを調査します(リリースアプリでは明確なエラーになります)。

    • ファイルの一覧表示/読み取り、shared_prefs XML のダンプ、データベースの一覧表示と SQLite スキーマのダンプ(Room == SQLite)、スナップショットのエクスポート
    • オプション:--package、--ls [SUBPATH]、--cat FILE、--shared-prefs [NAME]、--databases [NAME]、--export DIR、--serial、--json
  8. model_inspector.py ⭐ 新規 - Android の永続化機能を調査します:ソース内の Room アノテーション、Room のエクスポート済みスキーマ JSON、run-as を使った実行中の SQLite スキーマ(Room == SQLite)。

    • オプション:--source DIR、--schema PATH、--show-versions、--raw NAME、--package、--db NAME、--serial、--json、--verbose
  9. anr_watcher.py ⭐ 新規 - セッション単位の段階的な情報開示で、logcat から Android の ANR/カクつきを記録して要約します(Choreographer のスキップフレーム + ActivityManager の ANR)。

    • セッションモード:--start [--package PKG] → ID、--stop ID → トークン数を抑えた要約、--get-details ID [--cluster N]、--list-sessions、--clear-sessions、--diff A B
    • 不明なセッション、まだ要約がないセッション、または範囲外の --cluster を指定すると、終了コード 1 で終了し、代わりに実行すべきコマンドを示します(--json では {"error": ...})。メッセージを出力して終了コード 0 で終了することはありません
    • --older-than には 30s/5m/24h/7d を指定します。セッション ID は anr-YYYYMMDD-HHMMSS-XXXX 形式です。どちらかの形式が不正な場合は使用方法のエラー(終了コード 2)となり、--clear-sessions は不正な値を拒否した場合、何も削除しません
    • レガシー:--watch [--duration N]、--since 5m
    • オプション:--watch、--since、--start、--stop、--get-details、 --list-sessions、--clear-sessions、--diff、--package、--serial、 --duration、--min-frames、--top、--all、--budget-tokens、 --cluster、--raw、--older-than、--terse、--json
  10. sms.py ⭐ NEW - エミュレーターに受信 SMS を送信し、届いたことを確認します。エミュレーター専用です。

    • --send は受信トレイを読み戻し、accepted と delivered を別々に報告します。コンソールの OK はコマンドが受け付けられたことを意味し、メッセージが存在することを意味するわけではありません。配信は非同期(実測で約 2 秒)なので、ポーリングします。
    • --otp は最新のメッセージからワンタイムコードを抽出し、そのコードを含むメッセージも表示するため、抽出ヒューリスティックを確認できます。
    • オプション:--send --to NUM --body TEXT [--no-verify]、--list [--limit N]、--otp、--serial、--json、--verbose
  11. snapshot.py ⭐ NEW - エミュレーターのスナップショットを保存/読み込み/削除します(adb emu avd snapshot)。60 秒の再起動に代わり、約 2 秒で状態をリセットできます。エミュレーター専用です。

    • 読み込みに失敗した場合は失敗として報告します。これこそが重要な点です。adb emu は KO を返しても終了コードが 0 になるため、復元されていないのに成功と報告されると、次のテストが不明な状態で実行されてしまいます。
    • オプション:--list、--save NAME、--load NAME、--delete NAME、--no-verify、--timeout、--serial、--json、--verbose
  12. crash_triage.py ⭐ NEW - 専用のクラッシュバッファー(logcat -b crash)を解析して、クラッシュを構造化し、障害ごとにグループ化します。

    • パッケージ+例外+シグネチャフレームで重複をグループ化します(そうしないとクラッシュループで出力が埋め尽くされます)。トリアージに最も役立つフレームを、その選択根拠とともに示します。正直な答えが「アプリのフレームなし。すべてのフレームがフレームワークのコード」の場合も含みます。
    • 終了ステータスが示すのは「トリアージが実行されたか」であり、「クラッシュがあったか」ではありません。--json の crash_count を条件分岐に使うか、--fail-on-crash を指定してください。
    • オプション:--package PKG、--clear、--fail-on-crash、--serial、--json、--verbose
  13. logs.py ⭐ NEW - ログを読み取るための単一のエントリーポイントです。質問に応じて処理を振り分けます。

    • logs.py tail(メインバッファー)→ log_monitor.py、logs.py crashes(-b crash)→ crash_triage.py、logs.py anr(ANR/カクつき。セッションモードを含む)→ anr_watcher.py。
    • 引数はそのまま渡されるため、各動詞で委譲先スクリプトのすべてのフラグを使用でき、各スクリプトも変更なく引き続き呼び出せます。
    • オプション:<verb> [verb options]、--json(振り分け表)、--help
  14. avd.py ⭐ NEW - エミュレーター/AVD のライフサイクル管理のための単一のエントリーポイントです。質問に応じて処理を振り分けます。

    • avd.py list → device_list.py、pick → emulator_selector.py、create → emulator_create.py、start → emulator_boot.py、stop → emulator_shutdown.py、reset(AVD は残してデータを消去)→ emulator_erase.py、delete(AVD を削除)→ emulator_delete.py。
    • reset と delete は破棄する対象が異なるため、別々の動詞です。どちらか判断に迷う場合は、推測せずに確認します。
    • 引数はそのまま渡されるため、各動詞で委譲先スクリプトのすべてのフラグを使用できます。各スクリプト固有の確認フラグ(delete --yes、reset --force)も含まれます。また、各スクリプトも変更なく引き続き呼び出せます。
    • オプション:<verb> [verb options]、--json(振り分け表)、--help

ビルドシステムは gradle/ サブパッケージ(builder、results、cache、config、reporter)にあり、build_and_test.py で使用されます。ANR ウォッチャーのクラスタリング/セッション処理は common/anr_pipeline.py と common/anr_sessions.py にあります。

SDK バイナリの解決処理は common/sdk_tools.py(get_emulator_path())にあります。emulator を単独の名前で実行しないでください。SDK ルートには emulator というディレクトリが含まれているため、$ANDROID_HOME を $ANDROID_HOME/emulator の代わりに PATH に設定すると、execve は FileNotFoundError ではなく PermissionError を発生させます。呼び出し側が通常備えているのは、FileNotFoundError に対する処理です。

Android と iOS の対応関係

iOS ツールAndroid の同等機能ステータス
xcrun simctladb / avdmanager / emulator✓ 完了
IDBadb shell uiautomator / input✓ 完了
iOS SimulatorAndroid Emulator✓ 完了
xcodebuildGradle wrapper✓ 完了
アクセシビリティツリーUI 階層ダンプ✓ 完了
simctl privacypm grant/revoke✓ 完了
xcresultGradle テストレポート / JUnit XML⚠ 基本機能(改善中)

スクリプトのカテゴリ

🚀 必須(日常的に使用)
  • app_launcher.py - アプリの起動/終了
  • screen_mapper.py - 現在の画面を把握
  • navigator.py - UI 要素を操作
  • gesture.py / keyboard.py - ユーザー入力
🔧 開発
  • build_and_test.py - プロジェクトのビルドとテストの実行
  • log_monitor.py - フィルタリングされたログでデバッグ
  • emulator_boot.py / emulator_shutdown.py - デバイス管理
🧪 テスト
  • accessibility_audit.py - アクセシビリティへの準拠を確認
  • visual_diff.py - ビジュアルリグレッションテスト
  • test_recorder.py - テスト実行を記録
  • app_state_capture.py - テスト失敗をデバッグ
⚙️ 高度な機能
  • privacy_manager.py - 権限フローをテスト
  • push_notification.py - シェルとして通知シェードに投稿し、アプリが投稿した内容を確認
  • status_bar.py - 詳細な制御
  • emulator_create/delete/erase.py - CI/CD のプロビジョニング
  • sms.py - 受信 SMS と OTP ログインフローのエンドツーエンドテスト(エミュレーターのみ)
  • snapshot.py - テスト間で約 2 秒で状態をリセット(エミュレーターのみ)
  • crash_triage.py - 専用クラッシュバッファーから構造化されたクラッシュ情報を取得

一般的なワークフロー

手動テストのフロー
# 1. Launch app
python3 "$SKILL_DIR/scripts/app_launcher.py" --launch com.example.app

# 2. See what's on screen
python3 "$SKILL_DIR/scripts/screen_mapper.py"

# 3. Interact
python3 "$SKILL_DIR/scripts/navigator.py" --find-text "Login" --tap
python3 "$SKILL_DIR/scripts/navigator.py" --find-type EditText --index 0 --enter-text "[email protected]"
python3 "$SKILL_DIR/scripts/keyboard.py" --button enter

# 4. Verify
python3 "$SKILL_DIR/scripts/screen_mapper.py"
自動テストのフロー

test_recorder.py はシェルから実行し、各ステップで 1 つのプロセスを起動します。セッションはディスクに保存されるため、コマンド間でプロセスを開いたままにする必要はありません。

# 1. Start recording; the session id is printed, and later steps default to it
python3 "$SKILL_DIR/scripts/test_recorder.py" --start "Login Flow" --app-name MyApp

# 2. Execute test steps, capturing the screen after each interaction
python3 "$SKILL_DIR/scripts/test_recorder.py" --step "Launch app" --screen Splash
# ... interactions ...
python3 "$SKILL_DIR/scripts/test_recorder.py" --step "Verify logged in" \
    --screen Home --assert "Home screen shown"

# 3. Finish: writes report.md + manifest.json (add --failed to mark it failed)
python3 "$SKILL_DIR/scripts/test_recorder.py" --stop
CI/CD のフロー
# 1. Create fresh emulator
python3 "$SKILL_DIR/scripts/emulator_create.py" --device pixel_7 --api 34 --name test-device

# 2. Boot emulator
python3 "$SKILL_DIR/scripts/emulator_boot.py" --avd test-device --wait-ready

# 3. Build and test
python3 "$SKILL_DIR/scripts/build_and_test.py" --project . --test

# 4. Run UI tests
# ... your test scripts ...

# 5. Cleanup
python3 "$SKILL_DIR/scripts/emulator_shutdown.py" --serial emulator-5554
python3 "$SKILL_DIR/scripts/emulator_delete.py" --name test-device
デバッグのフロー
# 1. Capture complete state
python3 "$SKILL_DIR/scripts/app_state_capture.py" --package com.myapp --output debug-snapshots/

# 2. Monitor logs in real-time
python3 "$SKILL_DIR/scripts/log_monitor.py" --app com.myapp --severity error,warning --follow

# 3. Check accessibility issues
python3 "$SKILL_DIR/scripts/accessibility_audit.py" --output audit-reports/ --verbose

要件

  • macOS、Linux、または Windows
  • platform-tools と emulator を含む Android SDK
  • Python 3.12+
  • ADB(Android Debug Bridge)
  • 任意:ビルド用の Gradle
  • 任意:スクリーンショットのサイズ変更用の Pillow

インストール

環境設定
# 1. Install Android SDK (via Android Studio or command line tools)
# Download from: https://developer.android.com/studio

# 2. Set environment variables
export ANDROID_HOME=$HOME/Library/Android/sdk  # macOS
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/emulator

# 3. Verify installation
adb version
emulator -version
Claude Code スキルとして
# As a plugin (preferred; updating is then two commands -- see README.md)
claude plugin marketplace add fluxxion82/android-emulator-skill
claude plugin install android-emulator-skill@fluxxion82

# From a clone: the skill is the INNER directory (this file's own), not the
# repository root, so clone the repo and link that directory into place.
git clone https://github.com/fluxxion82/android-emulator-skill ~/src/android-emulator-skill

# Personal installation
ln -s ~/src/android-emulator-skill/android-emulator-skill/skills/android-emulator-skill ~/.claude/skills/android-emulator-skill

# Project installation
ln -s ~/src/android-emulator-skill/android-emulator-skill/skills/android-emulator-skill .claude/skills/android-emulator-skill

ドキュメント

  • SKILL.md(このファイル) - スクリプトのリファレンス。正確な一覧です
  • README.md - 人間向けの簡潔な概要
  • examples/ - 自動化ワークフローの一式(パッケージに同梱)

ソースリポジトリ内のみ:

  • CLAUDE.md - アーキテクチャ、規約、記録済みフィクスチャのポリシー
  • references/ - adb、テストパターン、アクセシビリティの詳細解説 (リリースパッケージには含まれません)

知っておくべきプラットフォームの制限事項

クリップボードには adb からアクセスできません。 最新の Android では、シェルからクリップボードのテキストを設定する方法はありません。

  • API 33 と 35 では、cmd clipboard は「No shell command implementation」と報告します。クリップボードサービスにはシェルコマンドインターフェースがありません。
  • service call clipboard <setPrimaryClip> も機能しません。この呼び出しは ClipData Parcelable を受け取りますが、そのテキストは writeString8() とバージョン依存のフィールドを使ってマーシャリングされます。一方、service が書き込めるのは i32/s16/バインダーだけです。
  • エミュレーターコンソール(adb emu)にクリップボードコマンドはありません。

読み取りと消去にはアクセスできます。適切なシグネチャを指定した service call clipboard 3/4/9 は、READ_CLIPBOARD_IN_BACKGROUND を持つシェル UID で受け付けられます。しかし書き込みはできないため、貼り付けフローのテストにはアプリ側のフックまたはヘルパー IME が必要です。

エミュレーターに限り、gRPC 制御 API から setClipboard/getClipboard を利用できます($ANDROID_HOME/emulator/lib/emulator_controller.proto を参照)。これには gRPC クライアントとエミュレーターの認証トークンが必要ですが、ここでは接続されていません。

このため、v0.6.0 で clipboard.py は削除されました。残っていたコードパスは、Android 10 以前のシグネチャで service call clipboard 1 を呼び出しており、常に失敗していました。

Jetpack Compose の画面はダンプ上で見え方が異なります。 以下の内容は実際の Compose アプリの記録済みダンプから測定したものであり、推測ではありません。Compose に関するもっともらしい仮定が 3 つありましたが、いずれも間違いでした。

  • ホストクラスは androidx.compose.ui.platform.ComposeView です。 ダンプに AndroidComposeView はまったく現れないため、これを探す検出器は何も見つけられません。
  • リソース ID はありません。 デフォルトの Compose 画面で公開されるのは、AOSP の FrameLayout に属する android:id/content だけです。--find-id で検索できるものはありません。--find-text または screen_mapper を使ってください。
  • インタラクティブなノードには独自のラベルがありません。 クリック可能なノードとチェック可能なノードはすべて、text="" および content-desc="" です。uiautomator はマージされていないセマンティクスツリーをダンプするため、クリック可能な Card では、テキストがひとつのラベルにまとめられず、個別の子ノードとして残ります。どこにも結合処理はありません。screen_mapper はコントロールの子孫(Button、Card、リスト行)と、行に隣接する兄弟ノード(Checkbox、Switch、アイコン)からラベルを復元します。そのため、ダンプでは名前のないコントロールにも名前を付けられます。

独自の Compose アプリをアドレス指定可能にするには、 ツリーのルートでテストタグをリソース ID として有効にしてください。

Modifier.semantics { testTagsAsResourceId = true }   // once, at the root
Modifier.testTag("submit_button")                    // on each control

タグはそのままの resource-id として表示されます。たとえば com.example.app:id/submit_button ではなく submit_button です。リソース ID を ":id/" で分割して名前を取り出す処理は、画面上の Compose タグをすべて取りこぼします。

主な設計原則

セマンティックナビゲーション:ピクセル座標ではなく、意味(テキスト、種類、ID)で要素を検索します。UI の変更にも対応できます。

トークン効率:デフォルトの出力は簡潔(3~5 行)で、詳細を確認する場合は --verbose、機械可読な出力を得る場合は --json を使えます。ツールの生出力をパイプで渡す場合に比べて大幅に削減できますが、まだ測定されていません。根拠のない削減率ではなく、予算表の作成を予定しています。

アクセシビリティ優先:信頼性と互換性を高めるため、標準のアクセシビリティ API を基盤としています。

設定不要:Android SDK がインストールされていれば、すぐに動作します。複雑なセットアップは必要ありません。

構造化データ:スクリプトは生ログではなく、JSON または整形済みテキストを出力します。解析や統合が簡単です。

クロスプラットフォーム:macOS、Linux、Windows で動作します。

実機対応:iOS とは異なり、エミュレーターと実機の両方で動作します。

Android 固有の機能

実機対応:エミュレーターと、USB/Wi-Fi で接続された実機の両方で動作します。

複数のエミュレーター:複数のエミュレーターを同時に実行し、一括操作できます。

柔軟なアーキテクチャ:x86_64 と ARM のエミュレーターアーキテクチャの両方で動作します。

Gradle との統合:Android の Gradle ビルドシステムとネイティブに統合します。

高度なログ機能:正規表現、重大度、アプリごとの対象指定で Logcat をフィルタリングできます。

権限のテスト:実行時権限をプログラムから付与/取り消しできます。

通知のテスト:シェルとして通知を通知シェードに投稿し、パッケージが実際に投稿した内容を読み取ります。アプリ独自の FCM ハンドラーへの配信は adb からは行えません。上記の push_notification.py を参照してください。

ステータスとロードマップ

v0.7.0 — このスキルが出力した内容からエージェントのループが検証されます。 以前のリリースでは、修正が特定の行に存在することを確認していました。今回はループが完結することを確認します。テストでは screen_mapper の出力を読み取り、出力された名前をそれぞれ navigator に渡すため、「実装されているが到達不能」な場合は失敗します。

  • screen_mapper はデフォルトレポートでバケットごとに最大 15 個のコントロール名を出力し(全インベントリは --json に含まれます)、navigator は出力された名前をすべて見つけてタップします — ダンプでは名前のない Jetpack Compose のキャプションも含みます。これらは、それを所有するコントロールに対応付けられます。唯一の外側の「所有者」がスクロールコンテナであるキャプションは、コンテナの中央をタップするのではなく、拒否されます。
  • 引数なしの --tap は拒否されます。 タップは、名前が示す矩形の内側に行われます。テストでは独自のパーサーを使ってダンプからその矩形を解決するため、navigator 自体ではなく画面に対して検証されます。
  • --launch と --open-url はアクティビティを待機します — am start の終了直後に返るのではなく、Status: ok を必須とする am start -W を使用します。
  • emulator_shutdown は端末の電源を切れません。 reboot -p は削除され、エミュレーターではないシリアルは、何も実行される前に拒否されます。すべての呼び出し元で同じ三状態プローブを使って「このシリアルはどの AVD か」を判定するため、「デバイス一覧の取得自体に失敗した」ことが「実行中ではない」と解釈されることはなくなりました。
  • デバイスシェルに渡される値はすべて引用符で囲まれます — 全 19 箇所をガードで個別に明示的に列挙するため、盲目的な検出器が「引用符で囲まれていない箇所はゼロ」と誤判定することはありません。
  • 失敗するすべてのモードはゼロ以外で終了します。 ランタイムの一括テストでは、すべてのツールが失敗するツールチェーンを使って、16 個のスクリプトに対する文書化済みの呼び出し 23 件を実行し、メッセージではなく終了ステータスを検証します。JSON の失敗時の形式はまだ統一されておらず、一括テストでも検証していません。app_launcher、screen_mapper、navigator、anr_watcher、app_state_capture、device_list、emulator_* ライフサイクルスクリプトは --json で {"error": ...} を返します。一方、status_bar、appearance、privacy_manager、keyboard、gesture、location、emulator_create、build_and_test は {"success": false, "message": ...} を返します。
  • SDK ツールが見つからない場合、ライフサイクルスクリプトはその旨を通知します — device_list、emulator_boot、emulator_delete、emulator_erase、emulator_selector は common/sdk_tools.py を経由し、ツールを探した場所を示して、空のリストを返す代わりに cmdline-tools での解決方法を提示します。ツールが実行され、本当に何も見つからなかった場合は、終了コード 0 を返します。(device_list では、avdmanager が見つからない場合は警告にとどめます。エミュレーターがすでに一覧表示した AVD に情報を付加する場合にのみ使用するためです。)

修正は引き続きフィクスチャ駆動です。tests/fixtures/recorded/ には、2 つの API レベルで実機から取得した出力がそのまま保存され、パーサーのテストで使用されます。既知の不具合がある場合は xfail(strict=True) で固定し、修正時にはマーカーの削除を必須にします。v0.7.0 では固定された不具合はありません。ソースリポジトリの CLAUDE.md を参照してください。

既知の課題(次のリリース向け):

  • build_and_test は Gradle の終了コードだけで判定するため、ignoreFailures = true でビルドされたプロジェクトでは、テストが失敗していても成功として報告されます。
  • log_monitor --severity は指定された名前を検証しません。認識されない名前(たとえば warn)は何にも一致しないため、キャプチャ結果が何も表示されないままフィルタリングされます。
  • app_state_capture は優先度列を読み取らず、自由記述のどこかに「error」または「exception」という単語が含まれていると、logcat の行をエラーとして数えるため、エラーに関する説明文によって件数が増えます。
  • anr_watcher は、ANR の Reason: / PID: 続きの行を、その直前の ANR in イベントに紐付けません。そのため、1 つの ANR が 2 つのクラスタとして表示されることがあります。
  • status_bar --time にはゼロ埋めした値を指定する必要があります。9:41 ではなく 09:41 です。この値は SystemUI のデモモードに hhmm として渡されます。ゼロ埋めされていない値は無視されますが、スクリプトは成功を報告します。
  • accessibility_audit にはコントラストチェックがありません。コントラストチェックにはスクリーンショットからのピクセルサンプリングが必要ですが、これは実装されていません。
  • privacy_manager が操作するのはデフォルトユーザーのみです。--user を渡さないため、仕事用プロファイルやセカンダリユーザーにはアクセスできません。
  • emulator_create は、avdmanager または sdkmanager が見つからない、あるいは失敗した場合も、空のリストを返します。そのため --list-devices と --list-images は何も表示せずに終了コード 0 で終了します。これは、上記のライフサイクルスクリプトではもう使われていない形式です。
  • JSON の失敗時の形式が {"error": ...} なのは、このリリースで改修されたスクリプトのみです。残るグループは 2 つあります。status_bar、appearance、privacy_manager、keyboard、gesture、location、emulator_create、build_and_test は {"success": false, "message": ...} を返します(snapshot、sms、container は両方のキーを同時に出力します)。また、navigator、log_monitor、crash_triage、status_bar は、到達不能なデバイスを検出すると JSON を生成する前に処理を拒否し、stderr に説明文を出力しますが、JSON は出力しません。これらの統一は次の課題です。
  • 計測テストには対応していません。ここにあるスクリプトは connectedAndroidTest を実行せず、ビルドおよびテスト用スクリプトはユニットテストを実行します。

修正ではなく削除:clipboard.py — 上記の「プラットフォームの制限」を参照してください。最新の Android では adb からクリップボードに書き込めないため、このスクリプトを宣伝どおりに動作させることはできません。

コントリビューション

新しいスクリプトは、以下の要件を満たしてください。

  • ロジックが 50 行を超える場合は、クラスベースの設計を使用する
  • --serial と自動検出に対応する
  • --json 出力に対応する
  • --help のドキュメントを提供する
  • Black および Ruff の規約に従う
  • この SKILL.md を更新する
  • 提出前に実際のエミュレーターでテストする

iOS との違い

アーキテクチャ
  • iOS:UI 操作には IDB、デバイス管理には xcrun simctl を使用します
  • Android:すべてに adb、UI 操作には uiautomator を使用します
要素の種類
  • iOS:Button、TextField、SecureTextField、StaticText など
  • Android:Button、EditText、TextView、ImageView など
デバイス管理
  • iOS:シミュレーターのみ(macOS が必要)
  • Android:エミュレーターと実機(クロスプラットフォーム)
ビルドシステム
  • iOS:Xcode プロジェクトファイル(.xcodeproj)
  • Android:Gradle ビルドファイル(build.gradle)

ステータス:ios-simulator-skill と同等にするため、現在も積極的に近代化を進めています。上記の「ステータスとロードマップ」セクションを参照してください。

これらのスクリプトを直接使用するか、依頼内容がスキルの説明に合致する場合は Claude Code に自動実行させてください。

コメント