[Timer] Add part of the timer's model 17/117317/5
authorKamil Lipiszko <k.lipiszko@DIGITAL.local>
Fri, 3 Mar 2017 17:53:25 +0000 (18:53 +0100)
committerKamil Lipiszko <k.lipiszko@DIGITAL.local>
Fri, 10 Mar 2017 13:48:05 +0000 (14:48 +0100)
This patch implements timer with its counting down counter.
It is still needed to make app control to call the ring but
until the ring view is not creates, it will stay as it is.

Change-Id: I8ab207061caf78bdbf225cecc26597754433e432

16 files changed:
Clock/Clock.TizenMobile/Controls/ExtendedEntryRenderer.cs
Clock/Clock.TizenMobile/Views/TimerView.xaml
Clock/Clock.TizenMobile/Views/TimerView.xaml.cs
Clock/Clock.TizenMobile/Views/WorldClockView.xaml
Clock/Clock/Abstractions/IAlarmProvider.cs [new file with mode: 0644]
Clock/Clock/Clock.csproj
Clock/Clock/Model/Alarm.cs [new file with mode: 0644]
Clock/Clock/Model/CountUpTimer.cs [new file with mode: 0644]
Clock/Clock/Model/Counter.cs
Clock/Clock/Model/StopWatch.cs
Clock/Clock/Model/Timer.cs [new file with mode: 0644]
Clock/Clock/Tools/Converters/InvertBoolConverter.cs [new file with mode: 0644]
Clock/Clock/Tools/Converters/TimerMenuStateConverter.cs [new file with mode: 0644]
Clock/Clock/ViewModel/StopWatchViewModel.cs
Clock/Clock/ViewModel/TimerViewModel.cs [new file with mode: 0644]
clock/Clock.TizenMobile/Clock.Dependency.cs

index fb7edce..399e0b2 100644 (file)
@@ -1,4 +1,4 @@
-using System;
+using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
@@ -13,12 +13,10 @@ namespace CustomRenderer
 {
     public class ExtendedEntryRenderer : TNative.EntryRenderer
     {
-
         protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
         {
             if (e.PropertyName == Clock.TizenMobile.Controls.ExtendedEntry.IsSelectedProperty.PropertyName)
             {
-                Tizen.Log.Debug("CLOCK", "Entry selected!");
                 base.Control.SelectAll();
             }
 
index 865cb35..8393967 100644 (file)
@@ -2,40 +2,67 @@
 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              xmlns:controls="clr-namespace:Clock.TizenMobile.Controls;assembly=Clock.TizenMobile"
-             x:Class="Clock.TizenMobile.Views.TimerView">
+             xmlns:viewmodels="clr-namespace:Clock.ViewModel;assembly=Clock"
+             xmlns:converters="clr-namespace:Clock.Tools.Converters;assembly=Clock"
+             x:Class="Clock.TizenMobile.Views.TimerView"
+             >
+
+  <ContentPage.BindingContext>
+    <viewmodels:TimerViewModel />
+  </ContentPage.BindingContext>
+
+  <ContentPage.Resources>
+    <ResourceDictionary>
+      <converters:TimerMenuStateConverter x:Key="MenuStateConverter"/>
+      <x:String x:Key="TimeLabel">timeLabel</x:String>
+      <x:String x:Key="TimeSelector">timeSelector</x:String>
+      <x:String x:Key="StartButton">startButton</x:String>
+      <x:String x:Key="CancelButton">cancelButton</x:String>
+      <x:String x:Key="PauseButton">pauseButton</x:String>
+      <x:String x:Key="ResumeButton">resumeButton</x:String>
+    </ResourceDictionary>
+  </ContentPage.Resources>
 
   <ContentPage.Content>
       <AbsoluteLayout>
-        <StackLayout Orientation="Horizontal" AbsoluteLayout.LayoutBounds=".5, .3, 620, 426" AbsoluteLayout.LayoutFlags="PositionProportional" >
+        <Label x:Name="TimeLabel" Text="{Binding Time}" TextColor="White" HorizontalTextAlignment="Center" FontSize="80" AbsoluteLayout.LayoutBounds=".5, .38, 1.0, 0.2" AbsoluteLayout.LayoutFlags="All"
+                  IsVisible="{Binding State, Converter={StaticResource MenuStateConverter}, ConverterParameter={StaticResource TimeLabel}}"/>
+        <StackLayout x:Name="TimeSelector" Orientation="Horizontal" AbsoluteLayout.LayoutBounds=".5, .3, 620, 426" AbsoluteLayout.LayoutFlags="PositionProportional"
+                  IsVisible="{Binding State, Converter={StaticResource MenuStateConverter}, ConverterParameter={StaticResource TimeSelector}}">
           <StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
             <Label Text="Hours" AbsoluteLayout.LayoutBounds=".235, .0, .2, .09" AbsoluteLayout.LayoutFlags="All" VerticalTextAlignment="End" HorizontalTextAlignment="Center" TextColor="White" />
             <Button x:Name="ButtonHourIncrease" BackgroundColor="Transparent" Clicked="OnTimeButtonClicked" Image="images/alarm_picker_arrow_up.png" />
-            <controls:ExtendedEntry x:Name="EntryHours" Text="00" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalTextAlignment="Center" TextColor="White" MaxValue="99"/>
+            <controls:ExtendedEntry x:Name="EntryHours" Text="{Binding Hours, Mode=OneWayToSource}" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalTextAlignment="Center" TextColor="White"  MaxValue="99" />
             <Button x:Name="ButtonHourDecrease" BackgroundColor="Transparent" Clicked="OnTimeButtonClicked" Image="images/alarm_picker_arrow_down.png" />
           </StackLayout>
           <Label Text=":" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="Center" VerticalTextAlignment="Center" TextColor="White"/>
           <StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
             <Label Text="Minutes" AbsoluteLayout.LayoutBounds=".5, .0, .2, .09" AbsoluteLayout.LayoutFlags="All" HorizontalTextAlignment="Center" TextColor="White" />
             <Button x:Name="ButtonMinuteIncrease" BackgroundColor="Transparent" Clicked="OnTimeButtonClicked" Image="images/alarm_picker_arrow_up.png" />
-            <controls:ExtendedEntry x:Name="EntryMinutes" Text="00" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalTextAlignment="Center" TextColor="White" MaxValue="59"/>
+            <controls:ExtendedEntry x:Name="EntryMinutes" Text="{Binding Minutes, Mode=OneWayToSource}" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalTextAlignment="Center" TextColor="White"  MaxValue="59"/>
             <Button x:Name="ButtonMinuteDecrease" BackgroundColor="Transparent" Clicked="OnTimeButtonClicked" Image="images/alarm_picker_arrow_down.png" />
           </StackLayout>
           <Label Text=":" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="Center" VerticalTextAlignment="Center" TextColor="White"/>
           <StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
             <Label Text="Seconds" AbsoluteLayout.LayoutBounds=".765, .0, .2, .09" AbsoluteLayout.LayoutFlags="All" HorizontalTextAlignment="Center" TextColor="White" />
             <Button x:Name="ButtonSecondIncrease" BackgroundColor="Transparent" Clicked="OnTimeButtonClicked" Image="images/alarm_picker_arrow_up.png" />
-            <controls:ExtendedEntry x:Name="EntrySeconds" Text="00" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalTextAlignment="Center" TextColor="White" MaxValue="59"/>
+            <controls:ExtendedEntry x:Name="EntrySeconds" Text="{Binding Seconds, Mode=OneWayToSource}" FontSize="80" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalTextAlignment="Center" TextColor="White" MaxValue="59" />
             <Button x:Name="ButtonSecondDecrease" BackgroundColor="Transparent" Clicked="OnTimeButtonClicked" Image="images/alarm_picker_arrow_down.png" />
           </StackLayout>
         </StackLayout>
 
         <AbsoluteLayout AbsoluteLayout.LayoutBounds=".5, 1, 720, 120" AbsoluteLayout.LayoutFlags="PositionProportional">
           <BoxView Color="White" AbsoluteLayout.LayoutBounds=".5, 1, 1, 1" AbsoluteLayout.LayoutFlags="All"/>
-          <Button x:Name="StartButton" Text="Start" AbsoluteLayout.LayoutBounds=".5, .5, .7, .7" AbsoluteLayout.LayoutFlags="All" IsVisible="True"/>
-          <Button x:Name="PauseButton" Text="Pause" AbsoluteLayout.LayoutBounds=".1, .5, .4, .7" AbsoluteLayout.LayoutFlags="All" IsVisible="False"/>
-          <Button x:Name="CancelButton" Text="Cancel" AbsoluteLayout.LayoutBounds=".9, .5, .4, .7" AbsoluteLayout.LayoutFlags="All" IsVisible="False"/>
-          <Button x:Name="ResumeButton" Text="Resume" AbsoluteLayout.LayoutBounds=".1, .5, .4, .7" AbsoluteLayout.LayoutFlags="All" IsVisible="False"/>
-          <Button x:Name="ResetButton" Text="Reset" AbsoluteLayout.LayoutBounds=".9, .5, .4, .7" AbsoluteLayout.LayoutFlags="All" IsVisible="False"/>
+          <Button x:Name="StartButton" Text="Start" Command="{Binding StartTimer}" AbsoluteLayout.LayoutBounds=".5, .5, 0.7, 0.7" AbsoluteLayout.LayoutFlags="All"
+                  IsVisible="{Binding State, Converter={StaticResource MenuStateConverter}, ConverterParameter={StaticResource StartButton}}"/>
+          <Button x:Name="PauseButton" Text="Pause" Command="{Binding PauseTimer}" AbsoluteLayout.LayoutBounds=".1, .5, 0.4, 0.7" AbsoluteLayout.LayoutFlags="All"
+                  IsVisible="{Binding State, Converter={StaticResource MenuStateConverter}, ConverterParameter={StaticResource PauseButton}}"/>
+          <Button x:Name="CancelButton" Text="Cancel" Command="{Binding CancelTimer}" AbsoluteLayout.LayoutBounds=".9, .5, 0.4, 0.7" AbsoluteLayout.LayoutFlags="All"
+                  IsVisible="{Binding State, Converter={StaticResource MenuStateConverter}, ConverterParameter={StaticResource CancelButton}}"/>
+          <Button x:Name="ResumeButton" Text="Resume" Command="{Binding ResumeTimer}" AbsoluteLayout.LayoutBounds=".1, .5, 0.4, 0.7" AbsoluteLayout.LayoutFlags="All"
+                  IsVisible="{Binding State, Converter={StaticResource MenuStateConverter}, ConverterParameter={StaticResource ResumeButton}}"/>
+          <Button x:Name="ResetButton" Text="Reset" Command="{Binding ResetTimer}" AbsoluteLayout.LayoutBounds=".9, .5, 0.4, 0.7" AbsoluteLayout.LayoutFlags="All"
+                  IsVisible="False"/>
         </AbsoluteLayout>
 
     </AbsoluteLayout>
index a72e3a6..8313a6a 100644 (file)
@@ -28,6 +28,10 @@ namespace Clock.TizenMobile.Views
         {
             InitializeComponent();
 
+            EntryHours.TextChanged += OnEntryTextChanged;
+            EntryMinutes.TextChanged += OnEntryTextChanged;
+            EntrySeconds.TextChanged += OnEntryTextChanged;
+
             EntryHours.NextFocus = EntryMinutes;
             EntryMinutes.NextFocus = EntrySeconds;
             EntrySeconds.NextFocus = EntryHours;
@@ -70,10 +74,10 @@ namespace Clock.TizenMobile.Views
                 bDirection = ButtonDirection.DECREASE;
             }
 
-            EntryUpdate(bType, bDirection);
+            UpdateEntry(bType, bDirection);
         }
 
-        private void EntryUpdate(ButtonType type, ButtonDirection direction)
+        private void UpdateEntry(ButtonType type, ButtonDirection direction)
         {
             switch (type)
             {
@@ -111,5 +115,29 @@ namespace Clock.TizenMobile.Views
                     return;
             }
         }
+
+        private void OnEntryTextChanged(object sender, TextChangedEventArgs e)
+        {
+            int hours, minutes, seconds;
+
+            var isNotNumeric = !int.TryParse(EntryHours.Text, out hours);
+            isNotNumeric |= !int.TryParse(EntryMinutes.Text, out minutes);
+            isNotNumeric |= !int.TryParse(EntrySeconds.Text, out seconds);
+
+            if (isNotNumeric)
+            {
+                StartButton.IsEnabled = false;
+                return;
+            }
+
+            if (hours == 0 && minutes == 0 && seconds == 0)
+            {
+                StartButton.IsEnabled = false;
+            }
+            else
+            {
+                StartButton.IsEnabled = true;
+            }
+        }
     }
 }
index 3a4c933..086cd34 100644 (file)
@@ -13,7 +13,7 @@
             <converters:ClockLocationToOffsetConverter x:Key="ClockLocationToOffsetConverter"/>
         </ResourceDictionary>
     </ContentPage.Resources>
-    
     <AbsoluteLayout>
     <Image Source="images/clock_world_map_01.png" AbsoluteLayout.LayoutBounds="0, 0.025, 1, 0.37" AbsoluteLayout.LayoutFlags="All"/>
     <Button BackgroundColor="Transparent" Image="images/clock_icon_world_clock_arrow_left.png" AbsoluteLayout.LayoutBounds="0.015, 0.205, 0.08, 0.073" AbsoluteLayout.LayoutFlags="All"/>
diff --git a/Clock/Clock/Abstractions/IAlarmProvider.cs b/Clock/Clock/Abstractions/IAlarmProvider.cs
new file mode 100644 (file)
index 0000000..4a0a374
--- /dev/null
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Clock.Model;
+
+namespace Clock.Abstractions
+{
+    public interface IAlarmProvider
+    {
+        bool CreateAlarm(Alarm alarm);
+        bool DismissAlarm(Alarm alarm);
+        bool SnoozeAlarm(Alarm alarm, TimeSpan time);
+    }
+}
index 820559a..ed6fb03 100644 (file)
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Abstractions\IAppControl.cs" />
+    <Compile Include="Model\Alarm.cs" />
+    <Compile Include="Abstractions\IAlarmProvider.cs" />
     <Compile Include="Model\ClockLocation.cs" />
     <Compile Include="Model\Counter.cs" />
     <Compile Include="Model\StopWatch.cs" />
+    <Compile Include="Model\Timer.cs" />
     <Compile Include="Mvvm\INavigationAware.cs" />
     <Compile Include="Mvvm\ViewModelLocationProvider.cs" />
     <Compile Include="Mvvm\ViewModelLocator.cs" />
     <Compile Include="Tools\Converters\ClockLocationToOffsetConverter.cs" />
     <Compile Include="Tools\Converters\ClockLocationToPMDesignationConverter.cs" />
     <Compile Include="Tools\Converters\ClockLocationToTimeConverter.cs" />
+    <Compile Include="Tools\Converters\InvertBoolConverter.cs" />
+    <Compile Include="Tools\Converters\TimerMenuStateConverter.cs" />
     <Compile Include="ViewModel\AlarmViewModel.cs" />
     <Compile Include="ViewModel\StopWatchViewModel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ViewModel\TimerViewModel.cs" />
     <Compile Include="ViewModel\WorldClockViewModel.cs" />
   </ItemGroup>
   <ItemGroup>
diff --git a/Clock/Clock/Model/Alarm.cs b/Clock/Clock/Model/Alarm.cs
new file mode 100644 (file)
index 0000000..8b3bad1
--- /dev/null
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Clock.Model
+{
+    public class Alarm
+    {
+    }
+}
diff --git a/Clock/Clock/Model/CountUpTimer.cs b/Clock/Clock/Model/CountUpTimer.cs
new file mode 100644 (file)
index 0000000..1ac6691
--- /dev/null
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+
+namespace Clock.Model
+{
+    public class CountUpTimer : ITimeProvider
+    {
+        private DateTime startTime;
+        private TimeSpan pauseTime;
+
+        public TimeSpan Time
+        {
+            get
+            {
+                return DateTime.Now - startTime + pauseTime;
+            }
+        }
+
+        public DateTime StartTime
+        {
+            get
+            {
+                return startTime;
+            }
+        }
+
+        public void Run()
+        {
+            startTime = DateTime.Now;
+        }
+
+        public void Reset()
+        {
+            pauseTime = TimeSpan.Zero;
+        }
+
+        public void Resume()
+        {
+            Run();
+        }
+
+        public void Stop()
+        {
+            pauseTime += DateTime.Now - startTime;
+        }
+
+        public void Pause()
+        {
+            Stop();
+        }
+    }
+}
index 9f2836b..45cfc4a 100644 (file)
@@ -8,8 +8,24 @@ namespace Clock.Model
 {
     public class Counter
     {
-        private DateTime startTime;
-        private TimeSpan pauseTime;
+        protected DateTime startTime;
+        protected TimeSpan pauseTime;
+
+        public virtual TimeSpan Time
+        {
+            get
+            {
+                return DateTime.Now - startTime + pauseTime;
+            }
+        }
+
+        public DateTime StartTime
+        {
+            get
+            {
+                return startTime;
+            }
+        }
 
         public void Run()
         {
index 5d38e14..108eeb1 100644 (file)
@@ -16,7 +16,7 @@ namespace Clock.Model {
 
         public void LapAdd()
         {
-            var splitTime = ElapsedTime();
+            var splitTime = Time;
             if (splitTime == null)
             {
                 return;
diff --git a/Clock/Clock/Model/Timer.cs b/Clock/Clock/Model/Timer.cs
new file mode 100644 (file)
index 0000000..08b45cb
--- /dev/null
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Clock.Model
+{
+    public class Timer : Counter
+    {
+        public TimeSpan Timeout { get; set; }
+
+        public override TimeSpan Time {
+            get
+            {
+                return StartTime.Add(Timeout) - DateTime.Now - pauseTime;
+            }
+        }
+    }
+}
diff --git a/Clock/Clock/Tools/Converters/InvertBoolConverter.cs b/Clock/Clock/Tools/Converters/InvertBoolConverter.cs
new file mode 100644 (file)
index 0000000..2292d4e
--- /dev/null
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Clock.Tools.Converters
+{
+    class InvertBoolConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value is bool)
+            {
+                bool? val = (bool)value;
+                return !val;
+            }
+
+            return value;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value is bool)
+            {
+                bool? val = (bool)value;
+                return !val;
+            }
+
+            return value;
+        }
+    }
+}
diff --git a/Clock/Clock/Tools/Converters/TimerMenuStateConverter.cs b/Clock/Clock/Tools/Converters/TimerMenuStateConverter.cs
new file mode 100644 (file)
index 0000000..5e708f7
--- /dev/null
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Clock.ViewModel;
+
+namespace Clock.Tools.Converters
+{
+    public class TimerMenuStateConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value is MenuState && parameter is string)
+            {
+                var menuState = (MenuState)value;
+                var p = (string)parameter;
+
+                if (p == "timeLabel" && menuState != MenuState.STARTUP || p == "timeSelector" && menuState == MenuState.STARTUP)
+                {
+                    return true;
+                }
+
+                switch (menuState)
+                {
+                    case MenuState.STARTUP:
+                        if (p == "startButton")
+                        {
+                            return true;
+                        }
+                        break;
+                    case MenuState.RUNNING:
+                        if (p == "cancelButton" || p == "pauseButton")
+                        {
+                            return true;
+                        }
+                        break;
+                    case MenuState.PAUSED:
+                        if (p == "cancelButton" || p == "resumeButton")
+                        {
+                            return true;
+                        }
+                        break;
+                }
+            }
+            return false;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
index 548d8ee..6f0644b 100644 (file)
@@ -23,12 +23,12 @@ namespace Clock.ViewModel {
                 return false;
             }
 
-            MainCounter = model.ElapsedTime().ToString(@"mm\:ss");
-            MsCounter = model.ElapsedTime().ToString(@"\.ff");
+            MainCounter = model.Time.ToString(@"mm\:ss");
+            MsCounter = model.Time.ToString(@"\.ff");
 
             if (lapList.Count != 0)
             {
-                LapCounter = (model.ElapsedTime() - model.LastLapSplitTime()).ToString(@"mm\:ss\.ff");
+                LapCounter = (model.Time - model.LastLapSplitTime()).ToString(@"mm\:ss\.ff");
             }
 
             return true;
diff --git a/Clock/Clock/ViewModel/TimerViewModel.cs b/Clock/Clock/ViewModel/TimerViewModel.cs
new file mode 100644 (file)
index 0000000..c6518ad
--- /dev/null
@@ -0,0 +1,214 @@
+using Clock.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Xamarin.Forms;
+
+namespace Clock.ViewModel
+{
+    public enum MenuState
+    {
+        STARTUP,
+        STARTUP_EXTENDED,
+        RUNNING,
+        PAUSED
+    }
+
+    public class TimerViewModel : INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        private string time;
+        private int hours, minutes, seconds;
+        private MenuState state = MenuState.STARTUP;
+        private IAlarmProvider alarmProvider = null;
+
+        private Model.Timer model = new Model.Timer();
+
+        public MenuState State
+        {
+            get
+            {
+                return state;
+            }
+            set
+            {
+                if (value != state)
+                {
+                    state = value;
+
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("State"));
+                }
+            }
+        }
+
+
+        public string Hours {
+            get
+            {
+                return hours.ToString();
+            }
+            set
+            {
+                int num;
+                var isNumeric = int.TryParse(value, out num);
+                if (!isNumeric)
+                {
+                    return;
+                }
+
+                if (hours != num)
+                {
+                    hours = num;
+
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hours)));
+                }
+            }
+        }
+
+        public string Minutes {
+            get
+            {
+                return minutes.ToString();
+            }
+            set
+            {
+                int num;
+                var isNumeric = int.TryParse(value, out num);
+                if (!isNumeric)
+                {
+                    return;
+                }
+
+                if (minutes != num)
+                {
+                    minutes = num;
+
+                    if (PropertyChanged != null)
+                    {
+                        PropertyChanged(this, new PropertyChangedEventArgs(nameof(Minutes)));
+                    }
+                }
+            }
+        }
+
+        public string Seconds {
+            get
+            {
+                return seconds.ToString();
+            }
+            set
+            {
+                int num;
+                var isNumeric = int.TryParse(value, out num);
+                if (!isNumeric)
+                {
+                    return;
+                }
+
+                if (seconds != num)
+                {
+                    seconds = num;
+
+                    if (PropertyChanged != null)
+                    {
+                        PropertyChanged(this, new PropertyChangedEventArgs(nameof(Seconds)));
+                    }
+                }
+            }
+        }
+
+        public ICommand StartTimer { private set; get; }
+        public ICommand PauseTimer { private set; get; }
+        public ICommand CancelTimer { private set; get; }
+        public ICommand ResumeTimer { private set; get; }
+        public ICommand ResetTimer { private set; get; }
+
+        private bool TimersTick()
+        {
+            if (State != MenuState.RUNNING)
+            {
+                return false;
+            }
+
+            if ((int)model.Time.TotalHours == 0 && model.Time.Minutes == 0 && model.Time.Seconds == 0)
+            {
+                State = MenuState.STARTUP;
+                CancelTimer.Execute(null);
+            }
+
+            Time = string.Format("{0:00}:{1:00}:{2:00}", (int)model.Time.TotalHours, model.Time.Minutes, model.Time.Seconds);
+
+            return true;
+        }
+
+        public TimerViewModel()
+        {
+            StartTimer = new Command(() =>
+            {
+                model.Timeout = new TimeSpan(hours, minutes, seconds);
+                model.Run();
+                Device.StartTimer(TimeSpan.FromMilliseconds(33), TimersTick);
+
+                State = MenuState.RUNNING;
+
+                alarmProvider?.CreateAlarm(null);
+            });
+
+            PauseTimer = new Command(() =>
+            {
+                model.Pause();
+                State = MenuState.PAUSED;
+                alarmProvider?.DismissAlarm(null);
+            });
+
+            CancelTimer = new Command(() =>
+            {
+                model.Stop();
+                model.Reset();
+                State = MenuState.STARTUP;
+
+                alarmProvider?.DismissAlarm(null);
+            });
+
+            ResumeTimer = new Command(() =>
+            {
+                model.Resume();
+                Device.StartTimer(TimeSpan.FromMilliseconds(33), TimersTick);
+                State = MenuState.RUNNING;
+
+                alarmProvider?.CreateAlarm(null);
+            });
+
+            ResetTimer = new Command(() =>
+            {
+                model.Reset();
+                Time = "00:00:00";
+            });
+
+            Time = "00:00:00";
+        }
+
+        public string Time
+        {
+            get
+            {
+                return time;
+            }
+            set
+            {
+                if (time != value)
+                {
+                    time = value;
+
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Time)));
+                }
+            }
+        }
+
+    }
+}
index c1a134b..cb5ff70 100644 (file)
@@ -1,15 +1,17 @@
-using Clock.Abstractions;
-using Clock.TizenMobile.Views;
+using Clock.TizenMobile.Views;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Xamarin.Forms;
+
 using Clock.TizenMobile;
 using Clock.Mvvm;
 using Clock.ViewModel;
 using Clock.Model;
+using Clock.Abstractions;
+
 using static Tizen.Applications.AppControl;
 
 [assembly: Dependency(typeof(TizenAppControl))]
@@ -73,4 +75,22 @@ namespace Clock.TizenMobile
             return (60 * hours + minutes) * ((timeZone[0] == '-') ? (-1) : (1));
         }
     }
+
+    class TizenAlarmProvider : IAlarmProvider
+    {
+        public bool CreateAlarm(Alarm alarm)
+        {
+            throw new NotImplementedException();
+        }
+
+        public bool DismissAlarm(Alarm alarm)
+        {
+            throw new NotImplementedException();
+        }
+
+        public bool SnoozeAlarm(Alarm alarm, TimeSpan time)
+        {
+            throw new NotImplementedException();
+        }
+    }
 }