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
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
{
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();
}
<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>
{
InitializeComponent();
+ EntryHours.TextChanged += OnEntryTextChanged;
+ EntryMinutes.TextChanged += OnEntryTextChanged;
+ EntrySeconds.TextChanged += OnEntryTextChanged;
+
EntryHours.NextFocus = EntryMinutes;
EntryMinutes.NextFocus = EntrySeconds;
EntrySeconds.NextFocus = EntryHours;
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)
{
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;
+ }
+ }
}
}
<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"/>
--- /dev/null
+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);
+ }
+}
</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>
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Clock.Model
+{
+ public class Alarm
+ {
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
{
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()
{
public void LapAdd()
{
- var splitTime = ElapsedTime();
+ var splitTime = Time;
if (splitTime == null)
{
return;
--- /dev/null
+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;
+ }
+ }
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
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;
--- /dev/null
+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)));
+ }
+ }
+ }
+
+ }
+}
-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))]
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();
+ }
+ }
}