Google Fit, Android Wear & Xamarin

65
Google Fit, Wear & Xamarin Peter Friese @peterfriese Matt Sullivan @mjohnsullivan

Transcript of Google Fit, Android Wear & Xamarin

Page 1: Google Fit, Android Wear & Xamarin

Google Fit, Wear & XamarinPeter Friese @peterfriese Matt Sullivan @mjohnsullivan

Page 2: Google Fit, Android Wear & Xamarin
Page 3: Google Fit, Android Wear & Xamarin
Page 4: Google Fit, Android Wear & Xamarin
Page 5: Google Fit, Android Wear & Xamarin

Code Lab

Page 6: Google Fit, Android Wear & Xamarin

Food for Thought

Page 7: Google Fit, Android Wear & Xamarin

Are Fitness apps just for runners, cyclists, athletes?

Or for those on a mission to get fit and stay healthy?

Page 8: Google Fit, Android Wear & Xamarin

Can we add elements of fitness to everyday apps?

Page 9: Google Fit, Android Wear & Xamarin
Page 10: Google Fit, Android Wear & Xamarin

http://handy-games.com/games/kitty-my-fitness-cat/

Page 11: Google Fit, Android Wear & Xamarin
Page 12: Google Fit, Android Wear & Xamarin
Page 13: Google Fit, Android Wear & Xamarin
Page 14: Google Fit, Android Wear & Xamarin

Positive impact on physical and emotional wellbeing

== positive impact on user

experience and happiness

Page 15: Google Fit, Android Wear & Xamarin

Android Wear

Page 16: Google Fit, Android Wear & Xamarin

Create a Wear app module

Design the watch face UI

Extend WatchFaceService.Engine

Create a companion app

1

2

3

4

Page 17: Google Fit, Android Wear & Xamarin

Watch Face Architecture

Wearable App

CanvasWatchFaceService

Engine

WearableConfigActivity

WearableListenerService

Mobile App

MobileConfigActivity

Page 18: Google Fit, Android Wear & Xamarin

Design for square and round

Interactive and Ambient Modes

Gestures

Page 19: Google Fit, Android Wear & Xamarin

CanvasWatchFaceService.Engine

UI DrawingManually draw onto a Canvas Object

Ambient Mode ChangesReduced color spaceBurn protection

Redraw Intervals

Once a minute in Ambient – OnTimeTick()

Custom for interactive – use a System Timer

TimeZone ChangesUse a BroadcastReceiver

Interactive Watch Faces.SetAcceptsTapEvents() in BuilderOverride OnTapCommand()

Page 20: Google Fit, Android Wear & Xamarin

Drawing the watch face

Background

Hour TicksStep Count

Minutes Hand

Seconds Hand

Hours Hand

Page 21: Google Fit, Android Wear & Xamarin

backgroundScaledBitmap = Bitmap.CreateScaledBitmap(backgroundBitmap, width, height, true); canvas.DrawBitmap(backgroundScaledBitmap, 0, 0, null);

Drawing the Watch Face - Background

Page 22: Google Fit, Android Wear & Xamarin
Page 23: Google Fit, Android Wear & Xamarin

var innerTickRadius = centerX - 10; var outerTickRadius = centerX; for (var tickIndex = 0; tickIndex < 12; tickIndex++) { var tickRot = (float)(tickIndex * Math.PI * 2 / 12); var innerX = (float)Math.Sin(tickRot) * innerTickRadius; var innerY = (float)-Math.Cos(tickRot) * innerTickRadius; var outerX = (float)Math.Sin(tickRot) * outerTickRadius; var outerY = (float)-Math.Cos(tickRot) * outerTickRadius; canvas.DrawLine(centerX + innerX, centerY + innerY, centerX + outerX, centerY + outerY, tickPaint);}

Drawing the Watch Face - Ticks

Page 24: Google Fit, Android Wear & Xamarin

var width = bounds.Width(); var height = bounds.Height(); var steps = stepCount.ToString(); var stepsWidth = stepcountPaint.MeasureText(steps);var x = width - stepsWidth - stepCountOffset; var y = (height + stepCountTextSize) / 2.0f; canvas.DrawText(steps, x, y, stepcountPaint);

Drawing the Watch Face - Step Count

Page 25: Google Fit, Android Wear & Xamarin

Ambient mode

Reduce color depth Reduce number of non-black pixels Use color for < 5% pixelsWatch face updates once a minute - don’t show seconds

Save power

Interactive Ambient

Page 26: Google Fit, Android Wear & Xamarin

public override void OnDraw(Canvas canvas, Rect bounds){ DrawFace(canvas, bounds); DrawHands(canvas, bounds); } void DrawHands(Canvas canvas, Rect bounds){ if (!IsInAmbientMode) { canvas.DrawLine(centerX, centerY, centerX + secX, centerY + secY, secondPaint); } canvas.DrawLine(centerX, centerY, centerX + minX, centerY + minY, minutePaint);}

Ambient Mode

Page 27: Google Fit, Android Wear & Xamarin

public override void OnDraw(Canvas canvas, Rect bounds){ DrawFace(canvas, bounds); DrawHands(canvas, bounds); } void DrawHands(Canvas canvas, Rect bounds){ if (!IsInAmbientMode) { canvas.DrawLine(centerX, centerY, centerX + secX, centerY + secY, secondPaint); } canvas.DrawLine(centerX, centerY, centerX + minX, centerY + minY, minutePaint);}

Ambient Mode

No seconds hand in ambient mode

Page 28: Google Fit, Android Wear & Xamarin

void UpdateTimer() { if (timerSeconds == null) { timerSeconds = new System.Threading.Timer( state => { Invalidate(); }, null, dueTime, period } else { if (IsVisible && !IsInAmbientMode) { timerSeconds.Change(0, InteractiveUpdateRateMs); } else { timerSeconds.Change(System.Threading.Timeout.Infinite, 0); } } }

Ambient Mode / 2

Page 29: Google Fit, Android Wear & Xamarin

void UpdateTimer() { if (timerSeconds == null) { timerSeconds = new System.Threading.Timer( state => { Invalidate(); }, null, dueTime, period } else { if (IsVisible && !IsInAmbientMode) { timerSeconds.Change(0, InteractiveUpdateRateMs); } else { timerSeconds.Change(System.Threading.Timeout.Infinite, 0); } } }

Ambient Mode / 2

Redraw every second when in interactive mode

Page 30: Google Fit, Android Wear & Xamarin

void UpdateTimer() { if (timerSeconds == null) { timerSeconds = new System.Threading.Timer( state => { Invalidate(); }, null, dueTime, period } else { if (IsVisible && !IsInAmbientMode) { timerSeconds.Change(0, InteractiveUpdateRateMs); } else { timerSeconds.Change(System.Threading.Timeout.Infinite, 0); } } }

Ambient Mode / 2

Turn timer on in interactive mode off in ambient mode

Page 31: Google Fit, Android Wear & Xamarin

Configuration Activities

WEARABLE_CONFIGURATION on Wear

COMPANION_CONFIGURATION on Phone

DataLayer APIListen for Data Item EventsWrite DataItems to URI Paths

Page 32: Google Fit, Android Wear & Xamarin

void SaveStepCountStatus(GoogleApiClient googleApiClient, bool stepCountOn) { var putDataMapRequest = PutDataMapRequest.Create("/xfit_watchface"); putDataMapRequest.DataMap.PutBoolean("stepcount", stepCountOn); var putDataRequest = putDataMapRequest.AsPutDataRequest(); putDataRequest.SetUrgent(); WearableClass.DataApi.PutDataItem(googleApiClient, putDataRequest); }

Syncing configuration /1

Page 33: Google Fit, Android Wear & Xamarin

void SaveStepCountStatus(GoogleApiClient googleApiClient, bool stepCountOn) { var putDataMapRequest = PutDataMapRequest.Create("/xfit_watchface"); putDataMapRequest.DataMap.PutBoolean("stepcount", stepCountOn); var putDataRequest = putDataMapRequest.AsPutDataRequest(); putDataRequest.SetUrgent(); WearableClass.DataApi.PutDataItem(googleApiClient, putDataRequest); }

Syncing configuration /1

URI PathData Item KeySync now

Page 34: Google Fit, Android Wear & Xamarin

void OnDataChanged(DataEventBuffer dataEvents) { foreach (var dataEvent in dataEvents) { var dataItem = dataEvent.DataItem; if (dataItem.Uri.Path == "/xfit_watchface") { var dataMap = DataMapItem.FromDataItem(dataItem).DataMap; displayStepCount = dataMap.GetBoolean(“stepcount”); Invalidate(); // force redraw } } }

Syncing configuration /2

Page 35: Google Fit, Android Wear & Xamarin

void OnDataChanged(DataEventBuffer dataEvents) { foreach (var dataEvent in dataEvents) { var dataItem = dataEvent.DataItem; if (dataItem.Uri.Path == "/xfit_watchface") { var dataMap = DataMapItem.FromDataItem(dataItem).DataMap; displayStepCount = dataMap.GetBoolean(“stepcount”); Invalidate(); // force redraw } } }

Implements IDataApiDataListener

Syncing configuration /2

URI Path

Data Item Key

Page 36: Google Fit, Android Wear & Xamarin

Watch face drawing code

GoogleAPIClient code

Configuration code

Reading the step count code

1

2

3

4

Page 37: Google Fit, Android Wear & Xamarin

Clean Code

Page 38: Google Fit, Android Wear & Xamarin

Partial Classes

XFitWatchfaceService

XFitWatchFaceEngine (Drawing Code)

XFitWatchFaceEngine (Config Code)

XFitWatchFaceEngine (Google API Client Code)

XFitWatchFaceEngine (Step Count Code)

XFitWatchFaceEngine

Page 39: Google Fit, Android Wear & Xamarin

Partial Classes

XFitWatchfaceService

XFitWatchFaceEngine (Drawing Code)

XFitWatchFaceEngine (Config Code)

XFitWatchFaceEngine (Google API Client Code)

XFitWatchFaceEngine (Step Count Code)

XFitWatchFaceEngine

[Service(Label = "XFit Watchface 4", Permission = "android.permission.BIND_WALLPAPER")] [IntentFilter(new[] { "android.service.wallpaper.WallpaperService" }, Categories = new[] { "com.google.android.wearable.watchface.category.WATCH_FACE" })] public partial class XFitWatchfaceService: CanvasWatchFaceService { public override WallpaperService.Engine OnCreateEngine() { return new XFitWatchfaceEngine(this); } partial class XFitWatchfaceEngine : Engine { // drawing code } }

XFitWatchfaceService.cs (Drawing Code)

Page 40: Google Fit, Android Wear & Xamarin

Partial Classes

XFitWatchfaceService

XFitWatchFaceEngine (Drawing Code)

XFitWatchFaceEngine (Config Code)

XFitWatchFaceEngine (Google API Client Code)

XFitWatchFaceEngine (Step Count Code)

XFitWatchFaceEngine

[MetaData( "com.google.android.wearable.watchface.wearableConfigurationAction", Value = "XFitWatchface.CONFIG")] [MetaData( "com.google.android.wearable.watchface.companionConfigurationAction", Value = "XFitWatchface.CONFIG")] public partial class XFitWatchfaceService : CanvasWatchFaceService { partial class XFitWatchfaceEngine : Engine, IDataApiDataListener { void ConnectWatchfaceConfigUpdates() { } } }

XFitWatchfaceServiceConfig.cs (Config Code)

Page 41: Google Fit, Android Wear & Xamarin

Partial Classes

XFitWatchfaceService

XFitWatchFaceEngine (Drawing Code)

XFitWatchFaceEngine (Config Code)

XFitWatchFaceEngine (Google API Client Code)

XFitWatchFaceEngine (Step Count Code)

XFitWatchFaceEngine

public partial class XFitWatchfaceService : CanvasWatchFaceService { partial class XFitWatchfaceEngine : Engine { GoogleApiClient googleApiClient; void ConnectGoogleApiClient() { googleApiClient = new GoogleApiClient.Builder(owner) .AddApi(FitnessClass.HISTORY_API) .AddApi(FitnessClass.RECORDING_API) .AddApi(WearableClass.API) .Build(); googleApiClient.Connect(); } } }

XFitWatchfaceServiceGoogleApiClient.cs (Google API Client)

Page 42: Google Fit, Android Wear & Xamarin

Google Fit

Page 43: Google Fit, Android Wear & Xamarin
Page 44: Google Fit, Android Wear & Xamarin

Activity Read/Write

Body Read/Write

Location Read/Write

Nutrition Read/Write

Page 45: Google Fit, Android Wear & Xamarin

Sleep Duration & Type

Calories Consumed, Macronutrients

Height, Weight, BMI, Body Fat % Heart Rate

Distance Traveled Steps Calories Burned Activity Segments (walking, running, biking) Gym Activities

ACTIVITY

NUTRITION

BODY

SLEEP

Page 46: Google Fit, Android Wear & Xamarin

SENSORS API

RECORDING API

HISTORY API

SESSIONS API

Page 47: Google Fit, Android Wear & Xamarin

SENSORS API

Find all sensors, both on and off-device FindDataSources (GoogleApiClient c, DataSourcesRequest r)

Register for sensor data Add (GoogleApiClient c, SensorRequest r, IOnDataPointListener l);

Unregister for sensor data Remove (GoogleApiClient c, IOnDataPointListener l);

Page 48: Google Fit, Android Wear & Xamarin

RECORDING API

Stop recording data types/sources Unsubscribe (GoogleApiClient c, DataType t) Unsubscribe (GoogleApiClient c, DataSource s)

What data is being recorded ListSubscriptions (GoogleApiClient c)

Record from data types/sources Subscribe (GoogleApiClient c, DataType t) Subscribe (GoogleApiClient c, DataSource s)

Page 49: Google Fit, Android Wear & Xamarin

HISTORY API

Manually add data InsertData (GoogleApiClient c, DataSet d)

Remove data DeleteData (GoogleApiClient c, DeleteDataRequest r)

Retrieve data void ReadData (GoogleApiClient c, DataReadRequest r)

Page 50: Google Fit, Android Wear & Xamarin

SESSIONS API

Start recording a live session void StartSession(GoogleApiClient client, Session session);

Stop recording a live session void StopSession (GoogleApiClient client, String identifier);

Insert a session void InsertSession (GoogleApiClient client, SessionInsertRequest request)

Page 51: Google Fit, Android Wear & Xamarin

curl \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ https://www.googleapis.com/fitness/v1/users/me/dataSources/\ derived:com.google.step_count.delta:...:estimated_steps/\ datasets/1438701040000000000-143930584000000000

REST API

Page 52: Google Fit, Android Wear & Xamarin

Reading Steps

Page 53: Google Fit, Android Wear & Xamarin

Create a GoogleApiClient

Ensure device is recording steps

Build an aggregated query

Read and extract the data

1

2

3

4

Page 54: Google Fit, Android Wear & Xamarin

void BuildApiClient () { googleApiClient = new GoogleApiClient.Builder (this) .AddApi (FitnessClass.HISTORY_API) .AddApi (FitnessClass.RECORDING_API) .AddScope (FitnessClass.ScopeActivityReadWrite) .EnableAutoManage (this, AuthClientId, result => { // Unresolvable error, handle or fail silently }) .Build (); SubscribeToSteps (); ReadSteps (); }

Read before connection established

Auto manage connection

Build API Client

Page 55: Google Fit, Android Wear & Xamarin

void SubscribeToSteps () { FitnessClass.RecordingApi.Subscribe (googleApiClient, TypeStepCountDelta) .SetResultCallback( (IResult result) => { if (!result.Status.IsSuccess) { // Error subscribing, handle } }); }

Subscribing

Page 56: Google Fit, Android Wear & Xamarin

// Read step count deltas from the History API DataReadRequest DailyStepsForWeek() { long midnight = TimeUtility.MidnightLocalPosix (); long midnightWeekAgo = TimeUtility.DaysAgoPosix (midnight, 7);

return new DataReadRequest.Builder () .Aggregate (DataType.TypeStepCountDelta, DataType.AggregateStepCountDelta) .BucketByTime (1, TimeUnit.Days) .SetTimeRange (midnightWeekAgo, midnight, TimeUnit.Milliseconds) .Build(); }

Data Aggregation

Data Read Request

Page 57: Google Fit, Android Wear & Xamarin

// Reads and extracts step data void ReadDailySteps () { DataReadRequest readRequest = RequestBuilder.DailyStepsForWeek (); FitnessClass.HistoryApi.ReadData (googleApiClient, readRequest) .SetResultCallback ( (IResult result) => { IList<Bucket> buckets = ((DataReadResult)result).Buckets; foreach (var bucket in buckets) { var stepCount = bucket.DataSets.Sum(dataSet => dataSet.DataPoints.Sum (dp => dp.DataType.Fields.Sum (f => dp.GetValue (f).AsInt()))); Log.Info (Tag, $"Step Count: {stepCount}"); } }); }

Reading Data

Extracting Data

Read Daily Steps

Page 58: Google Fit, Android Wear & Xamarin

Writing Data

Page 59: Google Fit, Android Wear & Xamarin

// Writing data googleApiClient = new GoogleApiClient.Builder(context) .AddApi(...) .AddConnectionCallbacks( connectionHint => { // Connected! }, cause => { // Suspended } ).Build();

Writes require an established connection

Writing Data

Page 60: Google Fit, Android Wear & Xamarin

Reading Today’s Steps

Page 61: Google Fit, Android Wear & Xamarin

Create a GoogleApiClient

Ensure device is recording steps

Build an aggregated query

Read and extract the data

1

2

3

3

Page 62: Google Fit, Android Wear & Xamarin

Create a GoogleApiClient

Ensure device is recording steps

Read and extract the data

1

2

2

Page 63: Google Fit, Android Wear & Xamarin

// All in one reading steps void AllInOne() { var client = new GoogleApiClient.Builder (this) .AddApi (FitnessClass.HISTORY_API) .UseDefaultAccount () .Build (); client.Connect ();

FitnessClass.HistoryApi.ReadDailyTotal (client, TypeStepCountDelta) .SetResultCallback ( (IResult result) => { DataSet ds = ((DailyTotalResult) result).Total; int stepCount = ds.DataPoints.Sum ( dp => dp.DataType.Fields.Sum ( f => dp.GetValue (f).AsInt())); }); }

Default Account Read Daily Total

Read Today’s Steps

Page 64: Google Fit, Android Wear & Xamarin

Code Lab

Page 65: Google Fit, Android Wear & Xamarin

Thank You!https://developers.google.com/fit/https://developer.android.com/wear/http://developer.android.com/training/wearables/watch-faces/