Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
+PeterFriese
@peterfriese
#AndroidWear
Design Principles
• Launched automatically
Design Principles
• Launched automatically
• Glanceable
Design Principles
• Launched automatically
• Glanceable
• Suggest and Demand
Design Principles
• Launched automatically
• Glanceable
• Suggest and Demand
• Zero or low interaction
Design Principles
Developing for
Android Wear
Notifications ApplicationsWatch Faces
Notifications
Simple Notifications
Look, ma - no work required!
Intent viewIntent = new Intent(context, DummyActivity.class);



PendingIntent viewPendingIntent = PendingIntent.getActivi...
Can we do better?
BigPictureStyle
Enhanced Notifications
Intent viewIntent = new Intent(context, DummyActivity.class);



PendingIntent viewPendingIntent = PendingIntent.getActivi...
Intent viewIntent = new Intent(context, DummyActivity.class);



PendingIntent viewPendingIntent = PendingIntent.getActivi...
Pages
Enhanced Notifications
ArrayList<Notification> pages = new ArrayList<Notification>();

pages.add(new NotificationCompat.Builder(context)

.setCon...
ArrayList<Notification> pages = new ArrayList<Notification>();

pages.add(new NotificationCompat.Builder(context)

.setCon...
ArrayList<Notification> pages = new ArrayList<Notification>();

pages.add(new NotificationCompat.Builder(context)

.setCon...
pages.add(new NotificationCompat.Builder(context)

.extend(new NotificationCompat.WearableExtender()

.setHintShowBackgrou...
pages.add(new NotificationCompat.Builder(context)

.extend(new NotificationCompat.WearableExtender()

.setHintShowBackgrou...
ArrayList<Notification> pages = new ArrayList<Notification>();

pages.add(new NotificationCompat.Builder(context)

// ... ...
ArrayList<Notification> pages = new ArrayList<Notification>();

pages.add(new NotificationCompat.Builder(context)

// ... ...
ArrayList<Notification> pages = new ArrayList<Notification>();

pages.add(new NotificationCompat.Builder(context)

// ... ...
Voice Input
Enhanced Notifications
// Feedback intent

Intent replyIntent = new Intent(context, DummyActivity.class);

PendingIntent replyPendingIntent = Pen...
// Feedback intent

Intent replyIntent = new Intent(context, DummyActivity.class);

PendingIntent replyPendingIntent = Pen...
// Feedback intent

Intent replyIntent = new Intent(context, DummyActivity.class);

PendingIntent replyPendingIntent = Pen...
// Feedback intent

Intent replyIntent = new Intent(context, DummyActivity.class);

PendingIntent replyPendingIntent = Pen...
String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);

RemoteInput remoteInput = new...
String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);

RemoteInput remoteInput = new...
Intent intent = getIntent();

if (intent != null) {
Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
...
Intent intent = getIntent();

if (intent != null) {
Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
...
Intent intent = getIntent();

if (intent != null) {
Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
...
Actions
Enhanced Notifications
Intent mapIntent = new Intent(Intent.ACTION_VIEW);

Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode("London Heathrow"));
...
Intent mapIntent = new Intent(Intent.ACTION_VIEW);

Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode("London Heathrow"));
...
Intent mapIntent = new Intent(Intent.ACTION_VIEW);

Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode("London Heathrow"));
...
Applications
Launching
Wearable apps
Using app-provided
voice actions
Using the start
menu
<application

android:icon="@drawable/greenlinelogo"

android:label="@string/app_name"

android:theme="@android:style/Them...
<application

android:icon="@drawable/greenlinelogo"

android:label="@string/app_name"

android:theme="@android:style/Them...
Custom Layouts
Wearable Apps
<android.support.wearable.view.BoxInsetLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http...
<android.support.wearable.view.BoxInsetLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http...
<?xml version="1.0" encoding="utf-8"?>

<merge xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http...
<?xml version="1.0" encoding="utf-8"?>

<merge xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http...
private final class MyItemView extends FrameLayout implements
WearableListView.OnCenterProximityListener {



final Circle...
private final class MyItemView extends FrameLayout implements
WearableListView.OnCenterProximityListener {



final Circle...
private final class MyItemView extends FrameLayout implements
WearableListView.OnCenterProximityListener {



final Circle...
public class CheckInActivity extends Activity implements WearableListView.ClickListener {



@Override

protected void onC...
public class CheckInActivity extends Activity implements WearableListView.ClickListener {



@Override

protected void onC...
Watch Faces
Design Principles
• Square vs round
Design Principles
• Square vs round
• Ambient mode / low-bit
Design Principles
• Square vs round
• Ambient mode / low-bit
• Legibility
Design Principles
56° - Cloudy
56° - Cloudy
Developing
Watch Faces
• Start using a sample
Developing
Watch Faces
• Start using a sample
• Add a new wearable module
Developing
Watch Faces
Architecture
Wearable App Mobile App
CanvasWatchFaceService
ConfigActivity
Engine
ConfigActivityWearableListenerService
Architecture
Wearable App
CanvasWatchFaceService
ConfigActivity
Engine
WearableListenerService
Engine
onCreate
onPropertiesChanged
onDraw
onTimeTick
onAmbientModeChanged
onVisibilityChanged
@Override

public void onCreate(SurfaceHolder holder) {

super.onCreate(holder);



setWatchFaceStyle(new WatchFaceStyle.B...
@Override

public void onCreate(SurfaceHolder holder) {

super.onCreate(holder);



setWatchFaceStyle(new WatchFaceStyle.B...
@Override

public void onCreate(SurfaceHolder holder) {

super.onCreate(holder);



setWatchFaceStyle(new WatchFaceStyle.B...
@Override

public void onCreate(SurfaceHolder holder) {

super.onCreate(holder);



setWatchFaceStyle(new WatchFaceStyle.B...
@Override

public void onDraw(Canvas canvas, Rect bounds) {

mTime.setToNow();



canvas.drawBitmap(mBackgroundScaledBitma...
@Override

public void onDraw(Canvas canvas, Rect bounds) {

mTime.setToNow();



canvas.drawBitmap(mBackgroundScaledBitma...
@Override

public void onDraw(Canvas canvas, Rect bounds) {

mTime.setToNow();



canvas.drawBitmap(mBackgroundScaledBitma...
float centerX = width / 2f;

float centerY = height / 2f;



float innerTickRadius = centerX - 10;

float outerTickRadius ...
float outerX = (float) Math.sin(tickRot) * outerTickRadius;

float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
...
Watch Faces
Demo
+PeterFriese
@
Thank you!
#AndroidWear
W
e’re
hiring!
+PeterFriese
@
Q & A
#AndroidWear
W
e’re
hiring!
W
e’re
hiring!
Introduction to Android Wear
Introduction to Android Wear
Introduction to Android Wear
Introduction to Android Wear
Introduction to Android Wear
Introduction to Android Wear
Introduction to Android Wear
Upcoming SlideShare
Loading in …5
×

Introduction to Android Wear

962 views

Published on

This is an introductory session to Android Wear. We will cover Design Principles and how to implement them. In particular, I will show how to enhance your existing notifications to make them shine on Android Wear, how to implement Android Wear apps that make use of the new possibilities Android Wear gives you as a developer such as voice input and GPS. Finally, we will take a look at implementing watch faces.

Published in: Technology
  • Be the first to comment

Introduction to Android Wear

  1. 1. +PeterFriese @peterfriese #AndroidWear
  2. 2. Design Principles
  3. 3. • Launched automatically Design Principles
  4. 4. • Launched automatically • Glanceable Design Principles
  5. 5. • Launched automatically • Glanceable • Suggest and Demand Design Principles
  6. 6. • Launched automatically • Glanceable • Suggest and Demand • Zero or low interaction Design Principles
  7. 7. Developing for Android Wear
  8. 8. Notifications ApplicationsWatch Faces
  9. 9. Notifications
  10. 10. Simple Notifications Look, ma - no work required!
  11. 11. Intent viewIntent = new Intent(context, DummyActivity.class);
 
 PendingIntent viewPendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
 Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentText("Please proceed to gate C 17 to board. Have a nice flight!")
 .setContentIntent(viewPendingIntent)
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification);
 sendNotification() Simple Notifications
  12. 12. Can we do better?
  13. 13. BigPictureStyle Enhanced Notifications
  14. 14. Intent viewIntent = new Intent(context, DummyActivity.class);
 
 PendingIntent viewPendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
 Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentText("Please proceed to gate C 17 to board. Have a nice flight!")
 .setStyle(
 new NotificationCompat.BigPictureStyle()
 .bigPicture(BitmapFactory.decodeResource(context.getResources(), R.drawable.sanfrancisco))
 .setBigContentTitle("Flight AW123 is ready to board.")
 .setSummaryText("Please proceed to gate C 17 to board. Have a nice flight!"))
 .setContentIntent(viewPendingIntent)
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification);
 sendNotification() BigPictureStyle
  15. 15. Intent viewIntent = new Intent(context, DummyActivity.class);
 
 PendingIntent viewPendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
 Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentText("Please proceed to gate C 17 to board. Have a nice flight!")
 .setStyle(
 new NotificationCompat.BigPictureStyle()
 .bigPicture(BitmapFactory.decodeResource(context.getResources(), R.drawable.sanfrancisco))
 .setBigContentTitle("Flight AW123 is ready to board.")
 .setSummaryText("Please proceed to gate C 17 to board. Have a nice flight!"))
 .setContentIntent(viewPendingIntent)
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification);
 sendNotification() BigPictureStyle Create BigPictureStyle
  16. 16. Pages Enhanced Notifications
  17. 17. ArrayList<Notification> pages = new ArrayList<Notification>();
 pages.add(new NotificationCompat.Builder(context)
 .setContentTitle("Your seat")
 .setContentText("17A")
 .extend(new NotificationCompat.WearableExtender()
 .setBackground(BitmapFactory.decodeResource(context.getResources(), R.drawable.a380_seat)))
 .build()); sendNotification() Pages
  18. 18. ArrayList<Notification> pages = new ArrayList<Notification>();
 pages.add(new NotificationCompat.Builder(context)
 .setContentTitle("Your seat")
 .setContentText("17A")
 .extend(new NotificationCompat.WearableExtender()
 .setBackground(BitmapFactory.decodeResource(context.getResources(), R.drawable.a380_seat)))
 .build()); sendNotification() Pages Create page with title
  19. 19. ArrayList<Notification> pages = new ArrayList<Notification>();
 pages.add(new NotificationCompat.Builder(context)
 .setContentTitle("Your seat")
 .setContentText("17A")
 .extend(new NotificationCompat.WearableExtender()
 .setBackground(BitmapFactory.decodeResource(context.getResources(), R.drawable.a380_seat)))
 .build()); sendNotification() Pages Set background image
  20. 20. pages.add(new NotificationCompat.Builder(context)
 .extend(new NotificationCompat.WearableExtender()
 .setHintShowBackgroundOnly(true) .setHintAvoidBackgroundClipping(true)
 .setHintScreenTimeout(NotificationCompat.WearableExtender.SCREEN_TIMEOUT_LONG)
 .setBackground( BitmapFactory.decodeResource( context.getResources(), R.drawable.qrcode)))
 .build()); sendNotification() Background Only Pages
  21. 21. pages.add(new NotificationCompat.Builder(context)
 .extend(new NotificationCompat.WearableExtender()
 .setHintShowBackgroundOnly(true) .setHintAvoidBackgroundClipping(true)
 .setHintScreenTimeout(NotificationCompat.WearableExtender.SCREEN_TIMEOUT_LONG)
 .setBackground( BitmapFactory.decodeResource( context.getResources(), R.drawable.qrcode)))
 .build()); sendNotification() Background Only Pages Show background only Don’t clip on round displays Extended timeout
  22. 22. ArrayList<Notification> pages = new ArrayList<Notification>();
 pages.add(new NotificationCompat.Builder(context)
 // ... (set properties)
 .build()); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentIntent(viewPendingIntent)
 .extend(new NotificationCompat.WearableExtender()
 .addPages(pages))
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification);
 sendNotification() Adding Pages to Notifications
  23. 23. ArrayList<Notification> pages = new ArrayList<Notification>();
 pages.add(new NotificationCompat.Builder(context)
 // ... (set properties)
 .build()); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentIntent(viewPendingIntent)
 .extend(new NotificationCompat.WearableExtender()
 .addPages(pages))
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification);
 sendNotification() Adding Pages to Notifications Build pages
  24. 24. ArrayList<Notification> pages = new ArrayList<Notification>();
 pages.add(new NotificationCompat.Builder(context)
 // ... (set properties)
 .build()); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentIntent(viewPendingIntent)
 .extend(new NotificationCompat.WearableExtender()
 .addPages(pages))
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification);
 sendNotification() Adding Pages to Notifications Add pages to notification
  25. 25. Voice Input Enhanced Notifications
  26. 26. // Feedback intent
 Intent replyIntent = new Intent(context, DummyActivity.class);
 PendingIntent replyPendingIntent = PendingIntent.getActivity(context, 0, replyIntent, 0);
 
 String replyLabel = context.getResources().getString(R.string.reply_label);
 String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);
 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel(replyLabel)
 .setChoices(cannedResponses)
 .build();
 
 NotificationCompat.Action replyAction =
 new NotificationCompat.Action.Builder( R.drawable.chatbubble_working, replyLabel, replyPendingIntent)
 .addRemoteInput(remoteInput)
 .build(); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 sendNotification() Voice Input
  27. 27. // Feedback intent
 Intent replyIntent = new Intent(context, DummyActivity.class);
 PendingIntent replyPendingIntent = PendingIntent.getActivity(context, 0, replyIntent, 0);
 
 String replyLabel = context.getResources().getString(R.string.reply_label);
 String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);
 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel(replyLabel)
 .setChoices(cannedResponses)
 .build();
 
 NotificationCompat.Action replyAction =
 new NotificationCompat.Action.Builder( R.drawable.chatbubble_working, replyLabel, replyPendingIntent)
 .addRemoteInput(remoteInput)
 .build(); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 sendNotification() Voice Input Create pending intent
  28. 28. // Feedback intent
 Intent replyIntent = new Intent(context, DummyActivity.class);
 PendingIntent replyPendingIntent = PendingIntent.getActivity(context, 0, replyIntent, 0);
 
 String replyLabel = context.getResources().getString(R.string.reply_label);
 String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);
 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel(replyLabel)
 .setChoices(cannedResponses)
 .build();
 
 NotificationCompat.Action replyAction =
 new NotificationCompat.Action.Builder( R.drawable.chatbubble_working, replyLabel, replyPendingIntent)
 .addRemoteInput(remoteInput)
 .build(); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 sendNotification() Voice Input Create RemoteInput with canned responses
  29. 29. // Feedback intent
 Intent replyIntent = new Intent(context, DummyActivity.class);
 PendingIntent replyPendingIntent = PendingIntent.getActivity(context, 0, replyIntent, 0);
 
 String replyLabel = context.getResources().getString(R.string.reply_label);
 String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);
 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel(replyLabel)
 .setChoices(cannedResponses)
 .build();
 
 NotificationCompat.Action replyAction =
 new NotificationCompat.Action.Builder( R.drawable.chatbubble_working, replyLabel, replyPendingIntent)
 .addRemoteInput(remoteInput)
 .build(); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 sendNotification() Voice Input Create wearable action
  30. 30. String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);
 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel(replyLabel)
 .setChoices(cannedResponses)
 .build();
 
 NotificationCompat.Action replyAction =
 new NotificationCompat.Action.Builder( R.drawable.chatbubble_working, replyLabel, replyPendingIntent)
 .addRemoteInput(remoteInput)
 .build(); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentText("Please proceed to gate C 17 to board. Have a nice flight!")
 .extend(new NotificationCompat.WearableExtender()
 .addPages(pages)
 .addAction(replyAction))
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification); 
 sendNotification() Voice Input Create notification
  31. 31. String[] cannedResponses = context.getResources().getStringArray(R.array.canned_responses);
 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
 .setLabel(replyLabel)
 .setChoices(cannedResponses)
 .build();
 
 NotificationCompat.Action replyAction =
 new NotificationCompat.Action.Builder( R.drawable.chatbubble_working, replyLabel, replyPendingIntent)
 .addRemoteInput(remoteInput)
 .build(); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentText("Please proceed to gate C 17 to board. Have a nice flight!")
 .extend(new NotificationCompat.WearableExtender()
 .addPages(pages)
 .addAction(replyAction))
 .build();
 
 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
 notificationManager.notify(notificationId++, notification); 
 sendNotification() Voice Input Send notification
  32. 32. Intent intent = getIntent();
 if (intent != null) { Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
 if (remoteInputResults != null) {
 CharSequence utterance = remoteInputResults.getCharSequence(Constants.EXTRA_VOICE_REPLY); 
 Toast.makeText(this, utterance, Toast.LENGTH_LONG).show();
 } } ReceivingActivity.onCreate() Receiving Voice Input
  33. 33. Intent intent = getIntent();
 if (intent != null) { Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
 if (remoteInputResults != null) {
 CharSequence utterance = remoteInputResults.getCharSequence(Constants.EXTRA_VOICE_REPLY); 
 Toast.makeText(this, utterance, Toast.LENGTH_LONG).show();
 } } ReceivingActivity.onCreate() Receiving Voice Input Get remote input
  34. 34. Intent intent = getIntent();
 if (intent != null) { Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
 if (remoteInputResults != null) {
 CharSequence utterance = remoteInputResults.getCharSequence(Constants.EXTRA_VOICE_REPLY); 
 Toast.makeText(this, utterance, Toast.LENGTH_LONG).show();
 } } ReceivingActivity.onCreate() Receiving Voice Input Unpack voice reply
  35. 35. Actions Enhanced Notifications
  36. 36. Intent mapIntent = new Intent(Intent.ACTION_VIEW);
 Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode("London Heathrow"));
 mapIntent.setData(geoUri);
 PendingIntent mapPendingIntent = PendingIntent.getActivity(context, 0, mapIntent, 0); sendNotification() Actions
  37. 37. Intent mapIntent = new Intent(Intent.ACTION_VIEW);
 Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode("London Heathrow"));
 mapIntent.setData(geoUri);
 PendingIntent mapPendingIntent = PendingIntent.getActivity(context, 0, mapIntent, 0);
 
 NotificationCompat.Action walkingDirectionsAction =
 new NotificationCompat.Action.Builder( R.drawable.ic_full_directions_walking, "Directions to gate", mapPendingIntent)
 .build(); sendNotification() Actions
  38. 38. Intent mapIntent = new Intent(Intent.ACTION_VIEW);
 Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode("London Heathrow"));
 mapIntent.setData(geoUri);
 PendingIntent mapPendingIntent = PendingIntent.getActivity(context, 0, mapIntent, 0);
 
 NotificationCompat.Action walkingDirectionsAction =
 new NotificationCompat.Action.Builder( R.drawable.ic_full_directions_walking, "Directions to gate", mapPendingIntent)
 .build(); Notification notification = new NotificationCompat.Builder(context)
 .setSmallIcon(R.drawable.ic_launcher)
 .setSmallIcon(R.drawable.plane)
 .setContentTitle(String.format("Flight AW123 is ready to board", notificationId))
 .setContentText("Please proceed to gate C 17 to board. Have a nice flight!")
 .addAction(walkingDirectionsAction)
 .extend(new NotificationCompat.WearableExtender()
 .addPages(pages)
 .addAction(replyAction)
 .addAction(walkingDirectionsAction))
 .build();
 sendNotification() Actions
  39. 39. Applications
  40. 40. Launching Wearable apps Using app-provided voice actions Using the start menu
  41. 41. <application
 android:icon="@drawable/greenlinelogo"
 android:label="@string/app_name"
 android:theme="@android:style/Theme.DeviceDefault" >
 <activity
 android:name="de.peterfriese.weartravel.MainActivity"
 android:label="@string/app_name_voice" >
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>
 </application>
 AndroidManifest.xml Launching
  42. 42. <application
 android:icon="@drawable/greenlinelogo"
 android:label="@string/app_name"
 android:theme="@android:style/Theme.DeviceDefault" >
 <activity
 android:name="de.peterfriese.weartravel.MainActivity"
 android:label="@string/app_name_voice" >
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>
 </application>
 AndroidManifest.xml Launching Label for voice action
  43. 43. Custom Layouts Wearable Apps
  44. 44. <android.support.wearable.view.BoxInsetLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_height="match_parent"
 android:layout_width="match_parent">
 
 <FrameLayout
 android:id="@+id/frame_layout"
 android:layout_height="match_parent"
 android:layout_width="match_parent"
 app:layout_box="left|bottom|right">
 
 <android.support.wearable.view.WearableListView
 android:id="@+id/checkin_list"
 android:layout_height="match_parent"
 android:layout_width="match_parent">
 </android.support.wearable.view.WearableListView>
 </FrameLayout>
 </android.support.wearable.view.BoxInsetLayout> activity_checkin.xml Layout - List
  45. 45. <android.support.wearable.view.BoxInsetLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_height="match_parent"
 android:layout_width="match_parent">
 
 <FrameLayout
 android:id="@+id/frame_layout"
 android:layout_height="match_parent"
 android:layout_width="match_parent"
 app:layout_box="left|bottom|right">
 
 <android.support.wearable.view.WearableListView
 android:id="@+id/checkin_list"
 android:layout_height="match_parent"
 android:layout_width="match_parent">
 </android.support.wearable.view.WearableListView>
 </FrameLayout>
 </android.support.wearable.view.BoxInsetLayout> activity_checkin.xml Layout - List
  46. 46. <?xml version="1.0" encoding="utf-8"?>
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto">
 
 <android.support.wearable.view.CircledImageView
 android:id="@+id/image"
 android:alpha="0.5"
 android:layout_height="52dp"
 android:layout_marginLeft="16dp"
 android:layout_width="52dp"
 app:circle_border_color="#FFFFFFFF"
 app:circle_border_width="2dp"
 app:circle_color="#00000000"
 />
 
 <TextView
 android:id="@+id/text"
 android:alpha="0.5"
 android:fontFamily="sans-serif-condensed-light"
 android:gravity="center_vertical"
 android:layout_height="52dp"
 android:layout_marginLeft="72dp"
 checkin_listview_item.xml Layout - Item
  47. 47. <?xml version="1.0" encoding="utf-8"?>
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto">
 
 <android.support.wearable.view.CircledImageView
 android:id="@+id/image"
 android:alpha="0.5"
 android:layout_height="52dp"
 android:layout_marginLeft="16dp"
 android:layout_width="52dp"
 app:circle_border_color="#FFFFFFFF"
 app:circle_border_width="2dp"
 app:circle_color="#00000000"
 />
 
 <TextView
 android:id="@+id/text"
 android:alpha="0.5"
 android:fontFamily="sans-serif-condensed-light"
 android:gravity="center_vertical"
 android:layout_height="52dp"
 android:layout_marginLeft="72dp"
 android:layout_marginRight="16dp"
 android:layout_width="wrap_content"
 android:textColor="@color/white"
 android:textSize="14sp"
 />
 </merge> Layout - Item checkin_listview_item.xml
  48. 48. private final class MyItemView extends FrameLayout implements WearableListView.OnCenterProximityListener {
 
 final CircledImageView image;
 final TextView text;
 
 public MyItemView(Context context) {
 super(context);
 View.inflate(context, R.layout.checkin_listview_item, this);
 image = (CircledImageView) findViewById(R.id.image);
 text = (TextView) findViewById(R.id.text);
 }
 
 @Override
 public void onCenterPosition(boolean b) {
 image.animate().scaleX(1f).scaleY(1f).alpha(1);
 text.animate().scaleX(1f).scaleY(1f).alpha(1);
 }
 
 @Override
 public void onNonCenterPosition(boolean b) {
 image.animate().scaleX(0.8f).scaleY(0.8f).alpha(0.6f);
 CheckInActivity.java MyViewItem
  49. 49. private final class MyItemView extends FrameLayout implements WearableListView.OnCenterProximityListener {
 
 final CircledImageView image;
 final TextView text;
 
 public MyItemView(Context context) {
 super(context);
 View.inflate(context, R.layout.checkin_listview_item, this);
 image = (CircledImageView) findViewById(R.id.image);
 text = (TextView) findViewById(R.id.text);
 }
 
 @Override
 public void onCenterPosition(boolean b) {
 image.animate().scaleX(1f).scaleY(1f).alpha(1);
 text.animate().scaleX(1f).scaleY(1f).alpha(1);
 }
 
 @Override
 public void onNonCenterPosition(boolean b) {
 image.animate().scaleX(0.8f).scaleY(0.8f).alpha(0.6f);
 CheckInActivity.java MyViewItem
  50. 50. private final class MyItemView extends FrameLayout implements WearableListView.OnCenterProximityListener {
 
 final CircledImageView image;
 final TextView text;
 
 public MyItemView(Context context) {
 super(context);
 View.inflate(context, R.layout.checkin_listview_item, this);
 image = (CircledImageView) findViewById(R.id.image);
 text = (TextView) findViewById(R.id.text);
 }
 
 @Override
 public void onCenterPosition(boolean b) {
 image.animate().scaleX(1f).scaleY(1f).alpha(1);
 text.animate().scaleX(1f).scaleY(1f).alpha(1);
 }
 
 @Override
 public void onNonCenterPosition(boolean b) {
 image.animate().scaleX(0.8f).scaleY(0.8f).alpha(0.6f);
 text.animate().scaleX(0.8f).scaleY(0.8f).alpha(0.6f);
 }
 } CheckInActivity.java MyViewItem
  51. 51. public class CheckInActivity extends Activity implements WearableListView.ClickListener {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_checkin);
 
 MyListAdapter adapter = new MyListAdapter();
 
 WearableListView listView = (WearableListView) findViewById(R.id.checkin_list);
 listView.setAdapter(adapter);
 listView.setClickListener(CheckInActivity.this);
 }
 
 @Override
 public void onClick(WearableListView.ViewHolder viewHolder) {
 Toast.makeText(this, String.format("You selected item #%s", viewHolder.getPosition()), Toast.LENGTH_SHORT).show();
 }
 
 @Override
 public void onTopEmptyRegionClick() {
 CheckInActivity.java Activity
  52. 52. public class CheckInActivity extends Activity implements WearableListView.ClickListener {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_checkin);
 
 MyListAdapter adapter = new MyListAdapter();
 
 WearableListView listView = (WearableListView) findViewById(R.id.checkin_list);
 listView.setAdapter(adapter);
 listView.setClickListener(CheckInActivity.this);
 }
 
 @Override
 public void onClick(WearableListView.ViewHolder viewHolder) {
 Toast.makeText(this, String.format("You selected item #%s", viewHolder.getPosition()), Toast.LENGTH_SHORT).show();
 }
 
 @Override
 public void onTopEmptyRegionClick() {
 Toast.makeText(this, "You tapped into the empty area above the list", Toast.LENGTH_SHORT).show();
 }
 CheckInActivity.java Activity
  53. 53. Watch Faces
  54. 54. Design Principles
  55. 55. • Square vs round Design Principles
  56. 56. • Square vs round • Ambient mode / low-bit Design Principles
  57. 57. • Square vs round • Ambient mode / low-bit • Legibility Design Principles 56° - Cloudy 56° - Cloudy
  58. 58. Developing Watch Faces
  59. 59. • Start using a sample Developing Watch Faces
  60. 60. • Start using a sample • Add a new wearable module Developing Watch Faces
  61. 61. Architecture Wearable App Mobile App CanvasWatchFaceService ConfigActivity Engine ConfigActivityWearableListenerService
  62. 62. Architecture Wearable App CanvasWatchFaceService ConfigActivity Engine WearableListenerService
  63. 63. Engine onCreate onPropertiesChanged onDraw onTimeTick onAmbientModeChanged onVisibilityChanged
  64. 64. @Override
 public void onCreate(SurfaceHolder holder) {
 super.onCreate(holder);
 
 setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this)
 .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
 .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
 .setShowSystemUiTime(false)
 .build());
 
 Resources resources = AnalogWatchFaceService.this.getResources();
 Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg);
 mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
 
 mHourPaint = new Paint();
 mHourPaint.setARGB(255, 200, 200, 200);
 mHourPaint.setStrokeWidth(5.f);
 mHourPaint.setAntiAlias(true);
 mHourPaint.setStrokeCap(Paint.Cap.ROUND); onCreate Lifecycle - initialise watch face elements
  65. 65. @Override
 public void onCreate(SurfaceHolder holder) {
 super.onCreate(holder);
 
 setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this)
 .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
 .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
 .setShowSystemUiTime(false)
 .build());
 
 Resources resources = AnalogWatchFaceService.this.getResources();
 Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg);
 mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
 
 mHourPaint = new Paint();
 mHourPaint.setARGB(255, 200, 200, 200);
 mHourPaint.setStrokeWidth(5.f);
 mHourPaint.setAntiAlias(true);
 mHourPaint.setStrokeCap(Paint.Cap.ROUND); onCreate Lifecycle - initialise watch face elements Single-line peek card Do not show system time Show background briefly for interruptive cards
  66. 66. @Override
 public void onCreate(SurfaceHolder holder) {
 super.onCreate(holder);
 
 setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this)
 .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
 .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
 .setShowSystemUiTime(false)
 .build());
 
 Resources resources = AnalogWatchFaceService.this.getResources();
 Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg);
 mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
 
 mHourPaint = new Paint();
 mHourPaint.setARGB(255, 200, 200, 200);
 mHourPaint.setStrokeWidth(5.f);
 mHourPaint.setAntiAlias(true);
 mHourPaint.setStrokeCap(Paint.Cap.ROUND); onCreate Lifecycle - initialise watch face elements Load background image
  67. 67. @Override
 public void onCreate(SurfaceHolder holder) {
 super.onCreate(holder);
 
 setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this)
 .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
 .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
 .setShowSystemUiTime(false)
 .build());
 
 Resources resources = AnalogWatchFaceService.this.getResources();
 Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg);
 mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
 
 mHourPaint = new Paint();
 mHourPaint.setARGB(255, 200, 200, 200);
 mHourPaint.setStrokeWidth(5.f);
 mHourPaint.setAntiAlias(true);
 mHourPaint.setStrokeCap(Paint.Cap.ROUND); onCreate Lifecycle - initialise watch face elements Create styles for graphic objects
  68. 68. @Override
 public void onDraw(Canvas canvas, Rect bounds) {
 mTime.setToNow();
 
 canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null); float centerX = width / 2f;
 float centerY = height / 2f;
 
 float innerTickRadius = centerX - 10;
 float outerTickRadius = centerX;
 for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
 float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
 float innerX = (float) Math.sin(tickRot) * innerTickRadius;
 float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
 float outerX = (float) Math.sin(tickRot) * outerTickRadius;
 float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
 canvas.drawLine(centerX + innerX, centerY + innerY,
 centerX + outerX, centerY + outerY, mTickPaint);
 }
 
 float secRot = mTime.second / 30f * (float) Math.PI;
 onDraw Lifecycle - draw the watch face
  69. 69. @Override
 public void onDraw(Canvas canvas, Rect bounds) {
 mTime.setToNow();
 
 canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null); float centerX = width / 2f;
 float centerY = height / 2f;
 
 float innerTickRadius = centerX - 10;
 float outerTickRadius = centerX;
 for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
 float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
 float innerX = (float) Math.sin(tickRot) * innerTickRadius;
 float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
 float outerX = (float) Math.sin(tickRot) * outerTickRadius;
 float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
 canvas.drawLine(centerX + innerX, centerY + innerY,
 centerX + outerX, centerY + outerY, mTickPaint);
 }
 
 float secRot = mTime.second / 30f * (float) Math.PI;
 onDraw Lifecycle - draw the watch face Update time Draw background image
  70. 70. @Override
 public void onDraw(Canvas canvas, Rect bounds) {
 mTime.setToNow();
 
 canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null); float centerX = width / 2f;
 float centerY = height / 2f;
 
 float innerTickRadius = centerX - 10;
 float outerTickRadius = centerX;
 for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
 float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
 float innerX = (float) Math.sin(tickRot) * innerTickRadius;
 float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
 float outerX = (float) Math.sin(tickRot) * outerTickRadius;
 float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
 canvas.drawLine(centerX + innerX, centerY + innerY,
 centerX + outerX, centerY + outerY, mTickPaint);
 }
 
 float secRot = mTime.second / 30f * (float) Math.PI;
 onDraw Lifecycle - draw the watch face Draw the ticks
  71. 71. float centerX = width / 2f;
 float centerY = height / 2f;
 
 float innerTickRadius = centerX - 10;
 float outerTickRadius = centerX;
 for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
 float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
 float innerX = (float) Math.sin(tickRot) * innerTickRadius;
 float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
 float outerX = (float) Math.sin(tickRot) * outerTickRadius;
 float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
 canvas.drawLine(centerX + innerX, centerY + innerY,
 centerX + outerX, centerY + outerY, mTickPaint);
 }
 
 float secRot = mTime.second / 30f * (float) Math.PI;
 int minutes = mTime.minute;
 float minRot = minutes / 30f * (float) Math.PI;
 float hrRot = ((mTime.hour + (minutes / 60f)) / 6f ) * (float) Math.PI;
 
 float secLength = centerX - 20;
 float minLength = centerX - 40;
 float hrLength = centerX - 80;
 if (!isInAmbientMode()) {
 float secX = (float) Math.sin(secRot) * secLength;
 float secY = (float) -Math.cos(secRot) * secLength;
 canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mSecondPaint);
 }
 
 onDraw Lifecycle - draw the watch face Determine hands geometry
  72. 72. float outerX = (float) Math.sin(tickRot) * outerTickRadius;
 float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
 canvas.drawLine(centerX + innerX, centerY + innerY,
 centerX + outerX, centerY + outerY, mTickPaint);
 }
 
 float secRot = mTime.second / 30f * (float) Math.PI;
 int minutes = mTime.minute;
 float minRot = minutes / 30f * (float) Math.PI;
 float hrRot = ((mTime.hour + (minutes / 60f)) / 6f ) * (float) Math.PI;
 
 float secLength = centerX - 20;
 float minLength = centerX - 40;
 float hrLength = centerX - 80;
 if (!isInAmbientMode()) {
 float secX = (float) Math.sin(secRot) * secLength;
 float secY = (float) -Math.cos(secRot) * secLength;
 canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mSecondPaint);
 }
 
 float minX = (float) Math.sin(minRot) * minLength;
 float minY = (float) -Math.cos(minRot) * minLength;
 canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY, mMinutePaint);
 
 float hrX = (float) Math.sin(hrRot) * hrLength;
 float hrY = (float) -Math.cos(hrRot) * hrLength;
 canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY, mHourPaint);
 } onDraw Lifecycle - draw the watch face Don’t draw seconds when in ambient mode Draw minutes hand Draw hours hand
  73. 73. Watch Faces
  74. 74. Demo
  75. 75. +PeterFriese @ Thank you! #AndroidWear W e’re hiring!
  76. 76. +PeterFriese @ Q & A #AndroidWear W e’re hiring!
  77. 77. W e’re hiring!

×