74
Android Wear: A Developer’s Perspective Marc Lester Tan Mobility Innovation Center, APJ w: marctan.com t: @mharkus +MarcLesterTan

GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

  • Upload
    mharkus

  • View
    1.456

  • Download
    0

Embed Size (px)

DESCRIPTION

These are the slides that I've used for my Android Wear presentation in GDG GeorgeTown Malaysia's first ever Devfest and GDays. November 15, 2014

Citation preview

Page 1: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Android Wear: A Developer’s

Perspective

Marc Lester TanMobility Innovation Center, APJ

w: marctan.comt: @mharkus+MarcLesterTan

Page 2: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Agenda• Introduction to Android Wear• Notifications• Wearable Apps • Watch Faces• Demo

Page 3: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 4: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 5: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 6: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 7: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 8: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Notifications

Page 9: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Simple NotificationPendingIntent 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: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 11: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 12: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

NO WORK

REQUIRED

Page 13: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

RepliesPagesStacks

Page 14: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Stacked

Notifications

Page 15: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Stacked Notificationsbuilder1 = 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 16: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 17: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Pages

Page 18: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

PagesNotificationCompat.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 19: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Pagesbuilder1 = 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 20: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 21: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Replies

Page 22: GDG GeorgeTown Devfest 2014 Presentation: 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 23: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

RepliesAction 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 24: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 25: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Receiving Voice InputBundle remoteInput = RemoteInput.getResultsFromIntent(getIntent());

if (remoteInput != null) {

reply = remoteInput.getCharSequence("extra_voice_reply”);

}

Page 26: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Wearable Apps

Page 27: GDG GeorgeTown Devfest 2014 Presentation: 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 functionality

Page 28: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Wearable vs Handheld Apps

• System enforces timeout period

• Relatively small in size and functionality

• 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 29: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable

Apps

Page 30: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 31: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 32: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 33: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 34: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 35: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 36: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Creating Wearable Apps

Page 37: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Data Exchange Custom UI Voice Actions

Page 38: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 39: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Node

Data

Message

Data Exchange

Page 40: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Create a GoogleApiClientGoogleApiClient 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 41: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Check for connected nodesnodes = Wearable.NodeApi

.getConnectedNodes(mGoogleApiClient)

.await();

nodeList = nodes.getNodes();

if (nodeList.size() > 0) {

connectedNode = nodeList.get(0);

}

Page 42: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Send a messageresult = 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 43: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Receive a message@Override

public void onMessageReceived(MessageEvent messageEvent) {

String message = new String(messageEvent.getData());

}

Page 44: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Send a datamap = PutDataMapRequest.create("/image");

map.getDataMap().putAsset("image”,assetFromBitmap);

PutDataRequest request = map.asPutDataRequest();

pendingResult = Wearable.DataApi

.putDataItem(mGoogleApiClient,request);

Page 45: GDG GeorgeTown Devfest 2014 Presentation: 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 46: GDG GeorgeTown Devfest 2014 Presentation: 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 47: GDG GeorgeTown Devfest 2014 Presentation: 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 48: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Custom

Layouts

Page 49: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 50: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

● BoxInsetLayout

● Card Fragment

● CircledImageView

● ConfirmationActivity

● DismissOverlayView

● DelayedConfirmationView

● GridViewPager

● GridPagerAdapter

● FragmentGridPagerAdapter

● WatchViewStub

Page 51: GDG GeorgeTown Devfest 2014 Presentation: 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 52: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

BoxInsetLayout

Page 53: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

DelayedConfirmationView & ConfirmationActivity

Page 54: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

CardFragment

Page 55: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Voice Actions

Page 56: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 57: GDG GeorgeTown Devfest 2014 Presentation: 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 Action

Page 58: GDG GeorgeTown Devfest 2014 Presentation: 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 59: GDG GeorgeTown Devfest 2014 Presentation: 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 Action

Page 60: GDG GeorgeTown Devfest 2014 Presentation: 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 61: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Watch Faces

Page 62: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective
Page 63: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

UNOFFICIAL

Page 64: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Create a Wear Watch Face

• Same steps as creating 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 65: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Create a Wear Watch Face

Page 66: GDG GeorgeTown Devfest 2014 Presentation: 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 67: GDG GeorgeTown Devfest 2014 Presentation: 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 68: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

private void updateEverySecond() {

. . .

scheduledFuture = scheduleTaskExecutor.scheduleAtFixedRate(new

Runnable() {

public void run() {

updateClockView();

}

}, 0, 1, TimeUnit.SECONDS);

}

Per-second updates

Page 69: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

private void updateEveryMinute() {

if (scheduledFuture != null) {

scheduledFuture.cancel(true);

}

ClockManager.getInstance().setAmbientMode(true);

}

Per-minute updates

Page 70: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Hello Wear Watch Face

Page 71: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Demo

Page 72: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Thank You

Source Codegithub.com/mharkus/DevfestGeorgeTown2014

Android Wear Dev Documentationdeveloper.android.com/wear/index.html

My Blogmarctan.com

Page 73: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Android Wear: A Developer’s

Perspective

Page 74: GDG GeorgeTown Devfest 2014 Presentation: Android Wear: A Developer's Perspective

Android Wear: A Developer’s

Perspective