たくさんの自由帳

Xiaomi の端末で adb install ができないので代替案を考える

投稿日 : | 0 日前

文字数(だいたい) : 2970

どうもこんばんわ。Google I/Oもうすぐってマジ?
Android 開発のサイトを見るとAndroid 17の新機能(新API)があんまり書かれてないのですが、API 追加差分を見るとそこそこ増えている。

たとえばMediaProjection(画面共有API)に、既存の画面共有に加えてアプリコンテンツプロジェクション?とか言うのが追加されたのですが、↑で取り上げられてないため何者なのかは分かっていません。
あとは、ついにSurfaceViewをぼかす?APIが追加される模様。やった!!!でもAndroid 17以降しか使えない悲しみも同時に襲われる。

これらがGoogle I/Oで発表があるかは謎です。

本題

Xiaomiのスマートフォンは、なぜかadb installするためにはUSB デバッグとは別の設定を有効にする必要があるのですが、
この設定を有効にするにはSIM カードを刺したりXiaomiのアカウントが要求される謎の仕様がある。

パソコンからapkをインストールしたいが、SIMカード刺したりアカウントうんぬんは出来ないとして、adb install以外の方法を考える必要がある。

先に答え

apkファイルを転送→APK ファイルを処理できるサードパーティーアプリのActivityを開く→そのサードパーティーアプリ経由でapkをインストールする。
この方法のデメリットは、apkをインストールしてくれるサードパーティーアプリを探す手間があるのと、画面でいちいちインストールボタンを押さないといけない点だが、
必要な手順の半分くらいはショートカットできた、

必要なもの

apkファイルをActivityで受け入れてインストールをしてくれるアプリを探す必要があります。
SAIなら<intent-filter>で受け入れてインストールしてくれてそうです。てかこれくらいなら自分で作ってもよいかもしれませんね?

grant_file_permission

AndroidStudio を使っている

Android開発の場合は、build.gradle.ktsにこのGradle タスクを書けばよい。build.gradleをまだ使ってる?そんな、、、
このタスクをAndroid Studioで実行すれば、二回目以降は再生ボタンを押したときの挙動がこのタスクの実行になる。

ただ、:appモジュール(Android Studio初期状態のやつ)の、プロダクト フレーバーも使ってない状態のコードなので、
assembleDebugの部分も、apkのパスも修正が必要かもしれない。

cmdWindowsなのでmacOSとかだと使えないな

tasks.register<Exec>("xiaomiAdbInstallAlternative") {
    // app モジュール想定
    // デバッグ APK を作る
    dependsOn("app:assembleDebug")
    // APK を探す
    val apkFile = project.project("app").projectDir
        .resolve("build")
        .resolve("outputs")
        .resolve("apk")
        .resolve("debug")
        .resolve("app-debug.apk")
    // ADB を探すために local.properties を見る
    val properties = project.rootProject.file("local.properties").inputStream().use { inputStream ->
        java.util.Properties().apply {
            load(inputStream)
        }
    }
    val sdkFolder = File(properties["sdk.dir"].toString())
    val adbBin = sdkFolder.resolve("platform-tools").resolve("adb.exe")
    // apk を転送して、APK を受け入れる Activity を開く
    commandLine("cmd", "/c", "${adbBin.path} push ${apkFile.path} /sdcard/app-debug.apk && ${adbBin.path} shell am start -d file:///storage/emulated/0/app-debug.apk -a android.intent.action.VIEW -t application/vnd.android.package-archive")
    // この先は Android 端末でインストールする...
}

android_studio_edit_build_gradle_kts

android_studio_run_gradle_task

実行すると、こんな感じにSAIがインストールするか聞いてくれるので、インストールボタンを押す。この工程はたぶんスキップできないため、adb installをちゃんと使えるようにする方が良いかと。

install_app_from_sai

これの良くないところはAPK転送とActivity起動のコマンドを無理やりつないでcommandLine()に渡してますが、
たぶん正解はtasks.register()APK 転送Activity 起動で分けるのだと思います。

シェルスクリプトなら使える

もしマルチプラットフォームでVSCodeを使っている場合などは、こんな感じのシェルスクリプトを書けばよいはず。
以下のシェルスクリプトWindowsGit Bashで動くのをみました。bat ?Windowsユーザーなのに書いたことなくshです!

LinuxとかmacOSの場合、たぶんパス区切り~とかの部分をすっ飛ばせるはず。sed 's/\\\\/\//g' | sed 's/C\\:/\/c/g'の部分。そもそもここやらないとだめなんですかね(?)

マルチプラットフォーム、ろくに触ったことないから何のコマンドでデバッグ APK作れるのか知らない。gradlew直接呼ぶの違う気がしてきた。
APKのパスとかも皆さんの環境に合わせてください。

# デバッグ APK をつくるコマンド
./gradlew assembleDebug
# local.properties から ADB のパスを探す
GRADLE_MODULE_NAME='app'
# sdk.dir= を消し、パス区切りを修正し、C\: を /c/ にする
SDK_PATH=$(cat local.properties | sed -n '$p' | sed 's/sdk.dir=//' | sed 's/\\\\/\//g' | sed 's/C\\:/\/c/g')
APK_PATH="${GRADLE_MODULE_NAME}/build/outputs/apk/debug/app-debug.apk"
ADB_PATH="${SDK_PATH}/platform-tools/adb.exe"

# 転送する
# 先頭の // の出所はここ → https://stackoverflow.com/questions/16344985
"${ADB_PATH}" push "${APK_PATH}" "//sdcard\app-debug.apk"

# APK を処理できる Activity を立ち上げる
${ADB_PATH} shell am start -d file:///storage/emulated/0/app-debug.apk -a android.intent.action.VIEW -t application/vnd.android.package-archive

ことのはじまり

adb installできないし、apkを転送してファイルマネージャーで開くのもつらい。
かといってadb installを使えるようにするのもめんどい。adb installを無理やり有効にする案があるとかないとからしいんですが、一旦ここから離れた案を探したほうがよさそう。

普通に APK ファイルを入れることはできる

adb installだけを弾いてるっぽいので、まあよく考えたらできる。
ほなAPKをインストールしてくれる(APKインストールのダイアログを開いてくれるアプリ)を探せばよさそう。

作戦

これがGradleとかシェルスクリプトでやってることです!

デバッグ APK 作成Android 端末の適当なところに転送→APK インストール ダイアログを開いてくれるアプリを立ち上げる

で最後に、人間がインストールボタンを押す

おわりに

adbPATHは通ってるはず(もしくは通してるはず)なので、Android SDKの中のplatform-tools/adb.exeを探すことなくadbコマンドを叩けるはずです。
私が書いたコードは無駄にlocal.propertiesからadbを探しているのでたぶん無駄です。