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

Getting Started with Android Wear

Getting Started with Android Wear

Talk given at the Charlotte Android Developers meetup, Jul 14, 2015.

0aa5e9706ca27fc5c8a100a627c20a16?s=128

jebstuart

July 15, 2015
Tweet

More Decks by jebstuart

Other Decks in Programming

Transcript

  1. Getting Started with Android Wear Jeb Ware @jebstuart

  2. Outline • Step 1: Design Considerations • Step 2: Code

    Samples • Step 3: ?? • Step 4: Profit
  3. 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
  4. Do I Need A Wear App?

  5. Do I Need A Wear App? Probably Not.

  6. Do I Need A Wear App? • Android notification automatically

    mirrored • NotificationCompat • Actions • “Big View”
  7. Don’t Be Annoying

  8. Don’t Be Annoying • Users can block your notifications from

    showing on wearable
  9. Good Use Cases

  10. Good Use Cases • Reply to a text • Show

    directions to meeting • Add a reminder
  11. Good Use Cases • Order a pizza • Scroll through

    your photo library
  12. Do I Need A Wear App? • Figure out your

    user’s tasks • Then decide between rich notifications or a full-on Wear App
  13. User Interface

  14. • Launcher is rarely used • Use Voice, but have

    a fallback • Automatic, contextual cues
 (be like Google Now) User Interface
  15. • Very small screen • gross motor skills
 (swipes, not

    pinpoint taps) • max 1 or 2 tap targets on-screen User Interface
  16. User Interface • CardScrollView • CardFrame • CardFragment

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

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

  19. CODE?

  20. App Architecture Watch Phone Internet GoogleApiClient Location, Network, etc. UI,

    Sensors Wearable.DataApi
  21. Project Creation

  22. 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
  23. 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
  24. 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
  25. 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
 }
 };
  26. 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
 }
 };
  27. 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
 }
 };
  28. 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!
  29. 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<DataApi.DataItemResult> result =
 Wearable.DataApi.putDataItem(googleApiClient, request.asPutDataRequest());
 result.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
 @Override
 public void onResult(DataApi.DataItemResult dataItemResult) {
 refreshList();
 }
 });
 }
  30. Get Data private void refreshList() {
 Wearable.DataApi.getDataItems(googleApiClient).setResultCallback(new ResultCallback<DataItemBuffer>() {
 @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();
 }
 });
 }
  31. Delete Data private void deleteItem(DataItem dataItem) {
 PendingResult<DataApi.DeleteDataItemsResult> result =

    
 Wearable.DataApi.deleteDataItems(googleApiClient, dataItem.getUri());
 
 result.setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
 @Override
 public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
 refreshList();
 }
 });
 }
  32. 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();
 }
 };
  33. 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();
 }
 };
  34. 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);
  35. 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);
  36. 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);
  37. Activities & Manifest <activity
 android:name=".GroceryListActivity"
 android:label="@string/app_name" >
 <intent-filter>
 <action android:name="android.intent.action.MAIN"

    />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity> package com.jebware.weardemo;
 
 import android.app.Activity;
 
 public class GroceryListActivity extends Activity {
 
 ….
 
 }

  38. Activities & Manifest <activity
 android:name=".GroceryListActivity"
 android:label="@string/app_name" >
 <intent-filter>
 <action android:name="android.intent.action.MAIN"

    />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity> package com.jebware.weardemo;
 
 import android.app.Activity;
 
 public class GroceryListActivity extends Activity {
 
 ….
 
 }
 Same as phone app
  39. Layout & View Widgets

  40. Layout & View Widgets Mostly the same

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

  42. Layout

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

  44. WatchViewStub <android.support.wearable.view.WatchViewStub
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:id="@+id/stub"
 app:rectLayout="@layout/rect_layout"
 app:roundLayout="@layout/round_layout"/>

  45. WatchViewStub <android.support.wearable.view.WatchViewStub
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:id="@+id/stub"
 app:rectLayout="@layout/rect_layout"
 app:roundLayout="@layout/round_layout"/>

  46. BoxInsetLayout <android.support.wearable.view.BoxInsetLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/watch_view_stub"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".GroceryListActivity"
 tools:deviceIds="wear"

    >
 
 <android.support.v7.widget.RecyclerView
 android:id="@android:id/list"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 app:layout_box="all"
 />
 
 </android.support.wearable.view.BoxInsetLayout>
  47. BoxInsetLayout <android.support.wearable.view.BoxInsetLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/watch_view_stub"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".GroceryListActivity"
 tools:deviceIds="wear"

    >
 
 <android.support.v7.widget.RecyclerView
 android:id="@android:id/list"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 app:layout_box="all"
 />
 
 </android.support.wearable.view.BoxInsetLayout>
  48. Highlight Reel • NotificationCompat • wearApp project(':wear') • Cards, Cards,

    Cards • GoogleApiClient - Wearable.DataApi
  49. QUESTIONS? Jeb Ware @jebstuart https://github.com/jebstuart/WearDemo