C# Apps on Galaxy Watch Face

Overview

The Galaxy Watch is a perfect example of having an intuitive user interface while utilizing the circular bezel, making it feel natural for the wearer. Tizen enhances that experience even further with its optimized power management and ecosystem.

With this tutorial, you can learn how simple it is to expand on Galaxy Watch's existing watch faces with a new one, tailored to your needs.

The first part of the tutorial should not take more than 15 minutes. It ends with complete and ready to use watch application which is able to display the current time.

Figure 1 Galaxy Watch

Figure 1 Galaxy Watch

The second part shows how to display battery related properties on the watch face and should not take more than 10 additional minutes.

Figure 2 Galaxy Watch with battery indicator and charging status

Figure 2 Galaxy Watch with battery indicator and charging status

Presented solution is based on data provided by the Tizen Battery API.

Objective

Learn how to create your own watch face using Tizen C# on a Galaxy Watch.

Setup

To build the Tizen application for the wearable, the environment should be downloaded and installed.

To test the application, the following devices are required:

  • Samsung Galaxy Watch

  • Emulator (optional)

Application Development – Basic Version

1. Preparing the area

Open Visual Studio IDE to create a new Tizen Wearable App.

  • Go to File > New > Project to open the New Project window.

    Figure 3 New Project Window

    Figure 3 New Project Window

  • In the New Project window do the following steps:

    1. 1. Select Installed > Visual C# > Tizen platform.

    2. 2. Select Tizen Watchface App project type.

    3. 3. Specify the project Name.

    4. 4. Set the project Location.

    5. 5. Click the OK button to start creating a new project.

    Figure 4 Starting a new project

    Figure 4 Starting a new project

  • Project structure should look like the following one (Solution Explorer window).

    Figure 5 Project Structure

    Figure 5 Project Structure

2. Adding the assets

Step 1: Download and unpack the basic assetsfor the basic version of the application.

The basic assets pack consists of two folders ...

Figure 5 Project Structure

... which contain following set of images:

  • face image file (res/face.png)

  • hour hand image file (res/hour_hand.png)

  • minute hand image file (res/minute_hand.png)

  • second hand image file (res/second_hand.png)

  • nut image file (res/nut.png)

  • custom application icon - existing file should be replaced (shared/res/WatchFaceSDC.png). Note that the application icon file name will be different if you choose own name for the project.

Step 2: Select and copy the unpacked folders and paste them directly to the WatchFaceSDC project.

Figure 6 Copying unpacked folders to the project

Figure 6 Copying unpacked folders to the project

Step 3: Confirm following pop-ups to replace the content of the res and shared folders of the project.

Figure 7 Merging res folder prompt message

Figure 7 Merging res folder prompt message

Figure 8 Merging shared folder prompt message

Figure 8 Merging shared folder prompt message

3. Creating the view

Step 1: Go to Solution Explorer window and open the TextWatchApplication.xaml file.

Step 2: Remove the StackLayout element, which has been prepared by default, and add an empty AbsoluteLayout element. All other view elements will be placed in the AbsoluteLayout element to be positioned absolutely.

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="WatchFaceSDC.TextWatchApplication">
    <Application.MainPage>
        <ContentPage>
            <AbsoluteLayout>
                
            </AbsoluteLayout>
        </ContentPage>
    </Application.MainPage>
</Application>

Step 3: Add image elements responsible for displaying a watch face, all the watch hands, as well as a nut that connects all hands of the watch.

<AbsoluteLayout>
    <Image AbsoluteLayout.LayoutFlags="All"
           AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
           Source="face.png" />

    <Image AbsoluteLayout.LayoutBounds="177, 90, 6, 180"
           Source="hour_hand.png" />

    <Image AbsoluteLayout.LayoutBounds="178, 65, 4, 230"
           Source="minute_hand.png" />

    <Image AbsoluteLayout.LayoutBounds="179, 40, 2, 280"
           Source="second_hand.png" />

    <Image AbsoluteLayout.LayoutFlags="PositionProportional"
           AbsoluteLayout.LayoutBounds=".5, .5, 17, 17"
           Source="nut.png" />
</AbsoluteLayout>

Right now the application can be run on the target device.

To do this, follow the steps below:

  • Press Ctrl+Shift+B keys to build the project.r

  • Press Ctrl+F5 keys to start installing the application.

    Figure 9 UI Layers for watch face

    Figure 9 UI Layers for watch face

The application displays all UI layers but nothing more happens. In the next steps of the tutorial we will implement application's logic making the watch work.

4. Creating the view model

Step 1: Go to Solution Explorer window and open the ClockViewModel.cs file.

Step 2: Define three properties responsible for storing information about rotation value of each watch hand.

public double HourHandRotation { get; private set; }
public double MinuteHandRotation { get; private set; }
public double SecondHandRotation { get; private set; }

public DateTime Time
{
    get => _time;
    set
    {
        if (_time == value) return;
        _time = value;
        OnPropertyChanged();
    }
}

Step 3: Calculate new rotation value for each watch hand, whenever the Time property setter is executed.

public DateTime Time
{
    get => _time;
    set
    {
        if (_time == value) return;
        _time = value;

        var hourHandRotation = (_time.Hour + (double)_time.Minute / 60) * 30;

        var minuteHandRotation = (_time.Minute + (double)_time.Second / 60) * 6;

        var secondHandRotation = _time.Second * 6;

        OnPropertyChanged();
    }
}

Step 4: Update created properties and notify the application about changes.

public DateTime Time
{
    get => _time;
    set
    {
        if (_time == value) return;
        _time = value;

        var hourHandRotation = (_time.Hour + (double)_time.Minute / 60) * 30;
        if (hourHandRotation != HourHandRotation)
        {
            HourHandRotation = hourHandRotation;
            OnPropertyChanged(nameof(HourHandRotation));
        }

        var minuteHandRotation = (_time.Minute + (double)_time.Second / 60) * 6;
        if (minuteHandRotation != MinuteHandRotation)
        {
            MinuteHandRotation = minuteHandRotation;
            OnPropertyChanged(nameof(MinuteHandRotation));
        }

        var secondHandRotation = _time.Second * 6;
        if (secondHandRotation != SecondHandRotation)
        {
            SecondHandRotation = secondHandRotation;
            OnPropertyChanged(nameof(SecondHandRotation));
        }

        OnPropertyChanged();
    }
}

So we already have all important rotation data which values are updated when it is necessary. All that is left is to bind them to the user interface.

5. Creating bindings to the view model

Step 1: Go to Solution Explorer window and open the TextWatchApplication.xaml file.

Step 2: Create bindings to previously created view model properties.

<AbsoluteLayout>
    <Image AbsoluteLayout.LayoutFlags="All"
           AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
           Source="face.png" />

    <Image AbsoluteLayout.LayoutBounds="177, 90, 6, 180"
           Source="hour_hand.png"
           Rotation="{Binding HourHandRotation}" />

    <Image AbsoluteLayout.LayoutBounds="178, 65, 4, 230"
           Source="minute_hand.png"
           Rotation="{Binding MinuteHandRotation}" />

    <Image AbsoluteLayout.LayoutBounds="179, 40, 2, 280"
           Source="second_hand.png"
           Rotation="{Binding SecondHandRotation}" />

    <Image AbsoluteLayout.LayoutFlags="PositionProportional"
           AbsoluteLayout.LayoutBounds=".5, .5, 17, 17"
           Source="nut.png" />
</AbsoluteLayout>

Great! We have just completed the implementation of the basic functionality of the watch.

Right now the application can be run on the target device.

But launched application does not show the current time. Besides, the watch hands do not move.

Figure 10 Sample Watch Facee

Figure 10 Sample Watch Face

This is not a mistake. In order to allow the application to work, you should set it as a watch face. Go to the next page to find out how to do it.

6. Setting an application as the watch face

Step 1: Press the Back button to close the WatchFaceSDC application if it is launched.

Step 2: If necessary, use the Home button to switch the device screen to the default watch face.

Figure 11 Default Watch Face

Figure 11 Default Watch Face

Step 3: Tap and hold the device screen, and then rotate the Bezel to find your watch face on the list of available watch faces.

Figure 12 Selecting your Watch Face

Figure 12 Selecting your Watch Face

Step 4: Tap a desired watch face. The watch face you selected should be applied on the device screen.

Figure 13 Desired Watch Face selected

Figure 13 Desired Watch Face selected

The application sets the watch face and shows the correct time. Enjoy using your new watch face.

The next steps of this tutorial are optional.

But if you want to know how to display the battery information on the watch face, follow the next steps.

Application Development – Extended Version

1. Adding the assets for the extended version

Step 1: Download and unpack the additional assetsfor the extended version of the application.

The additional assets pack consists of one folder ...

... which contains one additional image:

  • charging image file (res/charging.png)

Step 2: Select and copy the unpacked folder and paste it directly to the WatchFaceSDC project.

Figure 14 Copying additional assets to the project

Step 3: Confirm following popup to replace the content of the res folder of the project.

Figure 15 Merging res folder for additional assets prompt message

2. Updating the view

Step 1: Go to Solution Explorer window and open the TextWatchApplication.xaml file.

Step 2: Add an image element responsible for displaying a charging indicator and a label element responsible for displaying a percentage value of the battery level.

<Image AbsoluteLayout.LayoutFlags="All"
       AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
       Source="face.png" />

<Image AbsoluteLayout.LayoutFlags="XProportional"
       AbsoluteLayout.LayoutBounds=".5, 269, 15, 21"
       Source="charging.png" />

<Label AbsoluteLayout.LayoutFlags="XProportional"
       AbsoluteLayout.LayoutBounds=".5, 300, 100, 19"
       Text="0"
       FontFamily="BreezeSans"
       FontSize="8.4"
       HorizontalTextAlignment="Center"
       TextColor="White" />

<Image AbsoluteLayout.LayoutBounds="177, 90, 6, 180"
       Source="hour_hand.png"
       Rotation="{Binding HourHandRotation}" />

Right now the application runs on the target device and sets as the device watch face.

Figure 16 Battery and charging indicator added on Watch Face

The application UI is now extended by two additional elements.

But still we have something to do. The charger indicator is displayed permanently, regardless of the charging state. Moreover, the battery level value does not display proper value. Solving these problems will be the goal of the further part of the tutorial.

3. Updating the view model

Step 1: Go to Solution Explorer window and open the ClockViewModel.cs file.

Step 2: Define two properties responsible for storing information about battery percentage value and charging state.

public double HourHandRotation { get; private set; }
public double MinuteHandRotation { get; private set; }
public double SecondHandRotation { get; private set; }
public int BatteryPercentage { get; private set; }
public bool IsCharging { get; private set; }

public DateTime Time
{

Step 3: Create a class constructor and set initial values to created properties.

public event PropertyChangedEventHandler PropertyChanged;

public ClockViewModel()
{
    BatteryPercentage = Battery.Percent;
    IsCharging = Battery.IsCharging;
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Step 4: Add a using statement for Tizen.System to get access to the Tizen Battery API.

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tizen.System;

namespace WatchFaceSDC
{

Step 5: Extend the class constructor by defining handlers of PercentChanged and ChargingStateChanged events.

public ClockViewModel()
{
    BatteryPercentage = Battery.Percent;
    IsCharging = Battery.IsCharging;

    Battery.PercentChanged += OnBatteryPercentChanged;
    Battery.ChargingStateChanged += OnBatteryChargingStateChanged;
}

Step 6: Implement defined event handlers.

public ClockViewModel()
{
    BatteryPercentage = Battery.Percent;
    IsCharging = Battery.IsCharging;

    Battery.PercentChanged += OnBatteryPercentChanged;
    Battery.ChargingStateChanged += OnBatteryChargingStateChanged;
}

private void OnBatteryPercentChanged(object sender, BatteryPercentChangedEventArgs e)
{
    BatteryPercentage = e.Percent;
    OnPropertyChanged(nameof(BatteryPercentage));
}

private void OnBatteryChargingStateChanged(object sender, BatteryChargingStateChangedEventArgs e)
{
    IsCharging = e.IsCharging;
    OnPropertyChanged(nameof(IsCharging));
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

So we already have all data needed to display battery properties on the watch face.

All that is left is to bind them to the user interface.

4. Creating bindings to the view model

Step 1: Go to Solution Explorer window and open the TextWatchApplication.xaml file..

Step 2: Create bindings to previously created view model properties.

<Image AbsoluteLayout.LayoutFlags="XProportional"
       AbsoluteLayout.LayoutBounds=".5, 269, 15, 21"
       Source="charging.png"
       IsVisible="{Binding IsCharging}" />

<Label AbsoluteLayout.LayoutFlags="XProportional"
       AbsoluteLayout.LayoutBounds=".5, 300, 100, 19"
       Text="{Binding BatteryPercentage, StringFormat='{0}%'}"
       FontFamily="BreezeSans"
       FontSize="8.4"
       HorizontalTextAlignment="Center"
       TextColor="White" />

Great! We have just completed the implementation of the extended functionality of the watch.

Right now the application can be run on the target device and set as the device watch face. As you can see, the charging indicator is displayed only when the device is plugged in to the charger. Additionally, the current value of the battery level is displayed on the screen.

Figure 17 Watch Face with functioning battery and charging indicator

Thank You.