Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Developing for Android Wear

Developing for Android Wear

Brief look at the Android Wear Development

Can Elmas

May 11, 2015
Tweet

More Decks by Can Elmas

Other Decks in Technology

Transcript

  1. @can_elmas Software Development Manager at Monitise MEA (formerly Pozitron) Backend

    and front-end roles in mobile projects since 2007 Currently leading Monitise MEA Android team
  2. 2014 - 2015, exciting years for wearable tech Android Wear,

    Apple Watch, Nike FuelBand, Pebble, Jawbone, Fitbit and more Over 720,000 Android Wear devices shipped in 2014 957,000 Apple Watches on the first day of pre-sales More than 1 million Pebble sold since 2013 Big players; Motorola, LG, Samsung, Sony, Asus, Tag Heuer 1201 android-wear tagged questions on StackOverflow; 517 for apple-watch* * data gathered on May the 9th Numbers
  3. 4GB storage, 512 MB RAM Heart-rate monitor, Accelerometer built-in GPS

    Sensor only on Sony SmartWach 3 Gyroscope and Barometer on LG Watch Urbane below 60 grams between 199$ - 350$ Battery Life vary between 1 day to 1.5 day Specs
  4. What You Can Do Get / Set reminders Track fitness

    Voice Search Start navigation See weather Control remote media Stay connected!
  5. Call a car/taxi Take a note Set alarm Set timer

    Start stopwatch Start/Stop a bike ride Show heart rate Show step count Voice Commands
  6. Voice Commands Declare your own app-provided voice actions to start

    activities “OK Google, start Lovely App” 
 <activity
 android:name=".MainActivity"
 android:label="Lovely App" >
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>

  7. Design Principles Do not stop user from cooking, eating, walking,

    running Design for big gestures Think about stream cards first i.e. notifications-instead-of-apps model Do one thing, really fast
  8. Extending Notifications with Wearable Functionality For a better user experience

    Wearable Only Actions Voice Capabilities Extra Pages
  9. Android 4.3 (API Level 18) or higher Android v4 support

    library (or v13, which includes v4) 400x400 for non-scrolling and 640x400 for parallax scrolling background images in res/drawable-nodpi Other non-bitmap resources in res/drawable-hdpi 
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationManagerCompat;
 import android.support.v4.app.NotificationCompat.WearableExtender;

  10. Notifications final int notificationId = 1;
 final NotificationCompat.Builder builder =

    new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Hello Wearable!")
 .setContentText("Sample text");
 NotificationManagerCompat.from(context).notify(notificationId, builder.build());

  11. Notifications final int notificationId = 1;
 final NotificationCompat.Builder builder =

    new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Hello Wearable!")
 .setContentText("Sample text");
 NotificationManagerCompat.from(context).notify(notificationId, builder.build());

  12. Notifications final int notificationId = 1;
 final NotificationCompat.Builder builder =

    new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Hello Wearable!")
 .setContentText("Sample text");
 NotificationManagerCompat.from(context).notify(notificationId, builder.build());

  13. Notifications final int notificationId = 1;
 final NotificationCompat.Builder builder =

    new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Hello Wearable!")
 .setContentText("Sample text");
 NotificationManagerCompat.from(context).notify(notificationId, builder.build());

  14. final int notificationId = 1;
 
 final NotificationCompat.Builder builder =

    new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Hello Wearable!")
 .setContentText("Sample text")
 .setContentIntent(browserPendingIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.ic_notif_large
 ));
 
 NotificationManagerCompat.from(context).notify(notificationId, builder.build()); Notifications
  15. final int notificationId = 1;
 
 final NotificationCompat.Builder builder =

    new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Hello Wearable!")
 .setContentText("Sample text")
 .setContentIntent(browserPendingIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.ic_notif_large
 ));
 
 NotificationManagerCompat.from(context).notify(notificationId, builder.build()); Notifications
  16. Extending Notifications : Actions final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)


    .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("HoverBoard is on sale!")
 .setContentText("Check it out!")
 .setContentIntent(itemDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_hoverboard2
 ));
 
 // Handheld only actions
 builder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context));
 
 // Wearable-only actions
 final NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();
 
 wearableExtender.addAction(
 new NotificationCompat.Action(
 R.drawable.ic_navigation, 
 "Start Navigation", 
 navigationIntent(context))
 );
 
 builder.extend(wearableExtender);
  17. Extending Notifications : Actions final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)


    .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("HoverBoard is on sale!")
 .setContentText("Check it out!")
 .setContentIntent(itemDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_hoverboard2
 ));
 
 // Handheld only actions
 builder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context));
 
 // Wearable-only actions
 final NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();
 
 wearableExtender.addAction(
 new NotificationCompat.Action(
 R.drawable.ic_navigation, 
 "Start Navigation", 
 navigationIntent(context))
 );
 
 builder.extend(wearableExtender);
  18. Extending Notifications : Actions final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)


    .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("HoverBoard is on sale!")
 .setContentText("Check it out!")
 .setContentIntent(itemDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_hoverboard2
 ));
 
 // Handheld only actions
 builder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context));
 
 // Wearable-only actions
 final NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();
 
 wearableExtender.addAction(
 new NotificationCompat.Action(
 R.drawable.ic_navigation,
 “Nearest Shop",
 navigationIntent(context))
 );
 
 builder.extend(wearableExtender);
  19. Extending Notifications : Actions final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)


    .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("HoverBoard is on sale!")
 .setContentText("Check it out!")
 .setContentIntent(itemDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_hoverboard2
 ));
 
 // Handheld only actions
 builder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context));
 
 // Wearable-only actions
 final NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();
 
 wearableExtender.addAction(
 new NotificationCompat.Action(
 R.drawable.ic_navigation,
 "Start Navigation",
 navigationIntent(context))
 );
 
 builder.extend(wearableExtender);
  20. Extending Notifications : Voice Capabilities public static final String EXTRA_VOICE_REPLY

    = "extra_voice_reply";
 
 final RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel("Rate the session")
 .build();
  21. public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
 
 final RemoteInput

    remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel("Rate the session")
 .setChoices(context.getResources().getStringArray(R.array.reply_choices))
 .build(); <string-array name="reply_choices">
 <item>It was alright</item>
 <item>Not so useful</item>
 </string-array> Extending Notifications : Voice Capabilities
  22. ... final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Was the

    session helpful?")
 .setContentIntent(openSessionDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_gdg
 ));
 
 builder.extend(
 new NotificationCompat.WearableExtender()
 .addAction(
 new NotificationCompat.Action.Builder(
 R.drawable.abc_ic_voice_search_api_mtrl_alpha,
 "Reply",
 openSessionDetailsIntent(context)
 ).addRemoteInput(remoteInput).build()
 ) ); ... Extending Notifications : Voice Capabilities
  23. ... final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Was the

    session helpful?")
 .setContentIntent(openSessionDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_gdg
 ));
 
 builder.extend(
 new NotificationCompat.WearableExtender()
 .addAction(
 new NotificationCompat.Action.Builder(
 R.drawable.abc_ic_voice_search_api_mtrl_alpha,
 "Reply",
 openSessionDetailsIntent(context)
 ).addRemoteInput(remoteInput).build()
 ) ); ... Extending Notifications : Voice Capabilities
  24. ... final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Was the

    session helpful?")
 .setContentIntent(openSessionDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_gdg
 ));
 
 builder.extend(
 new NotificationCompat.WearableExtender()
 .addAction(
 new NotificationCompat.Action.Builder(
 R.drawable.abc_ic_voice_search_api_mtrl_alpha,
 "Reply",
 openSessionDetailsIntent(context)
 ).addRemoteInput(remoteInput).build()
 ) ); ... Extending Notifications : Voice Capabilities
  25. … final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("Was the

    session helpful?")
 .setContentIntent(openSessionDetailsIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_gdg
 ));
 
 builder.extend(
 new NotificationCompat.WearableExtender()
 .addAction(
 new NotificationCompat.Action.Builder(
 R.drawable.abc_ic_voice_search_api_mtrl_alpha,
 "Reply",
 openSessionDetailsIntent(context)
 ).addRemoteInput(remoteInput).build()
 )
 ); …. Extending Notifications : Voice Capabilities
  26. Extending Notifications : Pages More information without requiring user to

    open the app on the handheld final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("New Pancake Recipe!")
 .setContentText("Start making now!")
 .setContentIntent(openRecipeIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_pancakes1
 ));
  27. Extending Notifications : Pages final Notification secondPage = new NotificationCompat.Builder(context)


    .setContentTitle("Step 1")
 .setContentText(RECIPE_STEP_1)
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_pancakes2
 )).build();
 
 final Notification thirdPage = new NotificationCompat.Builder(context)
 .setContentTitle("Step 2")
 .setContentText(RECIPE_STEP_2)
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_pancakes3
 )).build(); builder.extend(
 new NotificationCompat.WearableExtender()
 .addPage(secondPage)
 .addPage(thirdPage)
 ).build();
  28. Extending Notifications : Pages final Notification secondPage = new NotificationCompat.Builder(context)


    .setContentTitle("Step 1")
 .setContentText(RECIPE_STEP_1)
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_pancakes2
 )).build();
 
 final Notification thirdPage = new NotificationCompat.Builder(context)
 .setContentTitle("Step 2")
 .setContentText(RECIPE_STEP_2)
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_pancakes3
 )).build();
 
 builder.extend(
 new NotificationCompat.WearableExtender()
 .addPage(secondPage)
 .addPage(thirdPage)
 ).build();
  29. Extending Notifications : Pages builder.extend(
 new NotificationCompat.WearableExtender()
 .addPage(secondPage)
 .addPage(thirdPage)
 ).build();

    final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_notif)
 .setContentTitle("New Pancake Recipe!")
 .setContentText("Start making now!")
 .setContentIntent(openRecipeIntent(context))
 .setLargeIcon(BitmapFactory.decodeResource(
 context.getResources(),
 R.drawable.bg_pancakes1
 )); . // second page ... ... . // third page ...
  30. Custom Wearable Apps Run directly on the device Fundamentally same

    as apps built for handheld but differ greatly in design and usability Basically activities with custom layouts Access to sensors and GPU Small in size and functionality
  31. Custom Wearable Apps Different code base, no shared resources, different

    applications Custom notifications issued on the wearable are not synced with handheld When the device goes to sleep, activity gets destroyed No back or home button to exit the app Swiping from the left edge or Long press on the app
  32. 
 dependencies {
 ...
 compile 'com.google.android.support:wearable:1.1.0'
 compile 'com.google.android.gms:play-services-wearable:7.3.0'
 }
 SDK

    tools 23.0.0 or higher API 20 or higher Enable Debug over Bluetooth on both Wearable and Handheld (Android Wear companion app) adb forward tcp:4444 localabstract:/adb-hub adb connect localhost:4444
  33. Custom Wearable Apps : Wearable UI Library BoxInsetLayout CardFragment CircledImageView

    CrossFadeDrawable DelayedConfirmationView DismissOverlayView DotsPageIndicator GridViewPager GridPagerAdapter FragmentGridPagerAdapter WatchViewStub WearableListView
  34. Sending and Syncing Data Message API Data API Node API

    to sync data across all Wear devices great for one way communication “fire&forget” to learn about local or connected Nodes
  35. Sending and Syncing Data Channel API send large data or

    stream data between two or more devices
  36. Sending and Syncing Data Channel API send large data or

    stream data between two or more devices
  37. Sending and Syncing Data : Handheld googleApiClient = new GoogleApiClient.Builder(this)


    .addConnectionCallbacks(this)
 .addOnConnectionFailedListener(this)
 .addApi(Wearable.API)
 .build();
  38. @Override
 public void onConnected(Bundle bundle) { sendFlightInfo();
 }
 
 @Override


    public void onConnectionSuspended(int cause) {
 ...
 }
 
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
 ...
 } @Override
 protected void onStart() {
 super.onStart();
 googleApiClient.connect();
 }
 
 @Override
 protected void onStop() {
 googleApiClient.disconnect();
 super.onStop();
 } Sending and Syncing Data : Handheld
  39. @Override
 public void onConnected(Bundle bundle) { sendFlightInfo();
 }
 
 @Override


    public void onConnectionSuspended(int cause) {
 ...
 }
 
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
 ...
 } @Override
 protected void onStart() {
 super.onStart();
 googleApiClient.connect();
 }
 
 @Override
 protected void onStop() {
 googleApiClient.disconnect();
 super.onStop();
 } Sending and Syncing Data : Handheld
  40. private void sendFlightInfo() {
 
 final PutDataMapRequest request = PutDataMapRequest.create("/add-airlines/flight");


    
 request.getDataMap().putString("from", "SAW");
 request.getDataMap().putString("to", "ESB");
 request.getDataMap().putString("gate", "G22");
 request.getDataMap().putString("barcode", barcodeData);
 
 Wearable.DataApi.putDataItem(googleApiClient, request.asPutDataRequest())
 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
 @Override
 public void onResult(DataApi.DataItemResult dataItemResult) {
 
 if (!dataItemResult.getStatus().isSuccess()) {
 // handle error
 }
 }
 });
 
 } Sending and Syncing Data : Handheld
  41. private void sendFlightInfo() {
 
 final PutDataMapRequest request = PutDataMapRequest.create("/add-airlines/flight");


    
 request.getDataMap().putString("from", "SAW");
 request.getDataMap().putString("to", "ESB");
 request.getDataMap().putString("gate", "G22");
 request.getDataMap().putString("barcode", barcodeData);
 
 Wearable.DataApi.putDataItem(googleApiClient, request.asPutDataRequest())
 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
 @Override
 public void onResult(DataApi.DataItemResult dataItemResult) {
 
 if (!dataItemResult.getStatus().isSuccess()) {
 // handle error
 }
 }
 });
 
 } Sending and Syncing Data : Handheld
  42. private void sendFlightInfo() {
 
 final PutDataMapRequest request = PutDataMapRequest.create("/add-airlines/flight");


    
 request.getDataMap().putString("from", "SAW");
 request.getDataMap().putString("to", "ESB");
 request.getDataMap().putString("gate", "G22");
 request.getDataMap().putString("barcode", barcodeData);
 
 Wearable.DataApi.putDataItem(googleApiClient, request.asPutDataRequest())
 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
 @Override
 public void onResult(DataApi.DataItemResult dataItemResult) {
 
 if (!dataItemResult.getStatus().isSuccess()) {
 // handle error
 }
 }
 });
 
 } Sending and Syncing Data : Handheld
  43. private void sendFlightInfo() {
 
 final PutDataMapRequest request = PutDataMapRequest.create("/add-airlines/flight");


    
 request.getDataMap().putString("from", "SAW");
 request.getDataMap().putString("to", "ESB");
 request.getDataMap().putString("gate", "G22");
 request.getDataMap().putString("barcode", barcodeData);
 
 Wearable.DataApi.putDataItem(googleApiClient, request.asPutDataRequest())
 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
 @Override
 public void onResult(DataApi.DataItemResult dataItemResult) {
 
 if (!dataItemResult.getStatus().isSuccess()) {
 // handle error
 }
 }
 });
 
 } Sending and Syncing Data : Handheld
  44. Sending and Syncing Data : Wearable <service android:name=".datalayerapi.DataLayerListenerService">
 <intent-filter>
 <action

    android:name="com.google.android.gms.wearable.BIND_LISTENER" />
 </intent-filter>
 </service> public class DataLayerListenerService extends WearableListenerService {
 
 @Override
 public void onDataChanged(DataEventBuffer dataEvents) {
 
 for (DataEvent event : dataEvents) {
 
 final DataItem item = event.getDataItem();
 
 if (item.getUri().getPath().compareTo("/add-airlines/flight") == 0) {
 
 DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
 
 //raiseLocalBoardingPassNotification(dataMap);
 startBoardingPassActivity(dataMap);
 
 }
 }
 } ...
 
 }

  45. Sending and Syncing Data : Wearable <service android:name=".datalayerapi.DataLayerListenerService">
 <intent-filter>
 <action

    android:name="com.google.android.gms.wearable.BIND_LISTENER" />
 </intent-filter>
 </service> public class DataLayerListenerService extends WearableListenerService {
 
 @Override
 public void onDataChanged(DataEventBuffer dataEvents) {
 
 for (DataEvent event : dataEvents) {
 
 final DataItem item = event.getDataItem();
 
 if (item.getUri().getPath().compareTo("/add-airlines/flight") == 0) {
 
 DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
 
 //raiseLocalBoardingPassNotification(dataMap);
 startBoardingPassActivity(dataMap);
 
 }
 }
 } ...
 
 }

  46. Sending and Syncing Data : Wearable <service android:name=".datalayerapi.DataLayerListenerService">
 <intent-filter>
 <action

    android:name="com.google.android.gms.wearable.BIND_LISTENER" />
 </intent-filter>
 </service> public class DataLayerListenerService extends WearableListenerService {
 
 @Override
 public void onDataChanged(DataEventBuffer dataEvents) {
 
 for (DataEvent event : dataEvents) {
 
 final DataItem item = event.getDataItem();
 
 if (item.getUri().getPath().compareTo("/add-airlines/flight") == 0) {
 
 DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
 
 //raiseLocalBoardingPassNotification(dataMap);
 startBoardingPassActivity(dataMap);
 
 }
 }
 } ...
 
 }

  47. What’s Next and What to Expect? New Google Play Services

    7.3 features; Channel API and Capability API WatchFace API More to check on Data Layer APIs and UI library Android Wear 5.1 Update; Wi-Fi support, always-on screen, emojis, gesture controls, new app picker and rapid contacts Google IO 15 Sessions Smarter and personalized device authentication with Smart Lock Simplifying app development using the wearable support library Android Wear: Your app and the always-on screen