Android
Linking
Pochiru
ポケモンGo

物理ボタンを押してモンスターボールを投げる方法

More than 1 year has passed since last update.

せっかくPochiruを頂いたので、モンスターボールをPochiruのボタンを押すことで投げるようにしてみました。
今回の記事はその実装方法です。

※100%エクセレントが取れるわけでもなく、わざわざボタンで投げることに利便性はほぼありませんが。

Androidアプリのソースコードはここに置いています。
https://github.com/kojira/excellent-ball
※Android 6.0のNexus 5Xでのみ動作確認済み。

最近のAndroidはセキュリティに厳しくなっていて、簡単にキーイベントを送ることができなくなっているので、以前からやりたかった、アプリから自分自身にadb接続してshell権限をゲットしてコマンドを送信する方法を使いました。

pure javaのadbライブラリ(https://github.com/cgutman/AdbLib )のコードを使用しています。

まずは物理ボタン無しでボールを投げる

Androidの開発者モードを有効にし、デバッグ可能な状態にします。

adbコマンドで、

$ adb tcpip 5555

と打ち、adbの接続モードをtcpipにしておきます。

アプリのソースコードをclone。

git clone https://github.com/kojira/excellent-ball.git

Android Studioで、先ほどcloneしたプロジェクトを開いて、実行します。

アプリメイン画面

上記の画面が出るので「開始ボタン」を押します。

USBデバッグ許可画面

初回実行時は「他のアプリに重ねて描画することを許可」をONにしてバックキーで戻ります。

※「他のアプリに重ねて描画することを許可」はadbの仕組みとは本質的には関係がありませんが、ポケモンGoの画面にボタンをオーバーレイ表示させるために必要です。

するとUSBデバッグ許可の画面が出ます。(tcpipモードなのにUSBデバッグとはこれ如何に。)

USBデバッグ許可画面

ここでOKを押します。

※「このパソコンからのUSBデバッグを常に許可する」のチェックを入れてもアプリの再起動のたびに出てきてしまいます。フィンガープリントも毎回異なるようです。使用したpure javaのadbライブラリに再接続時の仕組みが実装されていないのかもしれません。(未調査)

あとはボタンを押すと

/system/bin/input swipe 540 1700 540 820

などのコマンドを送信します。
送信してから実際にスワイプ操作が送られるまでそこそこラグがあるのはシステムの問題のようです。

※Android 4.4時代くらいまでは cat /dev/input/event0 > log.bin でinputログが保存でき、保存したバイナリログを cat log.bin | /dev/input/event0 などとやれば、あまりラグなしで操作を再現できたのですが。(時系列情報が含まれていないので入力としては速すぎるので調整が必要)

shell権限で動作するため、デバッグで使用できる機能のすべてが利用可能です。
電車の中だとためらってしまうスクリーンショットを無音で取ったりすることもできます。
screenrecordコマンドを使って端末操作の動画を取ることも可能です。
ネット上からapkファイルを引っ張ってきて、アプリをバックグラウンドでインストールすることすら出来てしまいます。悪用厳禁。
夢が広がるなあ( `・ω・´)
ただ、tcpipモードで繋がるようにしておくのは接続時に確認ダイアログが出るとはいえ、セキュリティ上の不安があるので、他人が接続する可能性のあるWiFi接続の時は

adb usb

でUSBモードに戻しておくほうが無難です。

物理ボタンでボールを投げる

まず、以下の2点が必要です。

Linkingアプリを起動してPochiruのデバイスと接続しておきます。

Linking画面

連携アプリの項目の「Excellent Ball」のスイッチをONにします。

連携アプリ画面

この状態で、最初の手順で画面上のボタンを押す代わりに、物理ボタンを押すだけでswipeコマンドが送られるようになります。

Linkingを使った物理ボタンへ対応させるこコードは

  • AndroirdManifest.xml
  • 指定のIntentFilterのBroadcastReceiverを登録
  • BroadcastReceiverで入ってきたbutton IDを元に処理を分岐

の3点だけです。コード量にして34ステップほど。

  • AndroirdManifest.xml
   <meta-data
            android:name="com.nttdocomo.android.smartdeviceagent.feature.support"
            android:value="true" />
  • オーバーレイ表示させているserviceのコード抜粋
    private void register() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(PairingConst.ACTION_NOTIFY);
        registerReceiver(localReceiver, filter);

    }

    BroadcastReceiver localReceiver = new BroadcastReceiver() {
        int buttonId = -1;

        @Override
        public void onReceive(Context context, Intent intent) {
            buttonId = intent.getIntExtra(PairingConst.EXTRA_BUTTON_ID, -1);

            switch (buttonId) {
                default:
                    break;
                case 2://Single Click
                    sendCommand(0);
                    break;
                case 4://Double Click
                    sendCommand(1);
                    break;
                case 7://Long Click
                    sendCommand(2);
                    break;
            }
        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(localReceiver);

とっても簡単!
ボタンを押してからの反応速度が遅いという問題があるものの、気軽に物理デバイスと連携できるのは便利ですね!

ボタンを押すだけでスマホと連携したIRKitがエアコンを操作してくれるとか、無駄にピタゴラスイッチができそう!(※エアコンのリモコンでいいというツッコミは無しで。)
後は奥歯に嵌め込んだスイッチをカチッとするとスマホに入っているアプリや機密情報を削除するとか!

上記例はともかく、アイディア次第で実用的なものもできそうですね。