Development

Step 1: Overview of project

Here, we will briefly discuss parts of the sample project.

WelcomePage

WelcomePage is exactly the same page as you have seen in the overview section. It shows information about the app and lets the user go to the StartPage. Later on, we 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.


Step 2: Extend PrivilegeManager

To support the pedometer sensor, the application needs specific privileges. That is why you need to add privileges at 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.


Step 3: 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 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.


Step 4: 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 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.

You're done!

Congratulations! You have successfully achieved the goal of this Code Lab activity. Now, you can develop your own Pedometer app by yourself! But, if you're having trouble, you may check out the link below.

Pedometer Complete Code3.67 MB