Android端末にGetWildを歌わせる

[GetWild Advent Calendar 2016](http://qiita.com/advent-calendar/2016/getwild)の6日目です！！

## はじめに
このカレンダーに参加を決めて何を書くか考えている時に、パッと浮かんだのはこちらの記事でした！

- [カフェでトイレに行ってるときに、Macが盗まれそうになるとGet Wildが流れるようにする](http://qiita.com/sion_cojp/items/276d0135216b88e5314b)

そこで、同じようにAndroid端末が盗まれそうになったらできないかなーと思い、Wearと連携させることで実現できるのではないかと開発を始めました。

## 概要
- AndroidMobileに接続されているWearから歌をリクエストするとMobileが歌います
- AndroidMobileがWearから離れすぎて接続が切れるとMobileが歌います

## 動画
<blockquote class="twitter-video" data-lang="ja"><p lang="ja" dir="ltr"> <a href="https://t.co/pwUvb6jc5G">pic.twitter.com/pwUvb6jc5G</a></p>&mdash; futabooo (@futabooo) <a href="https://twitter.com/futabooo/status/805249365103128576">2016年12月4日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

## コード
### Mobile側
Mobile側で特定のServiceを起動している状態でのみ歌うようにします。
ServiceがWearからのMessageを受け取れるようにManifestに下記を追加します。

```AndroidManifest.xml
<service android:name=".GetWildListenerService">
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED"/>
        <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
            <data
                android:host="*"
                android:scheme="wear"/>
    </intent-filter>
</service>
```

dataタグの中ではoptionとして`android:pathPrefix="/prefix"`も使えるので、もっと細かいことがしたい場合は使えそうです。
[WearableListenerService](https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService)


GetWildListenerServiceはこんな感じです。
歌うときだけ音量MAXにしています。
`onPeerDisconnected`と`onMessageReceived`がそれぞれMobileとWearの接続が切れたとき、Messageを受け取った時に呼ばれるメソッドです。

```GetWildListenerService.java
public class GetWildListenerService extends WearableListenerService {

    private TextToSpeech tts;

    private AudioManager audioManager;
    private int originalVol;
    private int maxVol;

    @Override
    public void onCreate() {
        super.onCreate();

        // 歌う時に音量MAXにするために
        audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        originalVol = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        maxVol = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);

        tts = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                Log.d(TAG, "tts onInit");
            }
        });
    }

    @Override
    public void onDestroy() {
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, originalVol, 0);
        super.onDestroy();
    }

    @Override
    public void onPeerDisconnected(Node node) {
        super.onPeerDisconnected(node);
        Log.d(TAG, "onPeerDisconnected");
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVol, 0);
        startSing();
    }

    @Override
    public void onMessageReceived(MessageEvent messageEvent) {
        super.onMessageReceived(messageEvent);
        Log.d(TAG, "onMessageReceived");
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVol, 0);
        startSing();
    }

    private void startSing() {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
        if (!sp.getBoolean("isEnable", false)) {
            return;
        }

        getWildAndTough(Locale.ENGLISH, getString(R.string.lyrics_get_wild_and_tough));
        getWildAndTough(Locale.JAPAN, getString(R.string.lyrics_1));
        getWildAndTough(Locale.ENGLISH, getString(R.string.lyrics_get_wild_and_tough));
        getWildAndTough(Locale.JAPAN, getString(R.string.lyrics_2));
        getWildAndTough(Locale.ENGLISH, getString(R.string.lyrics_get_chance_and_luck));
        getWildAndTough(Locale.JAPAN, getString(R.string.lyrics_3));
        getWildAndTough(Locale.ENGLISH, getString(R.string.lyrics_get_chance_and_luck));
        getWildAndTough(Locale.JAPAN, getString(R.string.lyrics_4));
    }

    private void getWildAndTough(Locale locale, String string) {
        // 日本語遅すぎなので歌っぽくするために
        if (locale == Locale.JAPAN) {
            tts.setSpeechRate(2.0f);
        } else if (locale == Locale.ENGLISH) {
            tts.setSpeechRate(1.0f);
        }

        tts.setLanguage(locale);
        tts.speak(string, TextToSpeech.QUEUE_ADD, null, "1");
    }
}
```

### Wear側
Waer側からはMobileに対してメッセージを送る実装をします。
`sendMessage`の`"/get-wild"`がAndroidManifestでoptionとして追加可能なprefix部分です。

```WearMainActivity.java
public class WearMainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.wear_activity_main);

        WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                Button button = (Button) stub.findViewById(R.id.button);
                button.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                // Mobile側へメッセージを送る
                                GoogleApiClient client = new GoogleApiClient.Builder(getApplicationContext()).addApi(Wearable.API).build();
                                client.blockingConnect(100, TimeUnit.MILLISECONDS);
                                NodeApi.GetConnectedNodesResult result = Wearable.NodeApi.getConnectedNodes(client).await();
                                List<Node> nodes = result.getNodes();
                                if (nodes.size() > 0) {
                                    client.blockingConnect(100, TimeUnit.MILLISECONDS);
                                    Wearable.MessageApi.sendMessage(client, nodes.get(0).getId(), "/get-wild", null);
                                    client.disconnect();
                                }
                                client.disconnect();
                            }
                        }).start();
                    }
                });
            }
        });
    }
}
```

## おわりに
どれぐらいの距離を離れると接続が切れるのか試すことができませんでしたorz
Bluetoothの接続可能な距離って結構長いんですねｗ
TextToSpeechのチューニングしだいでもっと歌っぽくできそうです。

今回作ったものはここに置いてあります
https://github.com/futabooo/getwild

## 参考
https://developer.android.com/reference/android/speech/tts/TextToSpeech.html
https://developer.android.com/training/wearables/data-layer/messages.html