350 likes | 652 Views
Android Wear Development. July 25, 2014 Takahiro 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?.
E N D
Android Wear Development July 25, 2014 Takahiro 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 action etc…
Homescreen Home Cards (Context stream) … …
Cue card Voice Search and voice action … Cue cards
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
Notification • Add action • Add voice reply • Stacking notification • Add pages
Add action NotificationActivity.java (Handheld) Intent intent = new Intent(context, NotificationLandingActivity.class); PendingIntentpIntent = 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(); NotificationManagerCompatnotificationManager = NotificationManagerCompat.from(context); notificationManager.notify(0, n);
Add voice reply NotificationActivity.java (Handheld) 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); String replyLabel = ”会議に参加しますか?"; String[] replyChoices = {"はい", "いいえ”}; RemoteInputremoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY) .setLabel(replyLabel) .setChoices(replyChoices) .build(); Intent replyIntent= new Intent(ctx, VoiceReceiverActivity.class); PendingIntentreplyPendingIntent= PendingIntent.getActivity(ctx, 0, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Receive voice input VoiceReceiverActivity.java (Handheld) public class VoiceReceiverActivityextends 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)); } }
Stacking Notification NotificationActivity.java (Handheld) NotificationManagerCompatnotificationManager = 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);
Add pages NotificationActivity.java (Handheld) Notification 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(); NotificationManagerCompatnotificationManager = NotificationManagerCompat.from(context); notificationManager.notify(300, notification);
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 Custom Activity
Send notification to open an activity on the wearable WatchActivity.java (Wearable) Intent viewIntent = new Intent(context, WatchActivity.class); PendingIntentpendingViewIntent = 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(); NotificationManagerCompatnotificationManager= NotificationManagerCompat.from(context); notificationManager.notify(3000, notification);
Send notification embedding an activity on the wearable WatchActivity.java (Wearable) Intent viewIntent = new Intent(context, NotificationEmbeddedActivity.class); PendingIntentpendingViewIntent = 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(); NotificationManagerCompatnotificationManager= NotificationManagerCompat.from(context); notificationManager.notify(4000, notification); Custom Activity
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 Client DataActivity.java (Handheld) private GoogleApiClientmGoogleApiClient; @Override protected 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(inti) { } }).build(); mGoogleApiClient.connect(); }
Message API DataActivity.java (Handheld) private 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; }
Message API AndroidManifest.xml (Wearable) public class DataLayerListenerServiceextends WearableListenerService{ @Override public void onMessageReceived(MessageEventmessageEvent) { 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> DataLayerListenerService.java(Wearable)
Data APIsend data from handheld DataActivity.java (Handheld) int count = 0; private void sendCount() { PutDataMapRequestdataMap = 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.DataItemResultdataItemResult) { Log.d(TAG, "count updated:" + count); } }); }
Data APIdetect data change on wearable DataLayerListenerService.java (Wearable) public class DataLayerListenerServiceextends WearableListenerService { @Override public void onDataChanged(DataEventBufferdataEvents) { for (DataEvent event : dataEvents) { DataItemdataItem = event.getDataItem(); if (COUNT_PATH.equals(dataItem.getUri().getPath())) { DataMapdataMap = DataMapItem.fromDataItem(dataItem).getDataMap(); int count = dataMap.getInt(COUNT_KEY); …. } } } }
Data APIaccess to the existing data DataActivity.java (Handheld) The uri of data is wear://<NODE_ID>/<PATH> 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.DataItemResultdataItemResult) { DataItemdataItem = dataItemResult.getDataItem(); if (dataItem != null) { DataMapdataMap= DataMapItem.fromDataItem(dataItemResult.getDataItem()).getDataMap(); count = dataMap.getInt(COUNT_KEY); } } }); } private String getLocalNodeId() { NodeApi.GetLocalNodeResultnodeResult = Wearable.NodeApi.getLocalNode(mGoogleApiClient).await(); return nodeResult.getNode().getId(); }
Combination (1) • Update embedded activity in notification from the handheld Example: Google Map navigation on wearable Data API Notification with DisplayIntent Data API Notification with DisplayIntent Notification with DisplayIntent Data API
Combination (2) • Send notification from handheld to wearable to open a custom activity on wearable Example: Remote shutter for Google Camera app Custom Activity Data API Notification Tap to open Message API
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 toshare bitmap on the handheld works great