77
Android Wear: A Developer’s Perspective Marc Lester Tan Mobility Innova3on Center w: marctan.com t: @mharkus +MarcLesterTan

Android Wear: A Developer's Perspective

  • Upload
    vin-lim

  • View
    300

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Android Wear: A Developer's Perspective

Android Wear: A Developer’s Perspective

Marc  Lester  Tan                            Mobility  Innova3on  Center  w:  marctan.com  t:  @mharkus  +MarcLesterTan  

Page 2: Android Wear: A Developer's Perspective

Agenda  •  Introduc3on  to  Android  Wear  • No3fica3ons  • Wearable  Apps    • Watch  Faces  • Demo  

Page 3: Android Wear: A Developer's Perspective
Page 4: Android Wear: A Developer's Perspective
Page 5: Android Wear: A Developer's Perspective
Page 6: Android Wear: A Developer's Perspective
Page 7: Android Wear: A Developer's Perspective
Page 8: Android Wear: A Developer's Perspective

Notifications

Page 9: Android Wear: A Developer's Perspective

Simple  No3fica3on  PendingIntent pendingIntent = createIntent(); !!NotificationCompat.Builder builder = new NotificationCompat.Builder(this) ! .setContentTitle(“Message from Weng”) ! .setContentText(“Don’t forget to try Penang Laksa!”) ! .setSmallIcon(R.drawable.ic_launcher) ! .setContentIntent(pendingIntent); !!notificationMgr = NotificationManagerCompat.from(this); !!notificationMgr.notify(0, builder.build()); !!  

Page 10: Android Wear: A Developer's Perspective
Page 11: Android Wear: A Developer's Perspective
Page 12: Android Wear: A Developer's Perspective

NO WORK REQUIRED

Page 13: Android Wear: A Developer's Perspective
Page 14: Android Wear: A Developer's Perspective

NO WORK REQUIRED

Page 15: Android Wear: A Developer's Perspective

Replies Pages Stacks

Page 16: Android Wear: A Developer's Perspective

Stacked Notifications

Page 17: Android Wear: A Developer's Perspective

Stacked  No3fica3ons  builder1 = createNotification(“Don’t forget to try Penang Laksa!”); !builder1.setGroup("MESSAGES_GROUP_KEY”); !!builder2 = createNotification(“Also their Char Koay Teow!”); !builder2.setGroup("MESSAGES_GROUP_KEY”); !!summary = createNotification(“2 Messages from Weng”); !summary.setGroup(”MESSAGES_GROUP_KEY”); !summary.setGroupSummary(true); !!notificationMgr.notify(0, builder1.build()); !notificationMgr.notify(1, builder2.build()); !notificationMgr.notify(2, summary.build()); !!

Page 18: Android Wear: A Developer's Perspective
Page 19: Android Wear: A Developer's Perspective

Pages

Page 20: Android Wear: A Developer's Perspective

Pages  NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle(); !style.setBigContentTitle(”Penang Laksa"); !!pageNotif = NotificationCompat.Builder(this) ! .setStyle(style) ! .setContentText("Mouth Watering!") ! .extend(new NotificationCompat.WearableExtender() ! .setBackground(penangLaksaBitmap) ! .build(); !!!

Page 21: Android Wear: A Developer's Perspective

Pages  builder1 = createNotification(“Don’t forget to try Penang Laksa!”); !!wearableExtender = ! new NotificationCompat.WearableExtender() ! .setHintHideIcon(true) ! .setBackground(mainbgBitmap) ! .addPage(pageNotif); !!builder1.extend(wearableExtender); !notificationMgr.notify(0, builder1.build()); !!

!!!

Page 22: Android Wear: A Developer's Perspective
Page 23: Android Wear: A Developer's Perspective

Replies

Page 24: Android Wear: A Developer's Perspective

Replies  •  RemoteInput remoteInput = new

RemoteInput.Builder("extra_voice_reply”) ! .setLabel("What do you think?") ! .setChoices(new String[]{ ! "Awesome", ! "Shiok!", ! "Nom nom nom!" ! }) ! .build(); !!

Page 25: Android Wear: A Developer's Perspective

Replies  Action action = new Action.Builder(replyIcon,"Reply”,

replyPendingIntent) !.addRemoteInput(remoteInput) !.build(); !

!builder1 = createNotification(“Don’t forget to try Penang Laksa!”); !builder1.extend(wearableExtender.addAction(action)); !notificationMgr.notify(0, builder1.build()); !!!!!!!!

Page 26: Android Wear: A Developer's Perspective
Page 27: Android Wear: A Developer's Perspective

Receiving  Voice  Input  Bundle remoteInput = RemoteInput.getResultsFromIntent(getIntent()); !!if (remoteInput != null) { ! reply = remoteInput.getCharSequence("extra_voice_reply”); !} !!!!!!!!

Page 28: Android Wear: A Developer's Perspective

Wearable Apps

#GDGPenang  

Page 29: Android Wear: A Developer's Perspective

Wearable  Apps  •  Run  directly  on  the  device  •  Access  to  sensors  and  GPU  •  Greatly  differ  in  design  and  usability  •  Limited  func3onality  

Page 30: Android Wear: A Developer's Perspective

Wearable  vs  Handheld  Apps  •  System  enforces  3meout  period  •  Rela3vely  small  in  size  and  func3onality  •  Users  don’t  download  apps  directly  to  wearable  •  Don’t  support  the  following  APIs  

•  android.webkit  •  android.print  •  android.app.backup  

•  android.appwidget  •  android.hardware.usb  

   

Page 31: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 32: Android Wear: A Developer's Perspective

Crea3ng  Wearable  Apps  

Page 33: Android Wear: A Developer's Perspective

Crea3ng  Wearable  Apps  

Page 34: Android Wear: A Developer's Perspective

Crea3ng  Wearable  Apps  

Page 35: Android Wear: A Developer's Perspective

Crea3ng  Wearable  Apps  

Page 36: Android Wear: A Developer's Perspective

Crea3ng  Wearable  Apps  

Page 37: Android Wear: A Developer's Perspective

Crea3ng  Wearable  Apps  

Page 38: Android Wear: A Developer's Perspective

Crea3ng  Wearable  Apps  

Page 39: Android Wear: A Developer's Perspective

Data Exchange Custom UI Voice Actions

Page 40: Android Wear: A Developer's Perspective
Page 41: Android Wear: A Developer's Perspective

Node

Data

Message

Data  Exchange  

Page 42: Android Wear: A Developer's Perspective

Create  a  GoogleApiClient  GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this) ! .addConnectionCallbacks(new ConnectionCallbacks() { ! public void onConnected(Bundle connectionHint) { ! // Now you can use the data layer API ! } !

... ! }) ! .addOnConnectionFailedListener(new OnConnectionFailedListener()

{ ! public void onConnectionFailed(ConnectionResult result) { ! } ! }) ! // Request access only to the Wearable API ! .addApi(Wearable.API) ! .build(); !!mGoogleApiClient.connect(); !!  

Page 43: Android Wear: A Developer's Perspective

Check  for  connected  nodes  nodes = Wearable.NodeApi !

.getConnectedNodes(mGoogleApiClient) !

.await(); !!nodeList = nodes.getNodes(); !!if (nodeList.size() > 0) { ! connectedNode = nodeList.get(0); !} !!  

Page 44: Android Wear: A Developer's Perspective

Send  a  message  result = Wearable.MessageApi ! .sendMessage(mGoogleApiClient, parentNode.getId(), "/start/MainActivity/", message.getBytes()); !!result.setResultCallback(new

ResultCallback<MessageApi.SendMessageResult>() { ! public void onResult(MessageApi.SendMessageResult

sendMessageResult) { !! if (!sendMessageResult.getStatus().isSuccess()) { !

// handle error ! } ! } !}); !

Page 45: Android Wear: A Developer's Perspective

Receive  a  message  @Override !public void onMessageReceived(MessageEvent messageEvent) { ! String message = new String(messageEvent.getData()); !} !!

Page 46: Android Wear: A Developer's Perspective

Send  a  data  map = PutDataMapRequest.create("/image"); !map.getDataMap().putAsset("image”,assetFromBitmap); !!PutDataRequest request = map.asPutDataRequest(); !pendingResult = Wearable.DataApi !

.putDataItem(mGoogleApiClient,request); !!!

Page 47: Android Wear: A Developer's Perspective

Receive  a  data  @Override !public void onDataChanged(DataEventBuffer dataEvents) { !

for (DataEvent event : dataEvents) { ! if (event.getType() == DataEvent.TYPE_CHANGED && ! event.getDataItem() !

.getUri().getPath() !

.equals("/image")) { !! dataMapItem = DataMapItem !

.fromDataItem(event.getDataItem()); ! !

profileAsset = dataMapItem.getDataMap() !.getAsset("image"); !

} !} !

} !

Page 48: Android Wear: A Developer's Perspective

public class MyWearListener extends WearableListenerService { !!! @Override ! public void onMessageReceived(MessageEvent messageEvent) { ! } !! @Override ! public void onDataChanged(DataEventBuffer dataEvents) { ! } !!! @Override ! public void onPeerConnected(Node peer) { ! } !! @Override ! public void onPeerDisconnected(Node peer) { ! } !} !!

Page 49: Android Wear: A Developer's Perspective

<service android:name=”.MyWearListener" > <intent-filter>

<action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter> </service>

Intent Filter

Page 50: Android Wear: A Developer's Perspective

Custom Layouts

Page 51: Android Wear: A Developer's Perspective
Page 52: Android Wear: A Developer's Perspective

dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.android.support:wearable:+' compile 'com.google.android.gms:play-services-wearable:+' }

build.gradle

Page 53: Android Wear: A Developer's Perspective

●  BoxInsetLayout  

●  Card  Fragment  

●  CircledImageView  

●  ConfirmationActivity  

●  DismissOverlayView  

●  DelayedConfirmationView  

●  GridViewPager  

●  GridPagerAdapter  

●  FragmentGridPagerAdapter  

●  WatchViewStub  

Page 54: Android Wear: A Developer's Perspective

WatchViewStub  <?xml version="1.0" encoding="utf-8"?> !<android.support.wearable.view.WatchViewStub ! 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 ! app:rectLayout="@layout/rect_activity_main" ! app:roundLayout="@layout/round_activity_main" ! tools:context=".Main" ! tools:deviceIds="wear" android:padding="12dp"> !</android.support.wearable.view.WatchViewStub> !!  

Page 55: Android Wear: A Developer's Perspective

BoxInsetLayout  

Page 56: Android Wear: A Developer's Perspective

DelayedConfirma3onView  &  Confirma3onAc3vity    

Page 57: Android Wear: A Developer's Perspective

CardFragment  

Page 58: Android Wear: A Developer's Perspective

Voice Actions

Page 59: Android Wear: A Developer's Perspective
Page 60: Android Wear: A Developer's Perspective

<activity android:name="MyNoteActivity"> <intent-filter> <action android:name="android.intent.action.SEND" /> <category

android:name="com.google.android.voicesearch.SELF_NOTE" /> </intent-filter>

</activity>

System  Provided  Ac3on  

Page 61: Android Wear: A Developer's Perspective

●  Call a car/taxi

●  Take a note

●  Set alarm

●  Set timer

●  Start/Stop a bike ride

●  Start/Stop a run

●  Start/Stop a workout

●  Show heart rate

●  Show step count

Page 62: Android Wear: A Developer's Perspective

<activity android:name="StartRunActivity" android:label="MyRunningApp">

<intent-filter> <action android:name="android.intent.action.MAIN" /> <category

android:name="android.intent.category.LAUNCHER" /> </intent-filter>

</activity>

App  Provided  Voice  Ac3on  

Page 63: Android Wear: A Developer's Perspective

private void displaySpeechRecognizer() { ! Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); !! intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, ! RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); !! startActivityForResult(intent, SPEECH_REQUEST_CODE); !} !

Speech  Recognizer  

Page 64: Android Wear: A Developer's Perspective

Watch Faces

Page 65: Android Wear: A Developer's Perspective
Page 66: Android Wear: A Developer's Perspective

UNOFFICIAL

Page 67: Android Wear: A Developer's Perspective

Create  a  Wear  Watch  Face  •  Same  steps  as  crea3ng  a  wearable  app  •  Uses  Executors  for  per-­‐second  updates  •  Uses  Intent.ACTION_TIME_TICK  for  per-­‐minute  updates  

•  Use  DisplayManager  for  screen  events  •  HACK!  

Page 68: Android Wear: A Developer's Perspective

Create  a  Wear  Watch  Face  

Page 69: Android Wear: A Developer's Perspective

<activity ! android:name="com.marctan.hellowatchface.MainActivity" ! android:label="@string/app_name" ! android:allowEmbedded="true"> !!

<meta-data ! android:name="com.google.android.clockwork.home.preview" !

android:resource="@drawable/ic_launcher" /> !!

<intent-filter> ! <action android:name="android.intent.action.MAIN" /> !! <category android:name="com.google.android.clockwork.home.category.HOME_BACKGROUND" /> !

</intent-filter> !. . . !

</activity> !!!!!

Modify  AndroidManifest.xml  

Page 70: Android Wear: A Developer's Perspective

private void updateEverySecond() { ! . . . ! scheduledFuture = scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() { !! public void run() { ! runOnUiThread(new Runnable() { ! @Override ! public void run() { ! ClockManager.getInstance().updateTime(); ! } ! }); !! } ! }, 0, 1, TimeUnit.SECONDS); !} !!

Per-­‐second  updates  

Page 71: Android Wear: A Developer's Perspective

private void updateEveryMinute() { ! if (scheduledFuture != null) { ! scheduledFuture.cancel(true); ! } !! ClockManager.getInstance().setAmbientMode(true); !} !

Per-­‐minute  updates  

Page 72: Android Wear: A Developer's Perspective

public void onDisplayAdded(int i) { !} !!public void onDisplayRemoved(int i) { !} !!public void onDisplayChanged(int displayId) { ! switch(displayManager.getDisplay(displayId).getState()){ !

case Display.STATE_OFF: ! case Display.STATE_DOZING: ! updateEveryMinute(); ! break; ! default: ! updateEverySecond(); ! break; ! } !} !

DisplayListener  Events  

Page 73: Android Wear: A Developer's Perspective

Hello  Wear  Watch  Face  

Page 74: Android Wear: A Developer's Perspective

Demo

Page 75: Android Wear: A Developer's Perspective

Thank You

Demo  Sources  github.com/mharkus/DevfestGeorgeTown2014      Android  Wear  Dev  Documenta3on  developer.android.com/wear/index.html    My  Blog  marctan.com  

Page 76: Android Wear: A Developer's Perspective

Android Wear: A Developer’s Perspective

Page 77: Android Wear: A Developer's Perspective

Android Wear: A Developer’s Perspective