developing for android wear

73
Developing for Android Wear Can Elmas

Upload: can-elmas

Post on 19-Jul-2015

148 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Developing for Android Wear

Developing for Android Wear

Can Elmas

Page 2: Developing for Android Wear

@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

Page 3: Developing for Android Wear

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

Page 4: Developing for Android Wear

The market is still young

Page 5: Developing for Android Wear
Page 6: Developing for Android Wear

What You Can Do

Get / Set reminders

Track fitness

Voice Search

Start navigation

See weather

Control remote media

Stay connected!

Page 7: Developing for Android Wear

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

Page 8: Developing for Android Wear

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>

Page 9: Developing for Android Wear

Page 10: Developing for Android Wear

Do not port a small version of your handheld app

Page 11: Developing for Android Wear

The Vision

Suggest Demand

Context Stream Cue Card

Page 12: Developing for Android Wear

The Vision

Launched Automatically

Page 13: Developing for Android Wear

The Vision

Glanceable

Page 14: Developing for Android Wear

The Vision

Suggest and Demand

Page 15: Developing for Android Wear

The Vision

Zero or low interaction

Page 16: Developing for Android Wear

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

Page 17: Developing for Android Wear

Google Play Services

Page 18: Developing for Android Wear

No Play Store

Page 19: Developing for Android Wear
Page 20: Developing for Android Wear
Page 21: Developing for Android Wear
Page 22: Developing for Android Wear

Contextual Notifications

Custom App

Developing for Android Wear

Page 23: Developing for Android Wear

Notifications

Page 24: Developing for Android Wear

Notifications

No additional effort required

Page 25: Developing for Android Wear

Notifications

No additional effort required however..

Page 26: Developing for Android Wear

Extending Notifications with Wearable Functionality

For a better user experience

Wearable Only Actions

Voice Capabilities

Extra Pages

Page 27: Developing for Android Wear

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;

Page 28: Developing for Android Wear

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());

Page 29: Developing for Android Wear

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());

Page 30: Developing for Android Wear

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());

Page 31: Developing for Android Wear

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());

Page 32: Developing for Android Wear

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

Page 33: Developing for Android Wear

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

Page 34: Developing for Android Wear

Extending Notifications : Actionsfinal 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 actionsbuilder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context));// Wearable-only actionsfinal NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();wearableExtender.addAction( new NotificationCompat.Action( R.drawable.ic_navigation, "Start Navigation", navigationIntent(context))); builder.extend(wearableExtender);

Page 35: Developing for Android Wear

Extending Notifications : Actionsfinal 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 actionsbuilder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context)); // Wearable-only actionsfinal NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();wearableExtender.addAction( new NotificationCompat.Action( R.drawable.ic_navigation, "Start Navigation", navigationIntent(context))); builder.extend(wearableExtender);

Page 36: Developing for Android Wear

Extending Notifications : Actionsfinal 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 actionsbuilder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context));// Wearable-only actionsfinal NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(); wearableExtender.addAction( new NotificationCompat.Action( R.drawable.ic_navigation, “Nearest Shop", navigationIntent(context))); builder.extend(wearableExtender);

Page 37: Developing for Android Wear

Extending Notifications : Actionsfinal 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 actionsbuilder.addAction(R.drawable.ic_add_to_cart, "Add to Cart", addToCartIntent(context)); // Wearable-only actionsfinal NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(); wearableExtender.addAction( new NotificationCompat.Action( R.drawable.ic_navigation, "Start Navigation", navigationIntent(context))); builder.extend(wearableExtender);

Page 38: Developing for Android Wear

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();

Page 39: Developing for Android Wear

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

Page 40: Developing for Android Wear

...

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

Page 41: Developing for Android Wear

...

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

Page 42: Developing for Android Wear

...

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

Page 43: Developing for Android Wear

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

Page 44: Developing for Android Wear

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 ));

Page 45: Developing for Android Wear

Extending Notifications : Pagesfinal 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();

Page 46: Developing for Android Wear

Extending Notifications : Pagesfinal 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();

Page 47: Developing for Android Wear

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

...

Page 48: Developing for Android Wear

Sometimes notifications are not enough

Page 49: Developing for Android Wear

not possible with notifications

Page 50: Developing for Android Wear

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

Page 51: Developing for Android Wear

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

Page 52: Developing for Android Wear

Custom Wearable Apps : Unsupported APIs

android.webkit

android.print

android.app.backup

android.appwidget

android.hardware.usb

Page 53: Developing for Android Wear

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

Page 54: Developing for Android Wear

Custom Wearable Apps : Wearable UI Library

BoxInsetLayout

CardFragment

CircledImageView

CrossFadeDrawable

DelayedConfirmationView

DismissOverlayView

DotsPageIndicator

GridViewPager

GridPagerAdapter

FragmentGridPagerAdapter

WatchViewStub

WearableListView

Page 55: Developing for Android Wear

Custom Wearable Apps : Wearable UI Library

CardFragment

BoxInsetLayout

Page 56: Developing for Android Wear

Custom Wearable Apps : Wearable UI Library

GridViewPager

WearableListView

Page 57: Developing for Android Wear

Sending and Syncing Data

Message API Data API Node APIto sync data

across all Wear devicesgreat for one way communication

“fire&forget”

to learn about local or connected

Nodes

Page 58: Developing for Android Wear

Sending and Syncing Data

Channel APIsend large data or

stream data between two or more devices

Page 59: Developing for Android Wear

Sending and Syncing Data

Channel APIsend large data or

stream data between two or more devices

Page 60: Developing for Android Wear

Sending and Syncing Data

Capability APIto learn about

capabilities provided by nodes on the

Wear network

Page 61: Developing for Android Wear

Sending and Syncing Data : Handheld

googleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(Wearable.API) .build();

Page 62: Developing for Android Wear

@Overridepublic void onConnected(Bundle bundle) { sendFlightInfo();} @Overridepublic void onConnectionSuspended(int cause) {...} @Overridepublic void onConnectionFailed(ConnectionResult connectionResult) {...}

@Overrideprotected void onStart() { super.onStart(); googleApiClient.connect(); } @Overrideprotected void onStop() { googleApiClient.disconnect(); super.onStop(); }

Sending and Syncing Data : Handheld

Page 63: Developing for Android Wear

@Overridepublic void onConnected(Bundle bundle) { sendFlightInfo();} @Overridepublic void onConnectionSuspended(int cause) {...} @Overridepublic void onConnectionFailed(ConnectionResult connectionResult) {...}

@Overrideprotected void onStart() { super.onStart(); googleApiClient.connect();} @Overrideprotected void onStop() { googleApiClient.disconnect(); super.onStop();}

Sending and Syncing Data : Handheld

Page 64: Developing for Android Wear

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

Page 65: Developing for Android Wear

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

Page 66: Developing for Android Wear

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

Page 67: Developing for Android Wear

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

Page 68: Developing for Android Wear

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); } } } ...}

Page 69: Developing for Android Wear

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); } } } ...}

Page 70: Developing for Android Wear

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); } } } ...}

Page 71: Developing for Android Wear

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 SessionsSmarter and personalized device authentication with Smart LockSimplifying app development using the wearable support libraryAndroid Wear: Your app and the always-on screen

Page 72: Developing for Android Wear

Resources

https://developer.android.com/training/building-wearables.html

https://www.youtube.com/playlist?list=PLWz5rJ2EKKc-kIrPiq098QH9dOle-fLef

Building Apps for Wearables

DevBytes: Android Wear

Page 73: Developing for Android Wear

Thank You!@can_elmas

speakerdeck.com/canelmasgithub.com/canelmas