#スマホ側の設定
Activityの実装
- Android Wearと連携を行う際にGoogleApiClientというものを使います。
まずその設定方法から
private GoogleApiClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionFragment fragment = ActionFragment.newInstance();
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, fragment)
.commit();
}
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
Log.d(LOG_TAG, "Google Api Client connected");
Wearable.MessageApi.addListener(mGoogleApiClient,MainActivity.this);
}
@Override
public void onConnectionSuspended(int cause) {
// 一時的に切断された際に行いたい処理を記述する
}
}).build();
mGoogleApiClient.connect();
}
- 適当なFragmentを表示させています。
- mGoogleApiClientはクラスのプロパティとして保持しておいて下さい。
後で使いまわします。 - addApi(Wearable.API)とすることでスマホ、AndroidWear間のAPIが使えるようになります。
- GoogleApiClientの接続は非同期処理なので、GoogleApiClient.ConnectionCallbacks()をaddConnectionCallbacks()に設定して接続が終わったのを取れるようにします。
- 接続が終わるとonConnected()が呼ばれますので、そこでAndroidWearからのメッセージを取れるようにListenerをセットします。
- 今回はActivityにMessageApi.MessageListenerをimplementsしました。
そうするとonMessageReceived()を実装する必要があります。
private static final String CLICK_PATH = "/click/wearable";
@Override
public void onMessageReceived(MessageEvent messageEvent) {
if (messageEvent.getPath().equals(CLICK_PATH)) {
Log.d(LOG_TAG,"mobile onMessageReceived");
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "Watchでボタンが押されたよ", Toast.LENGTH_SHORT).show();
}
});
}
}
-
これでAndroidWearで送られたメッセージを受け取れるようになります。
-
log出しでは味気ないので、トーストを出すようにしました。
-
別スレッドでメッセージが来るのでrunOnUiThread()内で行います。
-
if文で確認しているCLICK_PATHとしているのはスマホとAndroidWearがそれぞれ持つ共通pathです。
"/"から始まれば何でも良いので、今回は/click/wearableとています。 -
最後にmGoogleApiClientをFragment内で取れるようにgetterを実装しておきます
public GoogleApiClient getGoogleApiClient() {
return mGoogleApiClient;
}
Fragment実装
- 適当にボタンをおいたFragmentを用意して、OnClickListenerを設定します
- Activityに直接ボタンをおいても問題ありません。何となくFragmentを使いました。
private static final String COUNT_KEY = "COUNT_KEY";
private static final String COUNT_PATH = "/count";
private int mCount = 0;
@Override
public void onClick(View v) {
PutDataMapRequest dataMap = PutDataMapRequest.create(COUNT_PATH);
dataMap.getDataMap().putInt(COUNT_KEY, ++mCount);
PutDataRequest request = dataMap.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi
.putDataItem(((MainActivity) getActivity()).getGoogleApiClient(), request);
pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
@Override
public void onResult(DataApi.DataItemResult dataItemResult) {
Log.d(LOG_TAG, "count updated:" + mCount);
}
});
}
- ボタンを押したら、AndroidWearへインクリメントしたintの値を送るようにします。
- 送ると書きましたが、COUNT_PATHにスマホ・AndroidWear共通の領域にデータを置くイメージです。CLICK_PATHと同じく"/"で始まる必要があります。
- COUNT_KEYをkeyとしてスマホ・AndroidWearどちらからも読み書き可能です。
- pendingResult.setResultCallback()で設定したCallbackはスマホ・AndroidWearどちらであっても書き込み完了時に呼ばれます。
#AndroidWear側の設定
WearableListenerServiceの実装
- まずWearableListenerServiceをextendsしたクラスを作成します。
DataLayerListenerServiceとしています。 - onDataChanged()を実装します。何かしらデータの書き込みが行われた際に呼ばれます。
private static final String COUNT_KEY = "COUNT_KEY";
private static final String COUNT_PATH = "/count";
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
DataItem dataItem = event.getDataItem();
if (COUNT_PATH.equals(dataItem.getUri().getPath())) {
DataMap dataMap = DataMapItem.fromDataItem(dataItem).getDataMap();
int count = dataMap.getInt(COUNT_KEY);
Intent intent = new Intent(this, NotificationEmbeddedActivity.class);
intent.putExtra(NotificationEmbeddedActivity.EXTRA_KEY_COUNT, count);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification secondPage = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.extend(
new Notification.WearableExtender()
.setCustomSizePreset(Notification.WearableExtender.SIZE_FULL_SCREEN)
.setDisplayIntent(pendingIntent))
.build();
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("メッセージ")
.setContentTitle("タイトル")
.extend(
new Notification.WearableExtender()
.addPage(secondPage)
.setHintHideIcon(true)
)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(1000, notification);
break;
}
}
}
今回の肝の部分になりますので、分解して解説します。
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
DataItem dataItem = event.getDataItem();
if (COUNT_PATH.equals(dataItem.getUri().getPath())) {
DataMap dataMap = DataMapItem.fromDataItem(dataItem).getDataMap();
int count = dataMap.getInt(COUNT_KEY);
....
....
break;
}
}
}
- onDataChanged()は複数の書き込みイベントが取れる可能性があるので、スマホ側で指定した共通のpathを使って目的の領域への書き込みか確認を行います。
- 目的の書き込みだった場合は、これまたスマホ側で指定したkeyを使って目的のデータを取得します。
- これでスマホでインクリメントしたintの値が取れます。
- そのデータを元にカードを作成します。
....
Intent intent = new Intent(this, NotificationEmbeddedActivity.class);
intent.putExtra(NotificationEmbeddedActivity.EXTRA_KEY_COUNT, count);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification secondPage = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.extend(
new Notification.WearableExtender()
.setCustomSizePreset(Notification.WearableExtender.SIZE_FULL_SCREEN)
.setDisplayIntent(pendingIntent))
.build();
....
- 表示したいActivityとデータをセットしたIntentを元にpendingIntentに作成します。
- そして、Notificationを作成します。
ここで通常addAction()でPendingIntentを指定しますが、extend()でAndroidWear用の処理が入ります。
Notification.WearableExtender生成し、setDisplayIntent()にPendingIntentをセットすることでActivityが表示されるようになります。 - setCustomSizePreset()という設定もあって、これは表示するActivityの表示サイズを指定します。
値としては以下のものが存在します。
Notification.WearableExtender.SIZE_DEFAULT
Notification.WearableExtender.SIZE_FULL_SCREEN
Notification.WearableExtender.SIZE_LARGE
Notification.WearableExtender.SIZE_MEDIUM
Notification.WearableExtender.SIZE_SMALL
Notification.WearableExtender.SIZE_XSMALL
- 作成したNotificationを実際に送るNotificationにセットします。
....
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("メッセージ")
.setContentTitle("タイトル")
.extend(
new Notification.WearableExtender()
.addPage(secondPage)
.setHintHideIcon(true)
)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(1000, notification);
....
-
ここでもextend()に生成したNotification.WearableExtenderを指定します。
-
先ほどのsecondPageはaddPage()で指定しています。
複数のNotificationを指定する場合は、NotificationのListを生成しaddPagesで指定できます。 -
そして、Notificationを投げます。ここで投げたものはスマホ側には表示されません。
-
ここまでがカード表示までの処理です。
-
そして、onDataChanged()が呼ばれるようにするためにDataLayerListenerServiceをAndroidManifest.xmlにてserviceとして登録します。
<service android:name=".DataLayerListenerService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
NotificationEmbeddedActivityの実装
- DataLayerListenerServiceで表示するActivityとして指定しているNotificationEmbeddedActivityを実装します。
- スマホのActivityの内容と同じくmGoogleApiClientを生成します。
- ほとんどスマホ側と同じですが、AndroidWearからはメッセージを送るのでNodeApiと言うものでスマホのidを取得する必要があります。
以下はmGoogleApiClient生成のaddConnectionCallbacks()で設定したCallBackの中身です。
@Override
public void onConnected(Bundle bundle) {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
@Override
public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
if (getConnectedNodesResult.getStatus().isSuccess() && getConnectedNodesResult.getNodes().size() > 0) {
mNodeId = getConnectedNodesResult.getNodes().get(0).getId();
}
}
});
}
@Override
public void onConnectionSuspended(int cause) {
// 一時的に切断された際に行いたい処理を記述する
}
- String型のmNodeIdというクラスのプロパティを用意し、取得したidを保持します。
- 複数Nodeが取れる可能性がありますが、今回はスマホとしか繋いでいないので配列の0番目をそのまま使います。
- 実際にボタンを押してメッセージを送ってみます。
private static final String CLICK_PATH = "/click/wearable";
@Override
public void onClick(View v) {
Wearable.MessageApi.sendMessage(mGoogleApiClient,mNodeId,CLICK_PATH,null);
}
-
スマホ側でも設定したCLICK_PATHと先ほど取得したNodeIdを使ってsendMessage()を行います。
第四引数にbyteの配列を入れることでデータも送ることができます。今回は送りません。 -
これでスマホ側のonMessageReceived()が呼ばれトーストが表示されます。
-
最後にIntentに設定したデータを取得してTextViewに表示します。
Intent intent = getIntent();
int count = intent.getIntExtra(EXTRA_KEY_COUNT, -1);
if (count >= 0) {
textView.setText(String.format("count is %d", count));
} else {
textView.setText(String.format("Hello world!"));
}
##まとめ
これでスマホ側のボタンを押せば、AndroidWear側にカードが現れ、左にスワイプすることでActivityが表示し、画面上にカウントが出ています。
またAndroidWear側のボタンを押すとスマホ側にトーストが表示ができるようになりました。
提供されているAPIの用途と合ってないような気がしますが、動作確認としては思った通りの動きをしてくれて良かったです。
次は画像の表示などを試してみたいと思います。