Develop a Pedometer App
Objective
Learn how to develop a pedometer app on wearables using Tizen.Sensor library. Here, you’ll get to know how to use a pedometer sensor and get data about calories burned, steps, speed, and distance travelled from it.
Overview
After setting up your environment, this Code Lab is divided into:
- Overview – includes description of the application and shortly describes example code from which you start your work with Pedometer.
- Implementation – you’ll learn here how to use Tizen.Sensor library to get data from pedometer sensor. You will also get to know what to do to make your application stable and safe to use.
This Code Lab is only applicable to a Galaxy Watch running in Tizen OS.
Set up your environment
You will need the following:
- Visual Studio 2019 (including Xamarin package/component)
- Visual Studio Tools for Tizen (Visual Studio extension)
- Samsung Galaxy Watch or Emulator (Tizen 5.5↑)
Sample Code
Here is a sample code for you to start coding in this Code Lab. Download it and start your learning experience!
Import and build
If you need help with loading and building a downloaded sample project, follow this tutorial. After that, the structure of the project should look similar to the image below:
Familiarize with the project
Here, we will briefly discuss parts of the sample project.
WelcomePage
WelcomePage
shows information about the app and lets the user go to the StartPage
. Later on, you will add a mechanism to this page which checks required privileges that the application needs to use the pedometer sensor:
~/Views/WelcomePage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<c:CirclePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:c="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=Tizen.Wearable.CircularUI.Forms"
xmlns:viewModels="clr-namespace:Pedometer.ViewModels;assembly=Pedometer"
x:Class="Pedometer.Views.WelcomePage">
<c:CirclePage.BindingContext>
<viewModels:WelcomeViewModel />
</c:CirclePage.BindingContext>
<c:CirclePage.Content>
<AbsoluteLayout>
<Image Source="bg.png" />
<Label AbsoluteLayout.LayoutBounds="0.5, 0.8, 1, 0.85"
AbsoluteLayout.LayoutFlags="All"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
Text="Pedometer is a simple app that lets you track your physical efficiency"
FontSize="Small"
TextColor="#e9e9e9" />
</AbsoluteLayout>
</c:CirclePage.Content>
<c:CirclePage.ActionButton>
<c:ActionButtonItem Text="START"
Command="{Binding GoToStartPageCommand}" />
</c:CirclePage.ActionButton>
</c:CirclePage>
GoToStartPageCommand
changes the main page of the application to StartPage
:
~/ViewModels/WelcomeViewModel.cs
public class WelcomeViewModel : ViewModelBase
{
...
public Command GoToStartPageCommand { get; }
...
public WelcomeViewModel()
{
GoToStartPageCommand = new Command(ExecuteGoToStartPageCommand);
}
...
private void ExecuteGoToStartPageCommand()
{
Application.Current.MainPage = new StartPage();
}
...
}
StartPage
StartPage
gives the user the possibility to start measurement which is also equivalent to going to the MainPage
:
~/Views/StartPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<c:CirclePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:c="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=Tizen.Wearable.CircularUI.Forms"
xmlns:viewModels="clr-namespace:Pedometer.ViewModels;assembly=Pedometer"
x:Class="Pedometer.Views.StartPage">
<c:CirclePage.BindingContext>
<viewModels:StartViewModel />
</c:CirclePage.BindingContext>
<c:CirclePage.Content>
<AbsoluteLayout>
<Image Source="startpage.png" />
<ImageButton AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 1, 1"
BackgroundColor="Transparent"
Command="{Binding GoToMainPageCommand}" />
</AbsoluteLayout>
</c:CirclePage.Content>
</c:CirclePage>
GoToMainPageCommand
from the StartViewModel
changes the main page of the application to MainPage
:
~/ViewModels/StartViewModel.cs
public class StartViewModel : ViewModelBase
{
...
public Command GoToMainPageCommand { get; }
...
public StartViewModel()
{
GoToMainPageCommand = new Command(ExecuteGoToMainPageCommand);
}
...
private void ExecuteGoToMainPageCommand()
{
Application.Current.MainPage = new MainPage();
}
...
}
MainPage
On this page of the pedometer app, all important information is displayed. Using the bezel of the Galaxy Watch, you can view the display showing the calories burned, steps, speed, or distance travelled.
Extend PrivilegeManager
To support the pedometer sensor, the application needs specific privileges. That is why you need to add privileges at the start of the application. According to the Tizen documentation of Pedometer, it is required for an app to have granted HealthInfo privilege if it uses this sensor. Let's add the privilege below to the application: http://tizen.org/privilege/healthinfo
Add the following field, which contains the name of the required field:
~/Privilege/PrivilegeManager.cs
...
/// <summary>
/// The healthinfo privilege key.
/// </summary>
private const string HEALTHINFO_PRIVILEGE = "http://tizen.org/privilege/healthinfo";
...
You have probably noticed that PrivilegeManager
has a list of the PrivilegeItem
objects, which contains privileges that are required by the application. Add the privilege for the healhtinfo
:
~/Privilege/PrivilegeManager.cs
...
/// <summary>
/// List of privileges required by the application.
/// </summary>
private readonly List<PrivilegeItem> _privilegeItems = new List<PrivilegeItem>
{
new PrivilegeItem(HEALTHINFO_PRIVILEGE)
};
...
That's all what you have to add to this class. The rest of the required methods are already there.
The application now will check at start if all required privileges are granted. Unless it is done, PrivilegeManager
will ask for all privileges that the application needs.
If you run the app now, you should see the following request right before WelcomePage
is showed.
Extend PedometerService
Here, you will write all important functionalities for the pedometer support and information display on the screen.
PedometerService
is responsible for supporting the pedometer. It will also inform other classes about changes in data returned by the pedometer.
Add the field of the Pedometer type:
~/Services/PedometerService.cs
#region fields
/// <summary>
/// Stores the instance of the Pedometer.
/// </summary>
private readonly Tizen.Sensor.Pedometer _pedometer;
#endregion
Now, you need to initialize this field and call its Start
method which starts reading data from the sensor:
~/Services/PedometerService.cs
/// <summary>
/// Initializes PedometerService class instance.
/// </summary>
private PedometerService()
{
_pedometer = new Tizen.Sensor.Pedometer();
_pedometer.Start();
}
To read data from the sensor every time it is changed, you have to subscribe event DataUpdated
of the pedometer object. Add this subscription before it is started:
~/Services/PedometerService.cs
using Tizen.Sensor;
...
private PedometerService()
{
_pedometer = new Tizen.Sensor.Pedometer();
_pedometer.DataUpdated += Pedometer_DataUpdated;
_pedometer.Start();
}
...
The definition of the Pedometer_DataUpdated
looks as below:
~/Services/PedometerService.cs
/// <summary>
/// Handles execution of DataUpdated event.
/// </summary>
/// <param name="sender">Object that invoked event.</param>
/// <param name="e">Event Args.</param>
private void Pedometer_DataUpdated(object sender, PedometerDataUpdatedEventArgs e)
{
}
Add the event to the PedometerService
, which informs all subscribers about the change of the data from the pedometer. This event will be of type EventHandler<PedometerUpdatedEventArgs>
since this class contains all information which are displayed on the MainPage
:
~/Services/PedometerService.cs
using System;
...
/// <summary>
/// Event connected with pedometer data change.
/// </summary>
public event EventHandler<PedometerUpdatedEventArgs> PedometerUpdated;
...
Service needs to invoke this method when data from pedometer has changed:
~/Services/PedometerService.cs
/// <summary>
/// Handles execution of DataUpdated event.
/// </summary>
/// <param name="sender">Object that invoked event.</param>
/// <param name="e">Event Args.</param>
private void Pedometer_DataUpdated(object sender, PedometerDataUpdatedEventArgs e)
{
PedometerUpdated?.Invoke(this, new PedometerUpdatedEventArgs((int)e.CalorieBurned,
(int)e.StepCount, (int)e.LastSpeed, (int)e.MovingDistance));
}
Then, add the following code to make the data updated from the beginning:
~/Services/PedometerService.cs
private PedometerService()
{
_pedometer = new Tizen.Sensor.Pedometer();
_pedometer.DataUpdated += Pedometer_DataUpdated;
_pedometer.Start();
PedometerUpdated?.Invoke(this, new PedometerUpdatedEventArgs((int)_pedometer.CalorieBurned,
(int)_pedometer.StepCount, (int)_pedometer.LastSpeed, (int)_pedometer.MovingDistance));
}
Now, add the subscription of the PedometerUpdated
event in MainViewModel
which is binded to the MainPage
:
~/ViewModels/MainViewModel.cs
using Pedometer.Services;
...
/// <summary>
/// Initializes MainViewModel class instance.
/// </summary>
public MainViewModel()
{
PedometerService service = PedometerService.Instance;
service.PedometerUpdated += Service_PedometerUpdated;
}
Update all displayed values in the method Service_PedometerUpdated
:
~/ViewModels/MainViewModel.cs
/// <summary>
/// Handles execution of PedometerUpdated event.
/// </summary>
/// <param name="sender">Object that invoked event.</param>
/// <param name="e">Event Arguments connected with PedometerUpdated event.</param>
private void Service_PedometerUpdated(object sender, PedometerUpdatedEventArgs e)
{
Calories = e.Calories;
Steps = e.Steps;
SpeedAverage = e.SpeedAverage;
Distance = ((double)e.Distance / 1000).ToString("0.###");
}
After launching the app, all data should appear on the screen and update.
However, it is not ready. You have to be sure that the sensor is stopped and disposed when the application is terminated. If this wasn't done, the application would not work correctly every time and it might cause some issues in different apps.
Extend AppTerminatedService
For the pedometer sensor to be stable, you have to be sure that all services are stopped and disposed before the termination of the application.
AppTerminatedService
will inform all subscribers of its event about the actual state of the app. Add the event which will be called at the termination of the app:
~/Services/AppTerminatedService.cs
using System;
...
/// <summary>
/// Event invoked when app is terminated.
/// </summary>
public event EventHandler Terminated;
...
And the method which invokes this event:
~/Services/AppTerminatedService.cs
/// <summary>
/// Invokes Terminated event.
/// </summary>
public void Terminate()
{
Terminated?.Invoke(this, EventArgs.Empty);
}
Now, add the calling for this method when application is terminated:
~/Pedometer.cs
using Pedometer.Services;
...
/// <summary>
/// Executes when app is terminated.
/// </summary>
protected override void OnTerminate()
{
AppTerminatedService service = AppTerminatedService.Instance;
service.Terminate();
base.OnTerminate();
}
This event has to be subscribed by PedometerService
, so the service will stop the measurement when the application is terminated:
~/Services/PedometerService.cs
/// <summary>
/// Initializes PedometerService class instance.
/// </summary>
private PedometerService()
{
_pedometer = new Tizen.Sensor.Pedometer();
_pedometer.DataUpdated += Pedometer_DataUpdated;
_pedometer.Start();
...
var appTerminatedService = AppTerminatedService.Instance;
appTerminatedService.Terminated += AppTerminatedService_Terminated;
}
You need to be sure that the pedometer has been stopped and disposed before the application is fully terminated. Define AppTerminatedService_Terminated
as the following:
~/Services/PedometerService.cs
/// <summary>
/// Handles execution of Terminated event.
/// </summary>
/// <param name="sender">Object that invoked event.</param>
/// <param name="e">Event Args.</param>
private void AppTerminatedService_Terminated(object sender, EventArgs e)
{
_pedometer.Stop();
_pedometer.DataUpdated -= Pedometer_DataUpdated;
_pedometer.Dispose();
}
The app is now ready! You can test it out using an emulator. To learn more about pedometer, you can go to the documentation here.
To test your app in an emulator:
- Build your project using Ctrl + Shift + B.
- Run the wearable emulator first (Tizen > Tizen Emulator Manager > Launch an emulator).
- Install the app using Ctrl + F5.
- When the app appears, right-click the emulator and open Control Panel to test it using emulated data.
You're done!
Congratulations! You have successfully achieved the goal of this Code Lab. Now, you can develop your own pedometer app by yourself! If you're having trouble, you may download this file: