Android Wear Development
description
Transcript of Android Wear Development
Android Wear Development
July 25, 2014Takahiro Poly Horikawa
Agenda• Android wear’s features and capabilities• How to develop?– Notification– Sending and syncing data
• Development case study
Concept
• Microinteraction
How it works?
• Require android (>= 4.3) phone/tablet (referred as handheld)
• Require “Android Wear” app on the handheld• Handheld and wearable communicate each
other via Bluetooth (wearable itself does not connect to internet)
Notifications
What you can do?
• Receive notifications and react them on the wearable
• Control apps on the phone from the wearable• Run wearable specific app (e.g. compass,
heart rate monitor)• Voice search and voice actionetc…
Homescreen
…
…
Home
Cards(Context stream)
Cue card
…Cue cards
Voice Search and voice action
Sensors (Samsung Gear Live)• MPL Rotation Vector (Invensense)• MPL Gravity (Invensense)• MPL Orientation (Invensense)• MPL Linear Accelration (Invensense)• MPU6515 Acceleration Sensor (Invensense)• MPU6515 Gyroscope Sensor (Invensense)• SAMSUNG Tilt Wake Sensor (Samsung Inc.)• SAMSUNG Significant Motion Sensor (Samsung Inc.)• SAMSUNG Game Rotation Vector (Samsung Inc.)• SAMSUNG Step Counter Sensor (Samsung Inc.)• SAMSUNG Step Detector Sensor (Samsung Inc.)• AK8963C Magnetic Sensor UnCalibrated (Asahi Kasei Microdevices)• AK8963C Magnetic field Sensor (Asahi Kasei Microdevices)• ADPD142 HRM Sensor Lib (ADI)
Application packaging and publishing
• When publishing to user, package a wearable app inside a handheld app
• Wearable app will be installed on user’s wearable automatically when user install handheld app from Google Play Store
• A handheld app has to have the same package name as a wearable app
ANDROID WEAR APP DEVELOPMENT
Notification
• Add action
• Add voice reply
• Stacking notification
• Add pages
Add actionIntent intent = new Intent(context, NotificationLandingActivity.class);PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent, 0);
Notification n = new NotificationCompat.Builder(context) .setContentTitle(" 新着メッセージがあります ") .setContentText("xx さんからメッセージが届いています ") .setSmallIcon(R.drawable.ic_launcher) .setContentIntent(pIntent) .addAction(R.drawable.ic_launcher, " 返信 ", pIntent) .addAction(R.drawable.ic_launcher, " 転送 ", pIntent) .build();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);notificationManager.notify(0, n);
NotificationActivity.java (Handheld)
Add voice replyString replyLabel = ” 会議に参加しますか? ";String[] replyChoices = {" はい ", " いいえ” };
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY) .setLabel(replyLabel) .setChoices(replyChoices) .build();
Intent replyIntent = new Intent(ctx, VoiceReceiverActivity.class);PendingIntent replyPendingIntent = PendingIntent.getActivity(ctx, 0, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_launcher, " 返信 ", replyPendingIntent) .addRemoteInput(remoteInput) .build();
Notification notification = new NotificationCompat.Builder(ctx) .extend(new NotificationCompat.WearableExtender().addAction(action)).build();
NotificationManagerCompat manager = NotificationManagerCompat.from(ctx);manager.notify(200, notification);
NotificationActivity.java (Handheld)
Receive voice inputpublic class VoiceReceiverActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_voice_input_receiver); Bundle remoteInput = RemoteInput.getResultsFromIntent(getIntent()); CharSequence message = remoteInput.getCharSequence(NotificationActivity.EXTRA_VOICE_REPLY); ((TextView) findViewById(R.id.message)).setText(String.format(" 音声入力メッセージは「 %s 」です。 ", message)); }
}
VoiceReceiverActivity.java (Handheld)
Stacking NotificationNotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
Notification card1 = new NotificationCompat.Builder(context) .setContentText(" 何してますか? ") .setGroup(GROUP_KEY).setSortKey("0”).build();notificationManager.notify(101, card1);
Notification card2 = new NotificationCompat.Builder(context) .setContentText(" 手伝ってもらってもいいですか? ") .setGroup(GROUP_KEY).setSortKey("1”).build();notificationManager.notify(102, card2);
Notification summary = new NotificationCompat.Builder(context) .setContentTitle(" 新着メッセージ 2 件 ") .setGroup(GROUP_KEY).setGroupSummary(true).build();notificationManager.notify(100, summary);
NotificationActivity.java (Handheld)
Add pagesNotification page = new NotificationCompat.Builder(context) .setStyle(new NotificationCompat.BigTextStyle().bigText(" メンチカツ定食 $7.99")) .build();
Notification notification = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle("xx レストラン通信 ") .setContentText(" 本日の日替わりランチ ") .extend(new NotificationCompat.WearableExtender().addPage(page)) .build();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);notificationManager.notify(300, notification);
NotificationActivity.java (Handheld)
Notification gotcha
• Notification from the handheld can only open an activity on the handheld
• To open/embed a custom activity on the wearable, you need to send notification from the wearable.
• Open a custom activity • Embed a custom activity
CustomActivity
Send notification to open an activity on the wearable
Intent viewIntent = new Intent(context, WatchActivity.class);PendingIntent pendingViewIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
Notification notification = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle("Wearable から送信 ") .setContentText("Wearable から送信した Notification です。 ") .addAction(R.drawable.ic_launcher, "Open", pendingViewIntent) .setLocalOnly(true) .extend(new NotificationCompat.WearableExtender().setContentAction(0).setHintHideIcon(true)) .build();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);notificationManager.notify(3000, notification);
WatchActivity.java (Wearable)
Send notification embedding an activity on the wearable
Intent viewIntent = new Intent(context, NotificationEmbeddedActivity.class);PendingIntent pendingViewIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
Notification notification = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle("Wearable から送信 ") .setContentText("Wearable から送信した Notification です。 ") .setLocalOnly(true) .extend(new NotificationCompat.WearableExtender().setDisplayIntent(pendingViewIntent)) .build();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);notificationManager.notify(4000, notification);
WatchActivity.java (Wearable)
CustomActivity
Sending and syncing data
• Message API– Fire and forgot, one way request– You can send just a string– Use case example
• Control media player from the wearable
• Data API– Sync data between the handheld and wearable– Payload is a byte array (100KB per item), but you can
use map interface to access data– Use case example
• Send Photo preview from the handheld to wearable
Connect Google Api Clientprivate GoogleApiClient mGoogleApiClient; @Overrideprotected void onCreate(Bundle savedInstanceState) { mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Wearable.API) .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { @Override public void onConnected(Bundle bundle) { Log.d(TAG, "Google Api Client connected"); }
@Override public void onConnectionSuspended(int i) { } }).build(); mGoogleApiClient.connect();}
DataActivity.java (Handheld)
Message APIprivate void sendMessageToStartActivity() { Collection<String> nodes = getNodes(); for (String node : nodes) { Wearable.MessageApi.sendMessage(mGoogleApiClient, node, START_ACTIVITY_PATH, null).await(); }}
private Collection<String> getNodes() { HashSet<String> results = new HashSet<String>(); NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await(); for (Node node : nodes.getNodes()) { results.add(node.getId()); } return results;}
DataActivity.java (Handheld)
Message API
public class DataLayerListenerService extends WearableListenerService { @Override public void onMessageReceived(MessageEvent messageEvent) { if (START_ACTIVITY_PATH.equals(messageEvent.getPath())) { Intent intent = new Intent(this, WatchActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); return; } }}
<service android:name=".DataLayerListenerService"> <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter></service>
AndroidManifest.xml (Wearable)
DataLayerListenerService.java (Wearable)
Data APIsend data from handheld
int count = 0;
private void sendCount() { PutDataMapRequest dataMap = PutDataMapRequest.create(COUNT_PATH); dataMap.getDataMap().putInt(COUNT_KEY, ++count); PutDataRequest request = dataMap.asPutDataRequest(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi .putDataItem(mGoogleApiClient, request); pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() { @Override public void onResult(DataApi.DataItemResult dataItemResult) { Log.d(TAG, "count updated:" + count); } });}
DataActivity.java (Handheld)
Data APIdetect data change on wearable
public class DataLayerListenerService extends WearableListenerService { @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);…. } } }}
DataLayerListenerService.java (Wearable)
Data APIaccess to the existing data
private void restoreCurrentCount() { String localNodeId = getLocalNodeId(); Uri uri = new Uri.Builder().scheme(PutDataRequest.WEAR_URI_SCHEME).authority(localNodeId).path(COUNT_PATH).build(); Wearable.DataApi.getDataItem(mGoogleApiClient, uri).setResultCallback(new ResultCallback<DataApi.DataItemResult>() { @Override public void onResult(DataApi.DataItemResult dataItemResult) { DataItem dataItem = dataItemResult.getDataItem(); if (dataItem != null) { DataMap dataMap = DataMapItem.fromDataItem(dataItemResult.getDataItem()).getDataMap(); count = dataMap.getInt(COUNT_KEY); } } });}
private String getLocalNodeId() { NodeApi.GetLocalNodeResult nodeResult = Wearable.NodeApi.getLocalNode(mGoogleApiClient).await(); return nodeResult.getNode().getId();}
DataActivity.java (Handheld)The uri of data is
wear://<NODE_ID>/<PATH>
Combination (1)
• Update embedded activity in notification from the handheld
Data API Notification with DisplayIntent
Data API Notification with DisplayIntent
Data API Notification with DisplayIntent
Example: Google Map navigation on wearable
Combination (2)
• Send notification from handheld to wearable to open a custom activity on wearable
Data API Notification Tap to open
Custom Activity
Message API
Example: Remote shutter for Google Camera app
DEVELOPMENT CASE STUDY
Draw Watch for Android Wear
• https://play.google.com/store/apps/details?id=com.polysfactory.drawwatch
Random stuff I leaned
• Prevent default behavior of wearable (right swipe to dismiss app)
– Place close menu on the second screen, but it is very confusing
• Starting app from cue card is a little bit annoying, it’s better to use notification
• Using Message API to share bitmap on the handheld works great