Android On Your Sleeve - DroidCon Montreal 2015

55
DroidCon Montréal 2015 ANDROID ON YOUR SLEEVE 1

Transcript of Android On Your Sleeve - DroidCon Montreal 2015

Page 1: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

ANDROID ON YOUR SLEEVE

1

Page 2: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

2

Building your Super Wearable App

• You've got an idea for the next super app?• But don't know where to start with Android Wear

development?• Let's spend some time stretching those Android Wear

development muscles.

Page 3: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

3

What is Android Wear?

• An Android platform for your wrist that can run native apps.• A stream of cards that you can swipe through and interact with.

• These are notifications delivered from your phone, or displayed directly by apps on the watch.

• Sensors like step counters, barometer, heart-rate sensor, and accelerometer.• Customizable watch faces, and the ability to design your own faces.• A communication channel between your watch and your phone.• A set of custom components to make application development for the watch

easier.

Page 4: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

4

Types of Android Wear Applications

• Extended Notifications in Phone Apps• Notifications appear in the watch stream and can be customized with

background images, pages, and layout modifications.• Phone App with Embedded Wear App

• More powerful and flexible: a full, native, Android app is delivered to the Wear device which can take full advantage of the platform.

Page 5: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

5

First Wear Wonder: Enhanced Notifications

• Notifications can be extended for wear using the Android Support Library and the NotificationCompat.Builder class.

• The NotificationCompat.WearableExtender class provides properties that Android Wear uses to change the appearance and behaviour of the notifications on the watch.

Page 6: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

6

Extending Notifications: Step One

dependencies { compile 'com.android.support:support-v4:22.0.0'}

Add the Android Support library to your dependencies in your App's build.gradle file.

Page 7: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

7

Extending Notifications: Step Two

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("My notification") .setContentText("When in danger,\nWhen in doubt,\nRun in circles,\nScream and shout!"); int mNotificationId = 1; NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mNotifyMgr.notify(mNotificationId, mBuilder.build());

Modify your notification building code to extend it for Android Wear.

Page 8: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

int mNotificationId = 1; NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mNotifyMgr.notify(mNotificationId, mBuilder.build());

8

Extending Notifications: Step TwoNotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("My notification") .setContentText("When in danger,\nWhen in doubt,\nRun in circles,\nScream and shout!");

Page 9: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

int mNotificationId = 1; NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mNotifyMgr.notify(mNotificationId, mBuilder.build());

8

Extending Notifications: Step TwoNotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("My notification") .setContentText("When in danger,\nWhen in doubt,\nRun in circles,\nScream and shout!");

NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender() .setGravity(Gravity.TOP) .setHintScreenTimeout(NotificationCompat.WearableExtender.SCREEN_TIMEOUT_LONG);

mBuilder.extend(wearableExtender);

Page 10: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

9

Extending Notifications: Wearable Only Actions

• Adding wearable-only actions can be done by adding one or more actions to the wearable extender.

// Create an intent for the actionIntent actionIntent = new Intent(this, LoveItActivity.class); PendingIntent actionPendingIntent = PendingIntent.getActivity(this, 0, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT);

// Create the actionNotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_love, getString(R.string.ic_love_label), actionPendingIntent) .build();

NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender() .setGravity(Gravity.TOP)

.setHintScreenTimeout(NotificationCompat.WearableExtender.SCREEN_TIMEOUT_LONG); .addAction(action)

Page 11: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

10

Extending Notifications: Adding Pages

• The WearableExtender provides the ability to add pages to a notification that can be swiped through.

wearableExtender.addPage( new NotificationCompat.Builder(this) .extend(new NotificationCompat.WearableExtender() .setBackground(BitmapFactory.decodeResource(getResources(), R.drawable.punk)) .setHintAvoidBackgroundClipping(true) .setHintShowBackgroundOnly(true)) .build());

Source code for this sample app can be found at: http://goo.gl/Kxucib

Page 12: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

11

Extending Notifications: Fake Demo

• In lieu of super demo hardware, you instead receive a fake demo.• The notification is sent, and on the Watch, the following pages are displayed.

Page 13: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

12

Native App Development

• A native Android Wear app will always be bundled with an app that's installed from Google Play on the Phone.

• Android Studio has templates to get you started.• The Google Play Services Wearable library bridges the

gap between the phone and the watch.

Page 14: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

13

Wear Development Ideas

• Wear Apps can fire their own Notifications. Those notifications will notbe shown on the phone.

• It can be very helpful to share a bit of code between a Wear app and it's Mobile App counterpart through a shared Android Library.

• The Google Play Services Wearable library provides data synchronization and message passing, but only passes byte arrays. Libraries can make this much easier, such as the Courier library by Denley Bihari, which will be discussed later.

• A watch isn't just for watch faces although you can make those as well.

Page 15: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

14

Getting Started With Wear Development

• Android Studio provides a Wear module template. This can be added into your app, and with a little Gradle Glue, you've added a wear extension to your app.

Page 16: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

15

Finishing the Template Wizard

• Choosing the Display Notification activity leads to some customization.

• Hitting finish, creates a project with two modules: mobile, and wear.

• The wear module contains two activities and a broadcast receiver.

MyStubBroadcastActivity

MyPostNotificationReceiver

MyDisplayActivity

Creates NotificationBroadcasts Intent

Page 17: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

16

Running the Wear App During Development

• You're going to want to run the app, and debug it, during development.• If you have a device with a charging dock that doubles as a USB bridge,

you can develop rapidly with the device on the dock.• Otherwise you'll need to use Debugging over Bluetooth or make your own

cable.

Page 18: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

17

Enabling Debugging over Bluetooth

• First, on your Wear device you'll have to enable developer settings by tapping on the Settings > About > Build Number a few times.

• Then in Settings > Developer Settings enable ADB debugging and Debug over Bluetooth.

Page 19: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

18

Enabling Debugging over Bluetooth• Next, in the Android Wear app on your

phone, open settings and turn on the Debugging over Bluetooth

• Initially you will likely see host is disconnected, so you will need to run some ADB commands:• adb forward tcp:4444 localabstract:/adb-hub • adb connect localhost:4444

Page 20: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

19

The Rest You Already Know• Your Android Wear device will show up

in Android Studio as another Android device you can deploy to, and debug on.

• Transfer to the device over Bluetooth takes quite a while, but it is conveniently devoid of wires.

• Choose the wear module from the configurations chooser, hit the run button, and the app will run on your watch.

Page 21: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

19

The Rest You Already Know• Your Android Wear device will show up

in Android Studio as another Android device you can deploy to, and debug on.

• Transfer to the device over Bluetooth takes quite a while, but it is conveniently devoid of wires.

• Choose the wear module from the configurations chooser, hit the run button, and the app will run on your watch.

Page 22: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

20

Gradle Glue and Matryoshka Apps

• The Wear app's dependencies in build.gradle look like this:dependencies { compile 'com.google.android.support:wearable:1.1.0' compile 'com.google.android.gms:play-services-wearable:6.5.87'}

• The Mobile app's dependencieslook like this:dependencies { wearApp project(':wear') compile 'com.google.android.gms:play-services:+'}

• Notice the special wearApp project statement. This is how the wear appis embedded into the mobile app. Apps inside apps. Like Russian Dolls.

• Only a signed, release build, will automatically deploy the wear app to awatch. Debug builds do not auto deploy their wear apps.

Page 23: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

21

Mobile and Wear app Symbiosis

• A Wear app is not very interesting without a Mobile app partnered with it.• The Mobile app and Wear app share data, and can send messages to each

other.• Let's quickly go through the Android Wear API for sharing data and sending

messages.

Page 24: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

22

Sharing Data and Sending Messages

• It all starts with the GoogleApiClient and the Wearable.API.// In onCreate GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { @Override public void onConnected(Bundle connectionHint) { Log.d(TAG, "onConnected: " + connectionHint); // Now you can use the Data Layer API } @Override public void onConnectionSuspended(int cause) { Log.d(TAG, "onConnectionSuspended: " + cause); } }) .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() { @Override public void onConnectionFailed(ConnectionResult result) { Log.d(TAG, "onConnectionFailed: " + result); } }) // Request access only to the Wearable API .addApi(Wearable.API) .build();

// In onResumemGoogleApiClient.connect();

Page 25: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

23

Syncing Data Items

• A Data Item has a Path and a Payload• A Path is a unique string that starts with a forward slash. Don't use

underscores.• A Payload is a byte array limited to 100kb. You must serialize the data

yourself. Flexible, but requires some assembly.

Page 26: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

24

Syncing Data Items

• Google recommends using the DataMap class instead. It allows data tobe accessed as key-value pairs. It's used as follows:

Page 27: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

25

Receiving Data Changes and Messages• For background receipt of Data Item changes, implement a subclass of

WearableListenerService and override some of the methods.• onMessageReceived - for listening for messages.• onDataChanged - for detecting Data Item changes.• onPeerConnected• onPeerDisconnected

• For Foreground receipt of messages and Data Item changes, you can implement the DataListener and MessageListener interfaces and register with either the DataApi, or the MessageApi.

Page 28: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

26

An Even Better Way

• Use the Courier library written by Denley Bihari!• It wraps up Wearable.DataApi and Wearable.MessageApi into a simple and clean

delivery service for Android Wear.• Provides an Annotation processor that simplifies serializing objects, and sending

them as either Data Items or Messages.

Page 29: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

27

Courier

• Imagine you want to deliver a message from your phone to a Wear Device

public class Message { public String mSender; public String mMessageText; public long mTimeStamp; public Message() { } public Message(String sender, String messageText, long timeStamp) { mSender = sender; mMessageText = messageText; mTimeStamp = timeStamp; } }

import me.denley.courier.Deliverable; @Deliverable

Page 30: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

28

Courier• Then in the Activity you want to send from, register with Courier to do the

work:public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Courier.startReceiving(this); } @Override protected void onDestroy() { Courier.stopReceiving(this); super.onDestroy(); } public void onSaySomethingButtonClicked(View button) { Courier.deliverMessage(this, "/message", "From Mobile!"); Message message = new Message("Me", getRandomPhrase(), System.currentTimeMillis()); Courier.deliverData(this, "/message/data", message); } }

Page 31: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

29

Receiving Is Just as Easy

• To Receive data, the code is remarkably similar :public class ContentActivity extends Activity { Message mMessage; @Override protected void onCreate(Bundle savedInstanceState) { Courier.startReceiving(this); } @Override protected void onDestroy() { Courier.stopReceiving(this); super.onDestroy(); } private void updateMessage() { mTextView.setText(mMessage.mMessageText); } @ReceiveData("/message/data") void onMessageChanged(Message message) { mMessage = message; updateMessage(); } }

Page 32: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

30

Courier's Packager• Receiving messages using a WearableListenerService is possible with Courier's

Packager class doing the deserialization. Handles Serializable and classes marked with Deliverable annotations.

public class ListenerService extends WearableListenerService { @Override public void onMessageReceived(MessageEvent messageEvent) { if (messageEvent.getPath().equals("/message")) { String message = Packager.unpack(messageEvent.getData(), String.class); Intent i = new Intent(); i.setAction("org.nsdev.wearableapp.SHOW_NOTIFICATION"); i.putExtra(MyPostNotificationReceiver.CONTENT_KEY, message); sendBroadcast(i); } super.onMessageReceived(messageEvent); } }

Page 33: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

31

Take a Look At Some Code

• While developing this section of the talk, I wrote some code that demonstrates the details, and the steps of arriving at the final result can be seen in the commits.

• Check it out at http://goo.gl/mQsFA1• Check out the Courier library at: http://goo.gl/qAI4wu• Thanks to Denley Bihari for permission to talk about his project.

Page 34: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

32

Wear UI Classes for Your Toolbelt

• You have a bit of an idea of the mechanics of writing wear applications, and how to get data to and from your wear device now, let's look at the UI Toolkit.

• Wear UI classes are designed to be easy to use without a lot of fine touch control.

• The use of cards and swipe gestures is encouraged.• Applications should be composed of the following

simple building blocks.

Page 35: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

33

Wear UI Patterns

Page 36: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

33

Wear UI Patterns

Page 37: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

33

Wear UI Patterns

• Cards - CardFragment, CardFrame, CardScrollView

Page 38: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

33

Wear UI Patterns

• Cards - CardFragment, CardFrame, CardScrollView

• Selection Lists - WearableListView

Page 39: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

33

Wear UI Patterns

• Cards - CardFragment, CardFrame, CardScrollView

• Selection Lists - WearableListView• 2D Pickers - GridViewPager

Page 40: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

33

Wear UI Patterns

• Cards - CardFragment, CardFrame, CardScrollView

• Selection Lists - WearableListView• 2D Pickers - GridViewPager• Countdown Pickers -

DelayedConfirmationView

Page 41: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

33

Wear UI Patterns

• Cards - CardFragment, CardFrame, CardScrollView

• Selection Lists - WearableListView• 2D Pickers - GridViewPager• Countdown Pickers -

DelayedConfirmationView• Long Press to Dismiss -

DismissOverlayView

Page 42: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

34

Round, Square and Butterknife

• You will be needing to use the WatchViewStub to make layouts that work both in square and round devices.

• The appropriate layout will be chosen automatically.• Let's go through the layout files and show an example of binding a WatchViewStub

to layouts.

Page 43: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

35

Round, Square and Butterknife

<?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" android:id="@+id/watch_view_stub" android:layout_width="match_parent" android:layout_height="match_parent" app:rectLayout="@layout/rect_activity_content" app:roundLayout="@layout/round_activity_content" tools:context="org.nsdev.wearableapp.ContentActivity" tools:deviceIds="wear"> </android.support.wearable.view.WatchViewStub>

activity_content.xml

Page 44: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

36

Round, Square and Butterkniferect_activity_content.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="org.nsdev.wearableapp.ContentActivity" tools:deviceIds="wear_square"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_square"/> <android.support.wearable.view.DismissOverlayView android:id="@+id/dismiss_overlay" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>

Page 45: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

37

Round, Square and Butterkniferound_activity_content.xml

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="org.nsdev.wearableapp.ContentActivity" tools:deviceIds="wear_round"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:textAppearance="@style/TextAppearance.Wearable.Large" android:textColor="#00F" android:text="@string/hello_round"/> <android.support.wearable.view.DismissOverlayView android:id="@+id/dismiss_overlay" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>

Page 46: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

38

Round, Square and ButterknifeContentActivity.java

public class ContentActivity extends Activity { Message mMessage; @InjectView(R.id.watch_view_stub) WatchViewStub mStub; @Optional @InjectView(value = R.id.text) TextView mTextView; @Optional @InjectView(R.id.dismiss_overlay) DismissOverlayView mDismissOverlayView; private GestureDetector mDetector;

Page 47: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

39

Round, Square and ButterknifeContentActivity.java

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_content); ButterKnife.inject(this); mStub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() { @Override public void onLayoutInflated(WatchViewStub stub) { ButterKnife.inject(ContentActivity.this, mStub); mDismissOverlayView.setIntroText(R.string.dismiss_intro_text); mDismissOverlayView.showIntroIfNecessary(); mDetector = new GestureDetector(ContentActivity.this, new GestureDetector.SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent e) { mDismissOverlayView.show(); } }); } }); }

Page 48: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

40

Round, Square and ButterknifeContentActivity.java

@Override public boolean onTouchEvent(MotionEvent event) { return mDetector.onTouchEvent(event) || super.onTouchEvent(event); }

• Let's do something a little interesting with all of this...• We'll detect a couple more gestures.

Page 49: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

41

Adding Interesting GesturesContentActivity.javamDetector = new GestureDetector(ContentActivity.this, new GestureDetector.SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent e) { mDismissOverlayView.show(); }X });

Page 50: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

42

Adding Interesting GesturesContentActivity.javamDetector = new GestureDetector(ContentActivity.this, new GestureDetector.SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent e) { mDismissOverlayView.show(); }X });

Page 51: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

43

Adding Interesting GesturesContentActivity.javamDetector = new GestureDetector(ContentActivity.this, new GestureDetector.SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent e) { mDismissOverlayView.show(); }X @Override public boolean onSingleTapConfirmed(MotionEvent e) { Courier.deliverMessage(ContentActivity.this, "/keynote/command", "next"); return true; } @Override public boolean onDoubleTap(MotionEvent e) { Courier.deliverMessage(ContentActivity.this, "/keynote/command", "previous"); return true; } });

Page 52: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

44

Mobile App ModificationsMainActivity.java

@ReceiveMessages("/keynote/command") public void onKeynoteMessage(String message) { try { runCommand(message); } catch (Exception e) { e.printStackTrace(); } } private final OkHttpClient client = new OkHttpClient(); public void runCommand(final String command) throws Exception { Request request = new Request.Builder() .get() .url(String.format("http://%s/json/%s", mServerHost.getText().toString(), command)) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { e.printStackTrace(); }

Page 53: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

45

Mobile App ModificationsMainActivity.java

@Override public void onResponse(Response response) throws IOException { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); try { JSONObject responseJson = new JSONObject(response.body().string()); String slide = responseJson.optString("slide"); String build = responseJson.optString("build"); Message message = new Message("Me", "Slide: " + slide + "\nBuild: " + build, System.currentTimeMillis()); Courier.deliverData(MainActivity.this, "/message/data", message); } catch (JSONException e) { e.printStackTrace(); } } }); }

Page 54: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

46

Notable Wear Libraries and Tools

• By Denley Bihari: Courier, WearPreferenceActivity, and WearPrefs

• Jimu Labs Mirror and Sandbox - jimulabs.com • Everything Square Does, ever! • Everything Jake Wharton Does, ever!

Page 55: Android On Your Sleeve - DroidCon Montreal 2015

DroidCon Montréal2015

CONFIDENTIAL INFORMATION - NOT FOR DISTRIBUTION.

THANK YOU :)

47

[email protected]@thorinside

Art: Mary Sanche, @thoughtsupnorth