Slide 1

Slide 1 text

Getting Started with Android Wear Jeb Ware @jebstuart

Slide 2

Slide 2 text

Outline • Step 1: Design Considerations • Step 2: Code Samples • Step 3: ?? • Step 4: Profit

Slide 3

Slide 3 text

Why Care About Wear? • Use Android APIs • Easy sync w/ phone • 720k shipments in 2014 Source: http://www.canalys.com/newsroom/over-720000-android-wear-devices-shipped-2014

Slide 4

Slide 4 text

Do I Need A Wear App?

Slide 5

Slide 5 text

Do I Need A Wear App? Probably Not.

Slide 6

Slide 6 text

Do I Need A Wear App? • Android notification automatically mirrored • NotificationCompat • Actions • “Big View”

Slide 7

Slide 7 text

Don’t Be Annoying

Slide 8

Slide 8 text

Don’t Be Annoying • Users can block your notifications from showing on wearable

Slide 9

Slide 9 text

Good Use Cases

Slide 10

Slide 10 text

Good Use Cases • Reply to a text • Show directions to meeting • Add a reminder

Slide 11

Slide 11 text

Good Use Cases • Order a pizza • Scroll through your photo library

Slide 12

Slide 12 text

Do I Need A Wear App? • Figure out your user’s tasks • Then decide between rich notifications or a full-on Wear App

Slide 13

Slide 13 text

User Interface

Slide 14

Slide 14 text

• Launcher is rarely used • Use Voice, but have a fallback • Automatic, contextual cues
 (be like Google Now) User Interface

Slide 15

Slide 15 text

• Very small screen • gross motor skills
 (swipes, not pinpoint taps) • max 1 or 2 tap targets on-screen User Interface

Slide 16

Slide 16 text

User Interface • CardScrollView • CardFrame • CardFragment

Slide 17

Slide 17 text

User Interface http://www.nngroup.com/articles/smartwatch/ Nielsen Norman Group Raluca Budiu

Slide 18

Slide 18 text

Demo App https://github.com/jebstuart/WearDemo

Slide 19

Slide 19 text

CODE?

Slide 20

Slide 20 text

App Architecture Watch Phone Internet GoogleApiClient Location, Network, etc. UI, Sensors Wearable.DataApi

Slide 21

Slide 21 text

Project Creation

Slide 22

Slide 22 text

Gradle apply plugin: 'com.android.application'
 
 android {
 compileSdkVersion 22
 buildToolsVersion "22.0.1"
 
 defaultConfig {
 applicationId "com.jebware.weardemo"
 minSdkVersion 18
 targetSdkVersion 22
 versionCode 1
 versionName "1.0"
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 wearApp project(':wear')
 compile 'com.android.support:appcompat-v7:22.2.0'
 compile 'com.google.android.gms:play-services-wearable:7.5.0'
 compile 'com.android.support:recyclerview-v7:22.2.0'
 compile 'com.jakewharton:butterknife:6.1.0'
 } apply plugin: 'com.android.application'
 
 
 android {
 compileSdkVersion 22
 buildToolsVersion "22.0.1"
 
 defaultConfig {
 applicationId "com.jebware.weardemo"
 minSdkVersion 20
 targetSdkVersion 22
 versionCode 1
 versionName "1.0"
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.google.android.support:wearable:1.2.0'
 compile 'com.google.android.gms:play-services-wearable:7.5.0'
 compile 'com.android.support:recyclerview-v7:22.2.0'
 compile 'com.jakewharton:butterknife:6.1.0'
 } mobile/build.gradle wear/build.gradle

Slide 23

Slide 23 text

Gradle apply plugin: 'com.android.application'
 
 android {
 compileSdkVersion 22
 buildToolsVersion "22.0.1"
 
 defaultConfig {
 applicationId "com.jebware.weardemo"
 minSdkVersion 18
 targetSdkVersion 22
 versionCode 1
 versionName "1.0"
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 wearApp project(':wear')
 compile 'com.android.support:appcompat-v7:22.2.0'
 compile 'com.google.android.gms:play-services-wearable:7.5.0'
 compile 'com.android.support:recyclerview-v7:22.2.0'
 compile 'com.jakewharton:butterknife:6.1.0'
 } apply plugin: 'com.android.application'
 
 
 android {
 compileSdkVersion 22
 buildToolsVersion "22.0.1"
 
 defaultConfig {
 applicationId "com.jebware.weardemo"
 minSdkVersion 20
 targetSdkVersion 22
 versionCode 1
 versionName "1.0"
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.google.android.support:wearable:1.2.0'
 compile 'com.google.android.gms:play-services-wearable:7.5.0'
 compile 'com.android.support:recyclerview-v7:22.2.0'
 compile 'com.jakewharton:butterknife:6.1.0'
 } mobile/build.gradle wear/build.gradle

Slide 24

Slide 24 text

Gradle apply plugin: 'com.android.application'
 
 android {
 compileSdkVersion 22
 buildToolsVersion "22.0.1"
 
 defaultConfig {
 applicationId "com.jebware.weardemo"
 minSdkVersion 18
 targetSdkVersion 22
 versionCode 1
 versionName "1.0"
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 wearApp project(':wear')
 compile 'com.android.support:appcompat-v7:22.2.0'
 compile 'com.google.android.gms:play-services-wearable:7.5.0'
 compile 'com.android.support:recyclerview-v7:22.2.0'
 compile 'com.jakewharton:butterknife:6.1.0'
 } apply plugin: 'com.android.application'
 
 
 android {
 compileSdkVersion 22
 buildToolsVersion "22.0.1"
 
 defaultConfig {
 applicationId "com.jebware.weardemo"
 minSdkVersion 20
 targetSdkVersion 22
 versionCode 1
 versionName "1.0"
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.google.android.support:wearable:1.2.0'
 compile 'com.google.android.gms:play-services-wearable:7.5.0'
 compile 'com.android.support:recyclerview-v7:22.2.0'
 compile 'com.jakewharton:butterknife:6.1.0'
 } mobile/build.gradle wear/build.gradle

Slide 25

Slide 25 text

GoogleApiClient @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_grocery_list);
 
 googleApiClient = new GoogleApiClient.Builder(this)
 .addApi(Wearable.API)
 .addConnectionCallbacks(connectionCallbacks)
 .addOnConnectionFailedListener(onConnectionFailedListener)
 .build();
 googleApiClient.connect();
 } private GoogleApiClient.ConnectionCallbacks connectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
 @Override
 public void onConnected(Bundle bundle) {
 // connection ready, now load your data
 }
 
 @Override
 public void onConnectionSuspended(int i) {
 //NOOP
 }
 };
 
 private GoogleApiClient.OnConnectionFailedListener onConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
 //NOOP
 }
 };

Slide 26

Slide 26 text

GoogleApiClient @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_grocery_list);
 
 googleApiClient = new GoogleApiClient.Builder(this)
 .addApi(Wearable.API)
 .addConnectionCallbacks(connectionCallbacks)
 .addOnConnectionFailedListener(onConnectionFailedListener)
 .build();
 googleApiClient.connect();
 } private GoogleApiClient.ConnectionCallbacks connectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
 @Override
 public void onConnected(Bundle bundle) {
 // connection ready, now load your data
 }
 
 @Override
 public void onConnectionSuspended(int i) {
 //NOOP
 }
 };
 
 private GoogleApiClient.OnConnectionFailedListener onConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
 //NOOP
 }
 };

Slide 27

Slide 27 text

GoogleApiClient @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_grocery_list);
 
 googleApiClient = new GoogleApiClient.Builder(this)
 .addApi(Wearable.API)
 .addConnectionCallbacks(connectionCallbacks)
 .addOnConnectionFailedListener(onConnectionFailedListener)
 .build();
 googleApiClient.connect();
 } private GoogleApiClient.ConnectionCallbacks connectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
 @Override
 public void onConnected(Bundle bundle) {
 // connection ready, now load your data
 }
 
 @Override
 public void onConnectionSuspended(int i) {
 //NOOP
 }
 };
 
 private GoogleApiClient.OnConnectionFailedListener onConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
 //NOOP
 }
 };

Slide 28

Slide 28 text

GoogleApiClient @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_grocery_list);
 
 googleApiClient = new GoogleApiClient.Builder(this)
 .addApi(Wearable.API)
 .addConnectionCallbacks(connectionCallbacks)
 .addOnConnectionFailedListener(onConnectionFailedListener)
 .build();
 googleApiClient.connect();
 } private GoogleApiClient.ConnectionCallbacks connectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
 @Override
 public void onConnected(Bundle bundle) {
 // connection ready, now load your data
 }
 
 @Override
 public void onConnectionSuspended(int i) {
 //NOOP
 }
 };
 
 private GoogleApiClient.OnConnectionFailedListener onConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
 //NOOP
 }
 }; Async All The Things!

Slide 29

Slide 29 text

Put Data @OnClick(android.R.id.button1)
 void addItem() {
 Grocery grocery = new Grocery(inputField.getText().toString());
 inputField.setText(null);
 
 //every item needs a unique path
 String path = String.format("/item/%d", grocery.id);
 PutDataMapRequest request = PutDataMapRequest.create(path);
 request.getDataMap().putInt("id", grocery.id);
 request.getDataMap().putString("value", grocery.value);
 PendingResult result =
 Wearable.DataApi.putDataItem(googleApiClient, request.asPutDataRequest());
 result.setResultCallback(new ResultCallback() {
 @Override
 public void onResult(DataApi.DataItemResult dataItemResult) {
 refreshList();
 }
 });
 }

Slide 30

Slide 30 text

Get Data private void refreshList() {
 Wearable.DataApi.getDataItems(googleApiClient).setResultCallback(new ResultCallback() {
 @Override
 public void onResult(DataItemBuffer dataItems) {
 groceries.clear();
 for (DataItem dataItem : dataItems) {
 DataMapItem mapItem = DataMapItem.fromDataItem(dataItem);
 int id = mapItem.getDataMap().getInt("id");
 String value = mapItem.getDataMap().getString("value");
 groceries.add(new Grocery(id, value, dataItem));
 }
 adapter.notifyDataSetChanged();
 }
 });
 }

Slide 31

Slide 31 text

Delete Data private void deleteItem(DataItem dataItem) {
 PendingResult result = 
 Wearable.DataApi.deleteDataItems(googleApiClient, dataItem.getUri());
 
 result.setResultCallback(new ResultCallback() {
 @Override
 public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
 refreshList();
 }
 });
 }

Slide 32

Slide 32 text

Data Listeners private GoogleApiClient.ConnectionCallbacks connectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
 @Override
 public void onConnected(Bundle bundle) {
 refreshList();
 // subscribe to updates to the data model
 Wearable.DataApi.addListener(googleApiClient, dataListener);
 }
 
 @Override
 public void onConnectionSuspended(int i) {
 //NOOP
 }
 }; private DataApi.DataListener dataListener = new DataApi.DataListener() {
 @Override
 public void onDataChanged(DataEventBuffer dataEventBuffer) {
 refreshList();
 }
 };

Slide 33

Slide 33 text

Data Listeners private GoogleApiClient.ConnectionCallbacks connectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
 @Override
 public void onConnected(Bundle bundle) {
 refreshList();
 // subscribe to updates to the data model
 Wearable.DataApi.addListener(googleApiClient, dataListener);
 }
 
 @Override
 public void onConnectionSuspended(int i) {
 //NOOP
 }
 }; private DataApi.DataListener dataListener = new DataApi.DataListener() {
 @Override
 public void onDataChanged(DataEventBuffer dataEventBuffer) {
 refreshList();
 }
 };

Slide 34

Slide 34 text

Notifications PendingIntent openListPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, GroceryListActivity.class), 0);
 
 NotificationCompat.Action action = new NotificationCompat.Action.Builder(
 R.drawable.ic_action_reload, "Reload", openListPendingIntent)
 .build();
 
 Notification notif = new NotificationCompat.Builder(this)
 .setSmallIcon(R.mipmap.ic_launcher)
 .setContentTitle("WearDemo")
 .setContentText("List Updated")
 .setContentIntent(openListPendingIntent)
 .setAutoCancel(true)
 .extend(new android.support.v4.app.NotificationCompat.WearableExtender().addAction(action))
 .build();
 
 NotificationManagerCompat mgr = NotificationManagerCompat.from(this);
 mgr.notify(NOTIFICATION_ID, notif);

Slide 35

Slide 35 text

Notifications PendingIntent openListPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, GroceryListActivity.class), 0);
 
 NotificationCompat.Action action = new NotificationCompat.Action.Builder(
 R.drawable.ic_action_reload, "Reload", openListPendingIntent)
 .build();
 
 Notification notif = new NotificationCompat.Builder(this)
 .setSmallIcon(R.mipmap.ic_launcher)
 .setContentTitle("WearDemo")
 .setContentText("List Updated")
 .setContentIntent(openListPendingIntent)
 .setAutoCancel(true)
 .extend(new android.support.v4.app.NotificationCompat.WearableExtender().addAction(action))
 .build();
 
 NotificationManagerCompat mgr = NotificationManagerCompat.from(this);
 mgr.notify(NOTIFICATION_ID, notif);

Slide 36

Slide 36 text

Notifications PendingIntent openListPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, GroceryListActivity.class), 0);
 
 NotificationCompat.Action action = new NotificationCompat.Action.Builder(
 R.drawable.ic_action_reload, "Reload", openListPendingIntent)
 .build();
 
 Notification notif = new NotificationCompat.Builder(this)
 .setSmallIcon(R.mipmap.ic_launcher)
 .setContentTitle("WearDemo")
 .setContentText("List Updated")
 .setContentIntent(openListPendingIntent)
 .setAutoCancel(true)
 .extend(new android.support.v4.app.NotificationCompat.WearableExtender().addAction(action))
 .build();
 
 NotificationManagerCompat mgr = NotificationManagerCompat.from(this);
 mgr.notify(NOTIFICATION_ID, notif);

Slide 37

Slide 37 text

Activities & Manifest 
 
 
 
 
 package com.jebware.weardemo;
 
 import android.app.Activity;
 
 public class GroceryListActivity extends Activity {
 
 ….
 
 }


Slide 38

Slide 38 text

Activities & Manifest 
 
 
 
 
 package com.jebware.weardemo;
 
 import android.app.Activity;
 
 public class GroceryListActivity extends Activity {
 
 ….
 
 }
 Same as phone app

Slide 39

Slide 39 text

Layout & View Widgets

Slide 40

Slide 40 text

Layout & View Widgets Mostly the same

Slide 41

Slide 41 text

Layout & View Widgets Mostly the same (Except for circles)

Slide 42

Slide 42 text

Layout

Slide 43

Slide 43 text

Layout (╯°□°)╯︵ ┻━┻)

Slide 44

Slide 44 text

WatchViewStub

Slide 45

Slide 45 text

WatchViewStub

Slide 46

Slide 46 text

BoxInsetLayout 
 
 
 


Slide 47

Slide 47 text

BoxInsetLayout 
 
 
 


Slide 48

Slide 48 text

Highlight Reel • NotificationCompat • wearApp project(':wear') • Cards, Cards, Cards • GoogleApiClient - Wearable.DataApi

Slide 49

Slide 49 text

QUESTIONS? Jeb Ware @jebstuart https://github.com/jebstuart/WearDemo