Android Wear開発まとめ

[このドキュメント](http://developer.android.com/intl/ja/training/building-wearables.html)を読んだので超ざっくりまとめる。
基本的に、この機能はこの為にありますとか、こういうデザインにしましょうという話はしない。
[デザインのガイドライン](http://developer.android.com/intl/ja/design/wear/index.html)があるので一度目を通しておくといいかも。
あとツールの使い方とか、細かい手順も省略。

# 通知編

※Androidに以前からあった通知機能については触れないので悪しからず

Handheld(電話とかタブレット)とWearableが接続してると、Handheldの通知が自動でWearableにも共有される(とは言え、Wearable固有の機能性を通知に追加するとUX的にいいよ)。

## 通知を作る

* `NotificationCompat.Builder`で通知を作る(従来の方法と同じ)
* `NotificationCompat.Builder#addAction`でアクションボタンを追加できる(従来の方法と同じ)

### Wearableだけのアクションボタン

* `NotificationCompat.WearableExtender`というクラスをインスタンス化して使う
* `WearableExtender#addAction`に(従来の方法で作った)`NotificationCompat.Action`を渡す
* `NotificationCompat.Builder#extend`に`WearableExtender`を渡す
* 一度でもこの方法でアクションボタンを追加すると`NotificationCompat.Builder#addAction`で追加されたアクションボタンはWearableの方では表示されなくなる

### Big View

* `BigTextStyle`または`InboxStyle`のスタイルが適用された通知は、Wearable側でもいい感じに表示される
* スタイルの設定の仕方は、従来のやり方と同じ

### 通知にWearable固有の機能を追加する

先に少し触れたけど`WearableExtender`というクラスをインスタンス化して使う。これに対して必要なプロパティを設定していく。そしてこの`WearableExtender`インスタンスを`NotificationCompat.Builder#extend`でセットすればOK。

### 通知を伝える

* `NotificationManagerCompat`を使う
* `NotificationManager`だと`WearableExtender`で追加した機能が動かないよ

## 音声入力を受信する

* メールの返信とかテキストを入力するアクションを含んだ通知では、普通はアクティビティを起動してテキストを入力していく
* そのような通知がWearableにあるとき、キーボードからの入力じゃなくてユーザにしゃべらせたり事前に定義しといたテキストを選んでもらうことができる
* そのテキストが付加されたインテントをhandheldのアプリに送信できるよ
* エミュレータでは音声入力できないのでキーボードで入力する

### 音声入力の定義

* `RemoteInput.Builder`をインスタンス化して使う
* コンストラクタは文字列を受け取る: 入力されたテキストを取得するために使うKey
* `RemoteInput.Builder#setChoices`に`CharSequence`配列を渡すと、それが事前定義されたテキストとしてユーザは音声入力の代わりに選択できる

### 通知のアクションとして音声入力を追加する

* `RemoteInput`オブジェクトを`NotificationCompat.Action.Builder#addRemoteInput`に渡すことでアクションに音声入力を追加できる

### 文字列として音声入力を受け取る

* アクションのインテントに`RemoteInput.getResultsFromIntent`を適用して得られる`Bundle`から取得する
* その`Bundle`から、`RemoteInput.Builder`のコンストラクタ呼び出し時に渡したKeyで音声入力を文字列として取得する

## 通知にページを追加する

1. `NotificationCompat.Builder`を使って従来と同じ方法で通知を作る(これがWearable側でのメイン、つまり最初のページになる)
2. `NotificationCompat.Builder`を使ってWearable用の追加ページとして`Notification`を作る
3. `WearableExtender`の`addPage`メソッドや`addPages`メソッドを使って複数ページのある`Notification`を作る

## 通知をスタックする

通知をスタック内にグルーピングする方法。

### 通知をグループに追加する

* `NotificationCompat.Builder#setGroup`を使って当該の通知をグループに追加する
* `setGroup`は引数にGroupKey(`String`)を取る
* ビルドした通知は普通に`NotificationManagerCompat#notify`で発行する
* 後ほど同一GroupKeyを持った別の通知が発行された際には、同じスタック内に表示される

### Summary Notificationを追加する

* Summary Notificationは、Wearable側では表示されずHandheld側でのみ表示される
* `NotificationCompat.Builder#setGroupSummary`に`true`を渡すとそれはSummary Notificationになる
* グループのSummary(要約)となる通知なので、もちろん`setGroup`でGroupKeyを指定すること
* Summary NotificationはWearableには表示されないけど影響を与える: `WearableExtender`で`setBackground`や`addAction`の呼び出しなど(例えばスタック全体の背景を指定したいとき)

# Wearable用アプリ編

基本的に普通のアプリと同じ。
だけどデザイン、ユーザビリティ、機能の量で大きく異なるので注意。
↓主な違い

* アクティビティはずっと表示できない。スリープから復帰したらWearホーム画面が表示される。ずっと表示したいのがあるときは通知でやること。
* WearableはHandheldと比べるとサイズも機能も小さい。なので操作は極力Handheldで行い、結果をWearableに送る感じで。
* ユーザはWearableに直接アプリをダウンロードしない。WearableアプリはHandheldアプリにバンドルされてて、Handheldアプリがインストールされるときにシステムが自動でWearableアプリもインストールしてくれる。開発のために直接Wearablにインストールすることはできる。
* Wearableアプリは標準のAndroid APIの大部分にアクセスできるけど、次に挙げるのはダメ
    * android.webkit
    * android.print
    * android.app.backup
    * android.appwidget
    * android.hardware.usb

## Wearableアプリを作って動かす

Android Wearエミュレータ、デバイスのセットアップやプロジェクト作成、インストールについては[こちら](http://developer.android.com/intl/ja/training/wearables/apps/creating.html)を参照。

## カスタムレイアウトを作る

### Custom Notification

* カスタムレイアウトをアクティビティに載っけて、それで通知できる(ただしHandheldとの自動同期はない)
* アクティビティはマニフェストで、`android:exported="true"` `android:allowEmbedded="true"` ` android:taskAffinity=""`と設定する必要がある
* アクティビティのテーマを`Theme.DeviceDefault.Light`にするといいよ
* このアクティビティを起動する`PendingIntent`を作る
* `WearableExtender#setDisplayIntent`でその`PendingIntent`を設定する
* あとは普通に通知を`notify`すればいい

### Wearable UI Library

[Android Studio](https://developer.android.com/intl/ja/sdk/installing/studio.html)で使える非公式のUIライブラリがあるらしい。[こちら](http://developer.android.com/intl/ja/training/wearables/apps/layouts.html#UiLibrary)を参照。

## 音声機能を追加する

WearはSystem-providedとApp-providedの2種類の音声アクションを提供している。

### System-provided

* システム、つまりAndroid Wearプラットフォームが提供する音声アクション
* ユーザがしたいことを言って、システムがそれを理解してアクティビティを起動する
* 具体的にはインテントフィルタで音声アクションを拾う感じ

Wearがサポートする音声インテント一覧は[こちら](http://developer.android.com/intl/ja/training/wearables/apps/voice.html#SystemProvided)を参照。

### App-provided

* 音声アクションによって直接アクティビティを起動できる
* "Start <アクティビティ名>"みたいに言うと、対象のアクティビティが起動する(日本語の場合はどうなんだろ？)
* このアクティビティは、Handheld上でランチャーアイコンから起動するやつと同じ
* <アクティビティ名>はマニフェストでアクティビティに設定した`android:label`の値

## Wearableアプリをパッケージングする

* WearableアプリはHandheldアプリ内にパッケージする必要がある
* ユーザはWearableアプリを直接インストールすることはできないから
* システムが自動的にWearableにWearableアプリをプッシュする

Android Studioを使った、あるいは手作業でのパッケージング手順は[こちら](http://developer.android.com/intl/ja/training/wearables/apps/packaging.html#Studio)を参照。

# データの送信、同期編

Google Play Servicesの一部であるWearable Data Layer APIは、Handheld-Wearableアプリ間の通信チャネルを提供する。

## Data Itemsを同期する

* DataItemはHandheld-Wearable間のデータ同期のためのインタフェースを定義する
* DataItemは一般に、PayloadとPathで構成される
* Payloadは最大100KBのbyte配列(だから好きなデータを載せられるしオブジェクトのシリアライズ／デシリアライズも可能)
* Pathはスラッシュから始まるユニークな文字列(例えば"/path/to/data")

普通はDataItemを直接実装することはない。代わりに

1. アイテムを一意に識別するPathを指定して`PutDataRequest`オブジェクトを生成する
2. `setData`メソッドでPayloadをセットする
3. `DataApi.putDataItem`を呼び出し、Data Itemの生成を要求する
4. Data Itemsが要求されるとシステムは`DataItem`インタフェースを適切に実装したオブジェクトを返す

`setData`の代わりに次に紹介するData Mapの方が簡単なのでおすすめらしい。

### Data Mapで同期する

* `Bundle`に似たインタフェースの`DataMap`クラスを使うといいよ
* `PutDataMapRequest`オブジェクトを生成して`getDataMap`メソッドでData Mapを取得できる
* put...メソッド(例えば`putString`)で必要な値をセットできる 
* `PutDataMapRequest.asPutDataRequest`で`PutDataRequest`が取得できるので`DataApi.putDataItem`に渡してData Itemを生成する

### Data Itemイベントをリスンする

[後述](#listen)

## Assetの転送

* 画像のようなBLOBをBluetoothで送るには、AssetをData Itemに付加する
* Assetは再送を防ぐため、Bluetoothの帯域幅を節約するためにデータのキャッシュを自動的に扱う

### Assetを転送する

* Assetを作るには、`Asset`クラスのcreate...メソッドを使う(例えば`createFromBytes`)
* Assetを得たら`DataMap`とか`PutDataRequest`に`putAsset`する。

### Assetを受信する

[サンプルコード](http://developer.android.com/intl/ja/training/wearables/data-layer/assets.html#ReceiveAsset)

## メッセージの送受信

* メッセージはData Itemと違ってHandheld-Wearable間同期はないよ
* メッセージは"fire-and-forget"的な片方向通信
* メッセージを送って、なんかして、メッセージを送り返せばrequest/responseモデルにもできる

### メッセージを送信する

接続相手側のアクティビティを起動させるメッセージを送る[サンプルコード](http://developer.android.com/intl/ja/training/wearables/data-layer/messages.html#SendMessage)
このコードは、メッセージが受信されるかタイムアウトするまでブロックするらしい

### メッセージを受信する

[後述](#listen)

## Data Layerイベントのハンドル

### Data Layerの呼び出しのステータスを待つ

* Data Layer APIは`PendingResult`を返すことがある(例えば`putDataItem`)
* `PendingResult`を使って同期的にも非同期的にもオペレーションの結果を得られる
* `PendingResult#await`で同期的に結果を得る
* `PendingResult#setResultCallback`で非同期で結果を得る

### <a name="listen">Data Layerイベントをリスンする</a>

Data Layerイベントをリスンするには、`WearableListenerService`を継承したサービスを作るか、リスナを実装したアクティビティを作るかの2つの選択肢がある。

#### WearableListenerService

`WearableListenerService`固有のメソッド

* `onDataChanged`: Data Itemオブジェクトの生成、変更、削除時に呼び出される。接続している片方で起こったイベントは、双方に通知される
* `onMessageReceived`: 接続相手が送ったメッセージを受信したときに呼び出される
* `onPeerConnected`と`onPeerDisconnected`: 接続したとき、切断したときに呼び出される。接続している片方で起こったイベントは、双方に通知される

`WearableListenerService`を継承したサービスは、上記のメソッドを必要に応じて実装して、`com.google.android.gms.wearable.BIND_LISTENER`アクションを拾うようにインテントフィルタを設定する。

##### Data Layerコールバック内のパーミッション

* Data Layerコールバック内でパーミッションが必要な操作を行うと失敗する: IPCにより呼び出されるため
* `Binder.clearCallingIdentity`メソッドを呼び出せば大丈夫
* 最後に`Binder.restoreCallingIdentity`を呼び出して元に戻すこと

#### リスナアクティビティ

次のリスナを1つ以上実装すればアクティビティでイベントをリスンできる。

* `DataApi.DataListener`
* `MessageApi.MessageListener`
* `NodeApi.NodeListener`

アクティビティのライフサイクルに合わせていろいろやる必要がある(詳細は[公式ドキュメント](http://developer.android.com/intl/ja/training/wearables/data-layer/events.html#Listen)を参照)。