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

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">物理ボタンを押したらモンスターボールを投げるようにしてみた。<br>そのうちQiitaにでもやり方をまとめます。 <a href="https://twitter.com/hashtag/%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3Go?src=hash">#ポケモンGo</a><a href="https://t.co/yVHsGLXbvP">https://t.co/yVHsGLXbvP</a> <a href="https://t.co/rDqVbk9Zfn">pic.twitter.com/rDqVbk9Zfn</a></p>&mdash; Akio みかえる Kondo (@kojira) <a href="https://twitter.com/kojira/status/763983414546444288">2016年8月12日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

せっかく[Pochiru](https://ssl.braveridge.com/store/html/products/detail.php?product_id=30)を頂いたので、モンスターボールを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 )のコードを使用しています。

#<i class="fa fa-cube" style="font-size:1em;"></i> まずは物理ボタン無しでボールを投げる

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

adbコマンドで、

```shell-session
$ adb tcpip 5555
```

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

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

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

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

<img width="360" alt="アプリメイン画面" src="https://qiita-image-store.s3.amazonaws.com/0/1213/769c506e-a47c-5d01-e938-33bbe8929eaa.png">

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

<img width="360" alt="USBデバッグ許可画面" src="https://qiita-image-store.s3.amazonaws.com/0/1213/8be1bf8b-6de2-39d3-5480-440e4d54f6f7.png">

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

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

<img width="360" alt="USBデバッグ許可画面" src="https://qiita-image-store.s3.amazonaws.com/0/1213/daf87e62-aca6-0a69-d974-63a86a259a09.png">

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

あとはボタンを押すと

```shell-session
/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接続の時は

```shell-session
adb usb
```
でUSBモードに戻しておくほうが無難です。

#<i class="fa fa-cube" style="font-size:1em;"></i> 物理ボタンでボールを投げる

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

- Pochiruを入手する(https://ssl.braveridge.com/store/html/products/detail.php?product_id=30 )
- Linkingアプリをインストール(https://play.google.com/store/apps/details?id=com.nttdocomo.android.smartdeviceagent )
[この記事](http://qiita.com/kojira/items/a8cdf4b499efb06887ea)を参考に。

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

<img width="360" alt="Linking画面" src="https://qiita-image-store.s3.amazonaws.com/0/1213/e8bbee7b-59b3-e8eb-3178-3f6cb8f4ea5f.png">

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

<img width="360" alt="連携アプリ画面" src="https://qiita-image-store.s3.amazonaws.com/0/1213/c4eac046-a8f0-91c2-d967-463e24bfb488.png">

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

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

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

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

* AndroirdManifest.xml

```xml
   <meta-data
            android:name="com.nttdocomo.android.smartdeviceagent.feature.support"
            android:value="true" />
```

* オーバーレイ表示させているserviceのコード抜粋

```java
    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がエアコンを操作してくれるとか、無駄にピタゴラスイッチができそう！（※エアコンのリモコンでいいというツッコミは無しで。）
後は奥歯に嵌め込んだスイッチをカチッとするとスマホに入っているアプリや機密情報を削除するとか！

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