Create a Digital Watch Face


Objective

Learn how to create your own digital watch face using Tizen .NET on a Galaxy Watch with Tizen OS.

Overview

In this Code Lab, you will learn how simple it is to expand on Galaxy Watch’s existing watch faces by creating a new one, tailored to your needs. You will first create an app which displays current time. Afterwards, you will need to display additional information about the battery, like charging state and battery percentage.

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

Set up your environment

You will need the following:

  • Visual Studio 2017 or Visual Studio 2019 (including Xamarin package/component)

    • It is recommended that you use Visual Studio 2017
  • Visual Studio Tools for Tizen (Visual Studio extension)

To test the application, you may need:

  • Samsung Galaxy Watch or Emulator (Tizen 4.0↑)

Create a new project

Open MS Visual Studio to create a new Tizen Watch Face App.

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

  2. In the New Project window do the following steps:

    a. Select Installed > Visual C# > Tizen 4.0.

    b. Select Tizen Watchface App project type.

    c. Specify the project Name (Watchface).

    d. Set the project Location.

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

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

Add assets

  1. Download and unpack the basic assets file.

    The basic assets pack consists of two folders...

    ... which contain the following set of images:

    • Background image file (res/background.png)

    • Image with a satellite icon for displaying seconds (res/satellite.png)

    • Custom application icon - existing file should be replaced (shared/res/WatchFace.png). Notice that application icon filename will be different if you choose your own name for the project. In such case, make sure you update the icon tag in tizen-manifest.xml file.

  2. Select the unpacked folders, that is res and shared, and copy them with context menu or press Ctrl + C. Next, make a left mouse click on the project name WatchFace in Solution Explorer and press Ctrl + V to paste copied folders directly to the WatchFace project.

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

  1. Make sure that there are required files copied into the project folders.

Create the view

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

  1. Remove the StackLayout element, which has been prepared by default, and add an empty AbsoluteLayout element:

    
    <?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="WatchFace.TextWatchApplication">
       <Application.MainPage>
           <ContentPage>
               <AbsoluteLayout>
    
               </AbsoluteLayout>
           </ContentPage>
       </Application.MainPage>
    </Application>
    
    
  2. Add an Image as children of previously created AbsoluteLayout, which will be the background. AbsoluteLayout.LayoutBounds is using bounds that are set as a comma-separated list of values, X, Y, Width, and Height:

    
    <?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="WatchFace.TextWatchApplication">
       <Application.MainPage>
           <ContentPage>
               <AbsoluteLayout>
                   <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          Source="background.png" />
               </AbsoluteLayout>
           </ContentPage>
       </Application.MainPage>
    </Application>
    
    

Notice that Source attribute was set to background.png and it's a file that you have added to application resources in previous step.

  1. Add an Image displaying satellite, which will be rotated around the watchface to indicate the current second:

    
    <?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="WatchFace.TextWatchApplication">
       <Application.MainPage>
           <ContentPage>
               <AbsoluteLayout>
                   <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          Source="background.png" />
    
                   <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          Source="satellite.png"
                          Rotation="0" />
               </AbsoluteLayout>
           </ContentPage>
       </Application.MainPage>
    </Application>
    
    
  2. Add a Label which will display current time:

    
    <?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="WatchFace.TextWatchApplication">
       <Application.MainPage>
           <ContentPage>
               <AbsoluteLayout>
                   <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          Source="background.png" />
    
                   <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          Source="satellite.png"
                          Rotation="0" />
    
                   <Label AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          HorizontalTextAlignment="Center"
                          VerticalTextAlignment="Center"
                          FontAttributes="Bold"
                          FontSize="24"
                          TextColor="#FFFFFF"
                          Text="00:00" />
               </AbsoluteLayout>
           </ContentPage>
       </Application.MainPage>
    </Application>
    
    

After these changes, our application should look like this:

Edit the view model

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

  1. Add SecondsRotation property, which will store rotation value in degrees, based on current second:

    
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace WatchFace
    {
       public class ClockViewModel : INotifyPropertyChanged
       {
           DateTime _time;
    
           public DateTime Time
           {
               get => _time;
               set
               {
                   if (_time == value) return;
                   _time = value;
                   OnPropertyChanged();
               }
           }
    
           public int SecondsRotation { get; private set; }
    
           public event PropertyChangedEventHandler PropertyChanged;
    
           protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
           {
               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
           }
       }
    }
    
    
  2. Whenever the Time property setter is executed, calculate new value for SecondsRotation based on current second and notify the view about the change by calling OnPropertyChanged method with correct parameter:

    
    public DateTime Time
    {
       get => _time;
       set
       {
           if (_time == value) return;
           _time = value;
           OnPropertyChanged();
    
           SecondsRotation = _time.Second * 6;
           OnPropertyChanged(nameof(SecondsRotation));
       }
    } 
    
    

At this point, SecondsRotation have been calculated and the view has been informed. Next, you have to create a binding for that property, so the user interface can be updated.

Create bindings to the view model

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

  1. Add Bindings to the previously created properties in the view model:

    
    <?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="WatchFace.TextWatchApplication">
       <Application.MainPage>
           <ContentPage>
               <AbsoluteLayout>
                   <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          Source="background.png" />
    
                   <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          Source="satellite.png"
                          Rotation="{Binding SecondsRotation}" />
    
                   <Label AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                          HorizontalTextAlignment="Center"
                          VerticalTextAlignment="Center"
                          FontAttributes="Bold"
                          FontSize="24"
                          TextColor="#FFFFFF"
                          Text="{Binding Time, StringFormat='{}{0:HH:mm}'}" />
               </AbsoluteLayout>
           </ContentPage>
       </Application.MainPage>
    </Application>
    
    

Notice that you have created binding to the Time property, which is type of DateTime at the ClockViewModel.cs file. You used StringFormat to display the time in a required format.

Great! You have just completed the implementation of the basic functionality of the watch. Right now the application can be run on the target device or emulator.

Set an application as the default watch face

  1. Press the Back button (upper button on the right side) to close the watch face application if it is launched.

  2. If necessary, use the Home button (bottom button on the right side) to switch the device screen to the default watch face.

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

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

The application sets as the watch face and shows the correct time.

Add the assets

Next, you need to display the battery information on the watch face.

  1. Download and unpack the extended assets file.

    The basic assets pack consists of one folder...

    ... which contains one additional image:

    • charging image file (res/charging.png)

  2. Select the unpacked folder, that is res, and copy them with context menu or press Ctrl + C. Next, make a left mouse click on the project name WatchFace in Solution Explorer and press Ctrl + V to paste copied folders directly to the WatchFace project.

  1. Confirm the following pop-up to replace the content of the res folder of the project.

  1. Make sure that there are required files copied into the project folders.

Update the view

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

  1. Add a Label element responsible for displaying a percentage value of the battery level and an Image element responsible for displaying a charging indicator:

    
    <?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="WatchFace.TextWatchApplication">
            <Application.MainPage>
                <ContentPage>
                    <AbsoluteLayout>
                        <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                               Source="background.png" />
    
                        <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                               Source="satellite.png"
                               Rotation="{Binding SecondsRotation}" />
    
                        <Label AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                               HorizontalTextAlignment="Center"
                               VerticalTextAlignment="Center"
                               FontAttributes="Bold"
                               FontSize="26"
                               TextColor="#FFFFFF"
                               Text="{Binding Time, StringFormat='{}{0:HH:mm}'}" />
    
                        <Label AbsoluteLayout.LayoutBounds="150, 269, 60, 25"
                               HorizontalTextAlignment="Center"
                               VerticalTextAlignment="Center"
                               FontSize="6"
                               TextColor="#C0D5F4"
                               Text="50%" />
    
                        <Image AbsoluteLayout.LayoutBounds="142, 275, 10, 13"
                               Source="charging.png"
                               IsVisible="True" />
                    </AbsoluteLayout>
                </ContentPage>
            </Application.MainPage>
    </Application> 
    
    

At this point, you should have a watch face which displays not only time and seconds indicators, but also the charging icon and the fixed battery percentage. On the next steps, you are going to read actual data from Tizen Battery API.

Update the view model

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

  1. Add two properties responsible for storing information about battery percentage level (BatteryPercent) and charging state (IsCharging):

    
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace WatchFace
    {
        public class ClockViewModel : INotifyPropertyChanged
        {
            DateTime _time;
    
            public DateTime Time
            {
                get => _time;
                set
                {
                    if (_time == value) return;
                    _time = value;
                    OnPropertyChanged();
    
                    SecondsRotation = _time.Second * 6;
                    OnPropertyChanged(nameof(SecondsRotation));
                }
            }
    
            public int SecondsRotation { get; private set; }
            public bool IsCharging { get; private set; }
            public int BatteryPercent { get; private set; }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    
  2. 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;
    
    
  3. Create a class constructor and set initial values to created properties:

    
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using System.Tizen;
    
    namespace WatchFace
    {
        public class ClockViewModel : INotifyPropertyChanged
        {
            DateTime _time;
    
            public DateTime Time
            {
                get => _time;
                set
                {
                    if (_time == value) return;
                    _time = value;
                    OnPropertyChanged();
    
                    SecondsRotation = _time.Second * 6;
                    OnPropertyChanged(nameof(SecondsRotation));
                }
            }
    
            public int SecondsRotation { get; private set; }
            public bool IsCharging { get; private set; }
            public int BatteryPercent { get; private set; }
    
            public ClockViewModel()
            {
                IsCharging = Battery.IsCharging;
                BatteryPercent = Battery.Percent;
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    
  4. Extend the class constructor by defining handlers of ChargingStateChanged and PercentChanged events:

    
    public ClockViewModel()
    {
        IsCharging = Battery.IsCharging;
        BatteryPercent = Battery.Percent;
    
        Battery.ChargingStateChanged += OnChargingStateChanged;
        Battery.PercentChanged += OnPercentChanged;
    }
    
    
  5. Implement defined event handlers:

    
    public ClockViewModel()
    {
        IsCharging = Battery.IsCharging;
        BatteryPercent = Battery.Percent;
    
        Battery.ChargingStateChanged += OnChargingStateChanged;
        Battery.PercentChanged += OnPercentChanged;
    }
    
    private void OnPercentChanged(object sender, BatteryPercentChangedEventArgs e)
    {
        BatteryPercent = e.Percent;
        OnPropertyChanged(nameof(BatteryPercent));
    }
    
    private void OnChargingStateChanged(object sender, BatteryChargingStateChangedEventArgs e)
    {
        IsCharging = e.IsCharging;
        OnPropertyChanged(nameof(IsCharging));
    }
    
    

At this point, you 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.

Create bindings to the view model

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

  1. Create bindings to previously created view model properties:

    
    <?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="WatchFace.TextWatchApplication">
        <Application.MainPage>
            <ContentPage>
                <AbsoluteLayout>
                    <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                           Source="background.png" />
    
                    <Image AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                           Source="satellite.png"
                           Rotation="{Binding SecondsRotation}" />
    
                    <Label AbsoluteLayout.LayoutBounds="0, 0, 360, 360"
                           HorizontalTextAlignment="Center"
                           VerticalTextAlignment="Center"
                           FontAttributes="Bold"
                           FontSize="26"
                           TextColor="#FFFFFF"
                           Text="{Binding Time, StringFormat='{}{0:HH:mm}'}" />
    
                    <Label AbsoluteLayout.LayoutBounds="150, 269, 60, 25"
                           HorizontalTextAlignment="Center"
                           VerticalTextAlignment="Center"
                           FontSize="6"
                           TextColor="#C0D5F4"
                           Text="{Binding BatteryPercent, StringFormat='{0}%'}" />
    
                    <Image AbsoluteLayout.LayoutBounds="142, 275, 10, 13"
                           Source="charging.png"
                           IsVisible="{Binding IsCharging}" />
                </AbsoluteLayout>
            </ContentPage>
        </Application.MainPage>
    </Application>
    
    

Right now the application can be run on the target device and set its 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.

You're done!

Congratulations! You have successfully achieved the goal of this Code Lab. Now, you can develop your own digital watch face by yourself! If you're having trouble, you may download this file:

Watch Face Complete Code
(1.71MB)