Search code examples
c#xamarinxamarin.formssamsung-health

How to Get Samsung Health Step Count in Xamarin Forms


I’m trying to get a step count from Samsung Health using Xamarin Forms (for an Android App).

I tried using SamsungHealthForXamarin to recreate the SimpleHealth sample project from the Samsung Health SDK.

The Java sample project uses anonymous inline classes within the same scope that we can't do in C#. So it appears you have to build out an IConnectionListener interface instead.

Does anyone have any experience getting "step data" from Samsung Health using Xamarin Forms (C#) ? I’d love to see a super simple example of just getting today's step count in C#. It doesn't seem like it should be so difficult.


Solution

  • If I have understood you correctly, your issue is mostly with how to have the connection listener know what exactly its data store is. Starting from the fact that all listeners on Java are simply a class in C#, that implements the needed interface, we can create our listener like so:

    public class ConnectionListener : HealthDataStore.IConnectionListener
    {
        internal HealthDataStore Store { get; set; }
    
        public void OnConnected()
        {
            var stepCountReporter = new StepCountReporter(Store);
            // NOTE: Check for permissions here
            stepCountReporter.Start();
        }
        public void OnConnectionFailed(HealthConnectionErrorResult p0)
        {
            // Health data service is not available.
        }
        public void OnDisconnected()
        {
            Store.DisconnectService();
        }
    }
    

    The important line is the third one - the internal property Store. Here we will keep a reference to our HealthDataStore, which will depend on our listener.

    Our service will look like this:

    private void InitStepService()
    {
        var connectionListener = new ConnectionListener();
        store = new HealthDataStore(this, connectionListener);
        connectionListener.Store = store; // This is the important line
        store.ConnectService();
    }
    

    Again, the important line is the third line from the method - we are assigning the store to our listener's property, so that we can have its reference there.

    The same approach will go for the StepCountReporter class:

    public class StepCountReporter
    {
        private readonly HealthDataStore store;
        private const long OneDayInMillis = 24 * 60 * 60 * 1000L;
    
        public StepCountReporter(HealthDataStore store)
        {
            this.store = store;
        }
    
        public void Start()
        {
            HealthDataObserver.AddObserver(store, HealthConstants.StepCount.HealthDataType,
                new StepObserver(ReadTodayStepCount));
            ReadTodayStepCount();
        }
    
        private void ReadTodayStepCount()
        {
            var resolver = new HealthDataResolver(store, null);
    
            // Set time range from start time of today to the current time
            var startTime = DateTime.Now.Date.Ticks;
            var endTime = startTime + OneDayInMillis;
    
            ReadRequestBuilder requestBuilder = new ReadRequestBuilder()
                .SetDataType(HealthConstants.StepCount.HealthDataType)
                .SetProperties(new[] { HealthConstants.StepCount.Count })
                .SetLocalTimeRange(HealthConstants.StepCount.StartTime, HealthConstants.StepCount.TimeOffset,
                    startTime, endTime);
    
            IReadRequest request = requestBuilder.Build();
    
            try
            {
                resolver.Read(request).SetResultListener(new StepResultHolderResultListener());
            }
            catch (Exception)
            {
                // Getting step count fails.
            }
        }
    }
    

    You will need 2 additional classes here - StepResultHolderResultListener & StepObserver

    StepResultHolderResultListener

    public class StepResultHolderResultListener : IHealthResultHolderResultListener
    {
        public void OnResult(Java.Lang.Object resultObject)
        {
            if (resultObject is ReadResult result)
            {
                int count = 0;
    
                try
                {
                    var iterator = result.Iterator();
                    while (iterator.HasNext)
                    {
                        var data = (HealthData) iterator.Next();
                        count += data.GetInt(HealthConstants.StepCount.Count);
                    }
                }
                finally
                {
                    result.Close();
                }
    
                // Update your UI here with the count variable
            }
        }
    
        // Rest of the methods from the interface
    }
    

    StepObserver

    public class StepObserver : HealthDataObserver
    {
        private readonly Action readTodayStepCountAction;
    
        private StepObserver(Handler p0)
            : base(p0)
        {
        }
    
        public StepObserver(Action readTodayStepCountAction)
            : this((Handler) null)
        {
            this.readTodayStepCountAction = readTodayStepCountAction;
        }
    
        public override void OnChange(string dataTypeName)
        {
            readTodayStepCountAction();
        }
    }
    

    After that, you can notify the UI in whatever way you wish - using Xamarin's MessagingCenter, using events, using some other kind of observer logic - depending on your project's architecture.

    A few side notes on the topic:

    1. Please note that the project has been moved to Bitbucket as it says in the README.md
    2. As always, there are some restrictions, that you should take into consideration. Samsung Health docs - Restrictions

    Samsung Health Android SDK runs on devices with Android 6.0 Marshmallow (API level 23) or above.

    It requires Samsung Health installation. The latest SDK works with Samsung Health 6.2 or above. See the SDK and Samsung Health’s compatible versions here.

    An app’s targetSdkVersion that uses Samsung Health Android SDK should be 26 or above.

    Samsung Health is available on all Samsung smartphones and also non-Samsung Android smartphones with Marshmallow or above.