いつでも音楽を巻戻しできるアプリを書いてた

電車の中でAndroid端末を使っていて音楽を聞きつつTwitterをだらだら見たりしていて、急に曲の一部をもう一回聞きたくなるようなことが度々あった。

なのでそういったとき用にバックグラウンドで動きつつ画面にボタンが出てて巻戻しとか早送りとか再生/一時停止ができるようなアプリを書こうと思った。


問題は別のアプリから音楽プレーヤをどうやって制御するかだった。結局再生ボタンとか巻戻しボタンが押されたのと同じ処理をさせればいいので、KeyEvent.KEYCODE_MEDIA_PLAY_PAUSEとかKEYCODE_MEDIA_REWINDとかのキーが押されたことにしてしまえばいいと思った。そんなわけで、「キーイベントを発生させる方法」調べたところ("Android キーイベント 発行"とかでぐぐった)、Instrumentationを使えばできるよ的な記事が目について、これで行けるかと思ってたんだけどダメだった。


Instrumentation#sendKeyDownUpSync は、INJECT_EVENTSパーミッションを要求してくるんだけど、INJECT_EVENTSはシステムアプリでしか使えないのだった。


なのでもう無理かと思っていたんだけど、ヘッドセットからの操作はキーイベントではなく、"android.intent.action.MEDIA_BUTTON"のIntentがBroadcastされてくるということなので、逆にそのIntentを生成してBroadcastしてやると音楽プレーヤが制御できるということがわかった。
具体的には次のようなコードでできた。

    private void sendKeyEvent(int keycode, int action, int repeat) {
        long now = System.currentTimeMillis();
        KeyEvent ev = new KeyEvent(now, now, action, keycode, repeat);
        Intent intent = new Intent("android.intent.action.MEDIA_BUTTON");
        intent.putExtra("android.intent.extra.KEY_EVENT", ev);
        sendOrderedBroadcast(intent, null);
    }

これでKEYCODE_MEDIA_PLAY_PAUSEとか、KEYCODE_MEDIA_STOPなんかはACTION_DOWNとACTION_UPを連続して送ってやればOKだった。KEYCODE_MEDIA_REWINDやKEYCODE_MEDIA_FAST_FORWARDなどは長押しが前提になっているようだったので、ボタンが押されている間一定周期でACTION_MULTIPLEを送ってやる必要があった。


これまで音楽プレーヤアプリを開かないとできなかった巻戻し/早送りが、常に表示されているようになったのでちょっと便利になりそうだが、常時表示はウザいかもしれないとも思う。いまはボタンのデザインがダサすぎてあまり表示させておきたくない感じ。