Implementation
how to implement the sweat loss monitor application the following flow chart represents the order of invocation of the health services main functions and the health tracking service ones, and the interaction between both the libraries the following image represents an overview of the main classes used in the sweat loss example application the sweatingbucketsservice is a foreground service, which controls both healthservicesmanager and trackingservicemanager it starts when the user presses the start button and it stops after user stops the exercise, and gets the final sweat loss value when the user destroys the activity by swiping right during the active exercise, the application will continue to work in the background in the foreground service the user knows about the running application from the notification when the user opens the application again either via notification or by tapping the application icon , the new activity will show the current exercise status a similar event occurs when the application goes to the background i e when the screen goes into the sleep mode in this case, the activity is not destroyed, only get onstop event the user can bring the activity back by tapping the notification to observe the current exercise status and, after tapping the stop button, to get the final sweat loss result sweatingbucketsservice is responsible for collecting the results from the health services and the health tracking service and exchanging the data between both the services it also reports the following status values to the ui status for sweat loss measurement sweat loss value exercise distance exercise duration average steps per minute current steps per minute connection errors not available capabilities it uses android's messenger mechanism to establish a two-way communication with the ui permissions in order to measure the sweat loss, we need to get the following permissions body_sensors to use the samsung health sensor sdk’s sweat loss tracker activity_recognition for measuring the steps per minute during a running exercise additionally, we also need foreground_service to be able to continue measurement in the background internet to resolve the samsung health sensor sdk’s healthtrackingservice connection error the application will display the permissions popup immediately after the start capabilities before using the health services and the samsung health sensor sdk’s apis, let us check whether the galaxy watch can measure the sweat loss we need to check availability of the following capabilities from health services exercisetype running datatype steps_per_minute datatype distance from samsung health sensor sdk healthtrackertype sweat_loss all the capabilities from both the services should be checked immediately after the application launch so that the user is informed, as soon as possible, about any problems capabilities check with health services health services is used to measure a running exercise in order to check the capabilities of the running exercise measurement with health services, we first need to obtain the exerciseclient object exerciseclient = healthservices getclient context getexerciseclient ; we need to check the capabilities of the running exercise type, steps_per_minute and distance the capabilities come asynchronously public static void checkcapabilities exerciseclient exerciseclient, healthservicescapabilitieseventlistener listener { futures addcallback exerciseclient getcapabilitiesasync , new futurecallback<exercisecapabilities> { @override public void onsuccess exercisecapabilities result { log i tag, "setexercisecapabilities onsuccess " ; final boolean runningcapabilityavailable = result getsupportedexercisetypes contains exercisetype running ; final boolean stepsperminutecapabilityavailable = result getexercisetypecapabilities exercisetype running getsupporteddatatypes contains steps_per_minute ; final boolean distancecapabilityavailable = result getexercisetypecapabilities exercisetype running getsupporteddatatypes contains datatype distance ; listener onhealthservicescapabilitiesavailabilityresult runningcapabilityavailable && stepsperminutecapabilityavailable, distancecapabilityavailable ; } @override public void onfailure @nonnull throwable t { log i tag, "setexercisecapabilities onfailure , throwable message " + t getmessage ; } }, executors newsinglethreadexecutor ; } the onhealthservicescapabilitiesavailabilityresult listener informs the viewmodel about the results of the capabilities check capabilities check with health tracking service sweat loss is measured with the samsung health sensor sdk after a running exercise if the version of the galaxy watch’s software or the samsung health sensor sdk’s library is old, measuring sweat loss may not be available therefore, checking the capabilities of samsung health sensor sdk is required before starting the measurement first, get the healthtrackingservice object and set a connection listener to connect to the samsung health sensor sdk’s health tracking service healthtrackingservice = new healthtrackingservice healthtrackingserviceconnectionlistener, myapp getappcontext ; the connection listener will be covered in the following sections after connecting to the tracking service, we can check the capabilities this time it is a synchronous call public static boolean hascapabilities context context { if healthtrackingserviceconnection getinstance context isconnected { final list<healthtrackertype> availabletrackers = healthtrackingserviceconnection getinstance context gethealthtrackingservice gettrackingcapability getsupporthealthtrackertypes ; if availabletrackers contains healthtrackertype sweat_loss { log i tag, "the system does support sweat loss tracking" ; return true; } } log e tag, "the system does not support sweat loss tracking" ; return false; } if any of the mandatory capabilities are not available, the application cannot work correctly we should display a failure message to the user setting up and starting the exercise when the user starts the exercise, he taps the start button the request goes to the sweatingbucketsservice, which in turn requests the healthservicesmanager to start the exercise we need to configure the exercise to provide us with the steps per minute and distance using exerciseconfig setting the exercise type exercisetype running setting interesting data types for a running exercise datatype steps_per_minute and datatype distance then, invoke exerciseclient startexerciseasync the implementation of the process looks like this protected void startexercise throws runtimeexception { final exerciseconfig builder exerciseconfigbuilder = exerciseconfig builder exercisetype running setdatatypes new hashset<> arrays aslist datatype distance, steps_per_minute setisautopauseandresumeenabled false setisgpsenabled false ; log i tag, "calling startexerciseasync" ; futures addcallback exerciseclient startexerciseasync exerciseconfigbuilder build , new futurecallback<void> { @override public void onsuccess @nullable void result { log i tag, "startexerciseasync onsuccess " ; } @override public void onfailure @nonnull throwable t { log i tag, "startexerciseasync onfailure starting exercise " + t getmessage ; healthserviceseventlistener onexerciseerror r string exercise_starting_error ; } }, executors newsinglethreadexecutor ; } we show the distance on the watch’s screen and the steps per minute data are sent to the healthtracker object for further processing more on this in below sections steps per minute is an instantaneous value, which is fine when we pass it to the healthtracker, as it expects such format however, we should process it to display the preferred average value to the user the distance value received indicates the distance between the current and the previous readings to display the distance value to the user, we first need to sum all the values from the beginning of the exercise subscribing to updated exercise data health services enables a watch application to receive the running exercise’s data update and to manage the exercise’s lifetime we obtained the exerciseclient in capabilities check now, we set update callback to receive the exercise results including steps per minute and distance exerciseclient setupdatecallback exerciseupdatecallback ; in the callback, we receive the updated exercise state information and the latest exercise data private final exerciseupdatecallback = new exerciseupdatecallback { @override public void onexerciseupdatereceived exerciseupdate update { log i tag, "exerciseupdate update " + update ; exercisestate = update getexercisestateinfo getstate ; log i tag, "exercise state " + exercisestate ; if exercisestate == exercisestate active { if !wasexerciseactive { healthserviceseventlistener onexercisebecomeactive ; } } else { wasexerciseactive = false; if exercisestate == exercisestate ended { switch update getexercisestateinfo getendreason { case exerciseendreason user_end healthserviceseventlistener onexerciseuserended ; break; case exerciseendreason auto_end_superseded log i tag, "exercise terminated it was superseded by other exercise, started by other client " ; healthserviceseventlistener onexerciseautoended r string error_exercise_terminated ; break; case exerciseendreason auto_end_permission_lost log i tag, "exercise permission lost " ; healthserviceseventlistener onexerciseautoended r string error_exercise_permission_lost ; break; } } } wasexerciseactive = exercisestate == exercisestate active; updateintervaldatapoints update getlatestmetrics getintervaldatapoints ; updatesampledatapoints update getlatestmetrics getsampledatapoints ; } }; the detailed exercise state is returned with the update callback as update getexercisestateinfo getstate ; we can start tracking when the state becomes active and stop tracking when it becomes ended the ended status can have several reasons available in update getexercisestateinfo getendreason user_end the exercise ended by the user auto_end_superseded the exercise object has been superseded by another application auto_end_permission_lost the permission has been revoked by the user we should display an error message when the reason is not user_end the exercise results updates are typically received every second, but when the application goes to the background after a couple of seconds, the battery saving mechanism is activated, resulting in the data being updated every 150 secs, i e 2 5 min the results' resolution remains the same, but the data come in batches in the example, the application data is sent to the sweatingbucketsservice via callbacks ondistancedatareceived, onstepsperminutedatareceived the data is sent immediately to the health tracking service connecting to the health tracking service first, connect to the health tracking service private static final connectionlistener connectionlistener = new connectionlistener { @override public void onconnectionsuccess { log i tag, "connection to healthtrackingservice succeeded " ; connected = true; healthtrackingserviceconnectionlistener onhealthtrackingserviceconnectionresult true ; } @override public void onconnectionended { log i tag, "connection to healthtrackingservice ended " ; connected = false; } @override public void onconnectionfailed healthtrackerexception e { connected = false; log i tag, "connection to healthtrackingservice failed error message " + e getmessage ; if e hasresolution { healthtrackingserviceconnectionlistener onhealthtrackingserviceconnectionerrorwithresolution e ; } else { healthtrackingserviceconnectionlistener onhealthtrackingserviceconnectionresult false ; } } }; private static void inithealthtrackingservice { if healthtrackingservice == null { healthtrackingservice = new healthtrackingservice connectionlistener, myapp getappcontext ; } } we are informing the sweatlossviewmodel about the result using callback with onhealthtrackingserviceconnectionresult boolean result in the sweat loss monitor application the samsung health sensor sdk provides apis to resolve connection exceptions if the watch’s health platform is not installed, or its version is old to run the sdk’s library in the error cases, we can check the error’s resolution and resolve them by calling the sdk’s healthtrackerexception hasresolution and healthtrackerexception resolve to check the error’s resolution, the internet permission is required resolving the exception entails being redirected to a view, which downloads the health platform application on the watch setting an event listener to get a sweat loss result once we connect, it is time to acquire the healthtracker object to use the sweat loss feature try { sweatlosstracker = healthtrackingserviceconnection getinstance context gethealthtrackingservice gethealthtracker healthtrackertype sweat_loss, new userprofile gettrackeruserprofile , com samsung android service health tracking data exercisetype running ; } catch final illegalargumentexception e { log i tag, "tracker not created, an exception " + e getmessage ; } the sweat loss amount varies depending on the user’s height, weight, age, and gender set the user profile information with the userprofile object we can define the trackeruserprofile as public class userprofile { public trackeruserprofile gettrackeruserprofile { // the following user profile values are needed for sweat loss calculations // in the real market-ready application the numbers should be editable in ui // height in cm final float height = 182f; // weight in kg final float weight = 80f; // gender 0 woman, 1 man final int gender = 1; final int age = 45; final trackeruserprofile builder builder = new trackeruserprofile builder ; builder setheight height ; builder setweight weight ; builder setgender gender ; builder setage age ; return builder build ; } } to get the sweat loss result, set a trackereventlistener private final healthtracker trackereventlistener trackereventlistener = new healthtracker trackereventlistener { @override public void ondatareceived @nonnull list<datapoint> list { log i tag, "ondatareceived invoked " ; final valuekey<float> sweatlossvaluekey = valuekey sweatlossset sweat_loss; final valuekey<integer> statusvaluekey = valuekey sweatlossset status; // there is always up to 1 element in the list if !list isempty { log i tag, "ondatareceived list size is " + list size + ", timestamp " + list get 0 gettimestamp ; final float sweatloss = list get 0 getvalue sweatlossvaluekey ; final int status = list get 0 getvalue statusvaluekey ; sweatlossstatus = status; sweatlossvalue = sweatloss; log i tag, "sweat loss " + sweatloss + " status " + status ; final boolean finalresult = finalresult status, sweatloss ; //unset listener right after getting final result if finalresult unsettrackerupdatelistener ; trackingserviceeventlistener onsweatlossdatareceived status, sweatloss, finalresult ; } else { log i tag, "ondatareceived list size is null" ; } } @override public void onflushcompleted { log i tag, " onflushcompleted called" ; } @override public void onerror healthtracker trackererror trackererror { if trackererror == healthtracker trackererror permission_error { trackingserviceeventlistener onhealthtrackererror r string tracker_error_permission_error ; } if trackererror == healthtracker trackererror sdk_policy_error { trackingserviceeventlistener onhealthtrackererror r string tracker_error_sdk_policy_denied ; } } }; start an exercise and setting the exercise’s state to start when the user taps the start button on the sweat loss monitor application, we start the exercise in health services by calling startexerciseasync when the exercise starts, we should set the sweatlosstracker’s exercise state to start sweatlosstracker setexercisestate exercisestate start ; during the exercise, the watch’s related sensors measure the exercise and health services updates the exercise’s steps per minute and distance in the background the updated steps per minute must be sent to the sweat loss healthtracker with healthtracker setexercisedata public void feedtrackerwithstepsperminutedata float[] stepsperminutevalues, long[] stepsperminutetimestamps { log i tag, "feedtrackerwithstepsperminutedata " ; final datatype steps_per_minute = datatype step_per_minute; try { log i tag, "feed tracker with stepsperminute data " + arrays tostring stepsperminutevalues ; sweatlosstracker setexercisedata steps_per_minute, stepsperminutevalues, stepsperminutetimestamps ; } catch final illegalargumentexception e { log i tag, " error data spm " + arrays tostring stepsperminutevalues + " utctime " + stepsperminutetimestamps[0] ; } catch final illegalstateexception e { log i tag, e getmessage ; } } get a sweat loss measurement result after the exercise when the user taps the stop button on the sweat loss monitor application to end the exercise, stop the exercise with health services as follows invoke exerciseclient stopexerciseasync wait until the status of the exercise read in update callback changes to ended inform the healthtracker about the stopped exercise to set the sweat loss healthtracker’s exercise state to stop, just invoke sweatlosstracker setexercisestate exercisestate stop ; it gives the final sweat loss measurement result through trackereventlistener that we have set in setting an event listener to get a sweat loss result the result includes the sweat loss amount and the sweat loss measurement result’s status flag see the samsung health sensor sdk’s api reference document for the sweat loss status details unset the sweat loss event listener after receiving the sweat loss result, unset the sweat loss healthtracker’s event listener sweatlosstracker unseteventlistener ; after unsetting the update listener, the watch’s sensors used by healthtracker will stop working