[Test]
public void TimerManager_CreateManagerWithNullArguments()
{
- Assert.Throws(Is.TypeOf<ArgumentNullException>(), () => new TimerManager(null, null, null));
+ Assert.Throws(Is.TypeOf<ArgumentNullException>(), () => new TimerManager(null, null, null, null));
}
[SetUp]
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
//Act
var time = timerManager.GetTimerTime();
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
//Act
timerManager.SetTimerTime(TimeSpan.FromHours(2));
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
//Act
timerManager.Start();
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
_timerRepository.Time = TimeSpan.FromHours(24);
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
var timerRepositoryTime = new TimeSpan(99, 59, 59);
_timerRepository.Time = timerRepositoryTime;
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
_timerRepository.State = TimerState.Running;
_timerRepository.PlatformId = _platformAlarmsService
.CreateNewPlatformAlarm(new DateTime(2018, 01, 01, 10, 0, 0), DaysOfWeek.Never).Id;
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
_timerRepository.TimeLeft = TimeSpan.FromMinutes(30);
_timerRepository.State = TimerState.Paused;
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
_timerRepository.Time = new TimeSpan(99, 59, 59);
{
//Arrange
var timerManager =
- new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock);
+ new TimerManager(_platformAlarmsService, _timerRepository, MockFactory.NavigationServiceMock, null);
_timerRepository.PlatformId = 1;
_timerRepository.Time = TimeSpan.FromHours(24);
//Act
<Compile Update="Views\TimerRingPage.xaml.cs">
<DependentUpon>TimerRingPage.xaml</DependentUpon>
</Compile>
+ <Compile Update="Views\TimerPage.xaml.cs">
+ <DependentUpon>TimerPage.xaml</DependentUpon>
+ </Compile>
<Compile Update="Views\WorldClockPage.xaml.cs">
<DependentUpon>WorldClockPage.xaml</DependentUpon>
</Compile>
<EmbeddedResource Update="Views\TimerRingPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
- <EmbeddedResource Update="Views\TimerView.xaml">
+ <EmbeddedResource Update="Views\TimerPage.xaml">
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Views\WorldClockPage.xaml">
_ioCContainer.RegisterSingleton<TimerManager>(new InjectionConstructor(
typeof(IPlatformAlarmsService),
typeof(ITimerRepository),
- typeof(INavigationService)
+ typeof(INavigationService),
+ typeof(ILogger)
));
_ioCContainer.RegisterType<ITranslationService, TranslationService>(
-using System;
-using Clock.Controllers;
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
using Xamarin.Forms;
namespace Clock.Tizen.Mobile.Controls
{
- public class ExtendedEntry : Entry, IExtendedEntryController
+ public class ExtendedEntry : Entry
{
- private int changeNum;
+ public static readonly BindableProperty GuideProperty =
+ BindableProperty.Create(nameof(Guide), typeof(string), typeof(Entry), string.Empty, BindingMode.TwoWay);
- public event EventHandler<TextChangedEventArgs> TextChangedByUser;
- public const int MaxLenght = 2;
-
- public static readonly BindableProperty IsSelectedProperty =
- BindableProperty.Create("IsSelected", typeof(bool), typeof(Entry), default(bool), BindingMode.TwoWay);
-
- public static readonly BindableProperty MaxValueProperty =
- BindableProperty.Create("MaxValue", typeof(int), typeof(Entry), default(int), BindingMode.TwoWay);
-
- public ExtendedEntry NextFocus { get; set; }
-
- public void SendTextChangedByUser()
- {
- TextChangedByUser?.Invoke(this, null);
- }
-
- public ExtendedEntry()
- {
- TextChanged += OnEntryChanged;
-
- Unfocused += OnEntryUnfocus;
- Focused += OnEntryFocus;
-
- TextChangedByUser += IncreaseChangeCount;
- }
-
- private void IncreaseChangeCount(object sender, TextChangedEventArgs e)
- {
- changeNum++;
- }
-
- public static bool Validate(ExtendedEntry entry)
- {
- int n;
- var isNumeric = int.TryParse(entry.Text, out n);
-
- if (string.IsNullOrWhiteSpace(entry.Text) || !isNumeric || n < 0)
- {
- entry.Text = "00";
- return false;
- }
-
- if (n > entry.MaxValue)
- {
- entry.Text = entry.MaxValue.ToString("D2");
- return false;
- }
-
- return true;
- }
-
- public int MaxValue
- {
- get { return (int)GetValue(MaxValueProperty); }
- set { SetValue(MaxValueProperty, value); }
- }
-
- public bool IsSelected
- {
- get { return (bool)GetValue(IsSelectedProperty); }
- set { SetValue(IsSelectedProperty, value); }
- }
-
- public void Increase()
+ public string Guide
{
- int n;
-
- if (!int.TryParse(Text, out n))
- {
- return;
- }
-
- ++n;
-
- if (n > MaxValue)
- {
- n = 0;
- }
-
- Text = n.ToString("D2");
- }
-
- public void Decrease()
- {
- int n;
-
- if (!int.TryParse(Text, out n))
- {
- return;
- }
-
- --n;
-
- if (n < 0)
- {
- n = MaxValue;
- }
-
- Text = n.ToString("D2");
+ get { return (string)GetValue(GuideProperty); }
+ set { SetValue(GuideProperty, value); }
}
- private static void EntryFocusNext(ExtendedEntry entry)
- {
- if (entry.NextFocus != null)
- {
- entry.NextFocus.Focus();
- }
- }
+ public static readonly BindableProperty SelectAllOnFocusedProperty =
+ BindableProperty.Create(nameof(SelectAllOnFocused), typeof(bool), typeof(Entry), default(bool), BindingMode.TwoWay);
- private static void OnEntryFocus(object sender, EventArgs e)
+ public bool SelectAllOnFocused
{
- var entry = sender as ExtendedEntry;
-
- entry.IsSelected = !entry.IsSelected;
- }
-
- private static void OnEntryUnfocus(object sender, FocusEventArgs e)
- {
- var entry = sender as ExtendedEntry;
-
- int n;
- var isNumeric = int.TryParse(entry.Text, out n);
- if (n < 10)
- {
- var output = n.ToString().PadLeft(2, '0');
-
- if (entry.Text != output)
- {
- entry.Text = output;
- }
-
- return;
- }
-
- entry.Text = n.ToString("D2");
- }
-
- private static void OnEntryChanged(object sender, TextChangedEventArgs e)
- {
- var entry = sender as ExtendedEntry;
-
- if (!Validate(entry))
- {
- return;
- }
-
- int n;
- var isNumeric = int.TryParse(entry.Text, out n);
-
- if (entry.IsFocused && isNumeric && entry.changeNum >= 3)
- {
- entry.changeNum = 0;
- EntryFocusNext(entry);
- }
+ get { return (bool)GetValue(SelectAllOnFocusedProperty); }
+ set { SetValue(SelectAllOnFocusedProperty, value); }
}
}
}
\ No newline at end of file
ServiceLocator.Current.GetInstance<IAppCycleService>(),
ServiceLocator.Current.GetInstance<TimerManager>());
}
+
+ public static TimerViewModel CreateTimerViewModel()
+ {
+ return new TimerViewModel(ServiceLocator.Current.GetInstance<INavigationService>(),
+ ServiceLocator.Current.GetInstance<ILogger>(),
+ ServiceLocator.Current.GetInstance<TimerManager>());
+ }
}
}
-using System;
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using System;
using System.ComponentModel;
-using Clock.Controllers;
using Clock.Tizen.Mobile.Renderers;
using Xamarin.Forms;
using TNative = Xamarin.Forms.Platform.Tizen;
{
public class ExtendedEntryRenderer : TNative.EntryRenderer
{
+ private bool _isAllSelected;
+
protected override void OnElementChanged(TNative.ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
- if (e.NewElement != null)
+ if(Control != null && e.NewElement is Clock.Tizen.Mobile.Controls.ExtendedEntry _entry)
{
- if (Control != null)
+ if(!String.IsNullOrEmpty(_entry.Guide))
{
- Control.ChangedByUser += EntryChangedByUser;
+ Control.SetPartText("elm.guide", _entry.Guide);
}
+
+ Control.Focused += Control_Focused;
+ Control.Unfocused += Control_Unfocused;
+ _isAllSelected = false;
}
}
- protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ private void Control_Unfocused(object sender, EventArgs e)
{
- if (e.PropertyName == Clock.Tizen.Mobile.Controls.ExtendedEntry.IsSelectedProperty.PropertyName)
+ _isAllSelected = false;
+ }
+
+ private void Control_Focused(object sender, EventArgs e)
+ {
+ if (!_isAllSelected)
{
- base.Control.SelectAll();
+ Control.SelectAll();
+ _isAllSelected = true;
}
+ }
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
base.OnElementPropertyChanged(sender, e);
-
- if (Control != null)
- {
- Control.MoveCursorEnd();
- }
}
- private void EntryChangedByUser(object sender, EventArgs e)
+ protected override void Dispose(bool disposing)
{
- IExtendedEntryController entry = Element as IExtendedEntryController;
- entry?.SendTextChangedByUser();
+ if(!IsDisposed)
+ {
+ Control.Focused -= Control_Focused;
+ Control.Unfocused -= Control_Unfocused;
+ }
+ base.Dispose(disposing);
}
}
}
\ No newline at end of file
public object StopWatchViewModel => ViewModelFactory.CreateStopWatchViewModel();
public object AlarmRingViewModel => ViewModelFactory.CreateRingViewModel();
public object TimerRingViewModel => ViewModelFactory.CreateTimerRingViewModel();
+ public object TimerViewModel => ViewModelFactory.CreateTimerViewModel();
}
}
--- /dev/null
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using System;
+using System.Globalization;
+using Xamarin.Forms;
+
+namespace Clock.Tizen.Mobile.Tools.Converters
+{
+ public class StringToTimePartConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is int _value)
+ {
+ if (_value < 10)
+ {
+ return value.ToString().PadLeft(2, '0');
+ }
+
+ return value.ToString();
+ }
+ return "00";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string _value)
+ {
+ if (_value.Length == 2)
+ {
+ return int.TryParse(_value, out int _result) ? _result : -1;
+ }
+ }
+
+ return -1;
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using System;
+using System.Globalization;
+using Xamarin.Forms;
+using Clock.Common.Enums;
+
+namespace Clock.Tizen.Mobile.Tools.Converters
+{
+ public class TimerFlowToBoolConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is TimerFlow _value && parameter is TimerFlow _parameter)
+ {
+ var result = _value.HasFlag(_parameter);
+ return result;
+ }
+
+ return false;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+++ /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.ViewModels;
-
-namespace Clock.Tizen.Mobile.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();
- }
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using System;
+using System.Globalization;
+using Xamarin.Forms;
+
+namespace Clock.Tizen.Mobile.Tools.Converters
+{
+ public class TotalHourTimeSpanConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if(value is TimeSpan _value)
+ {
+ return $"{System.Convert.ToInt32(Math.Floor(_value.TotalHours)).ToString("D2")}:{_value.Minutes.ToString("D2")}:{_value.Seconds.ToString("D2")}";
+ }
+ return string.Empty;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using Xamarin.Forms;
+
+namespace Clock.Tizen.Mobile.Tools.TriggerActions
+{
+ public class ForceFocusAction : TriggerAction<VisualElement>
+ {
+ protected override void Invoke(VisualElement sender)
+ {
+ sender?.Focus();
+ }
+ }
+}
Icon="tabs/clock_tabs_ic_worldclock.png" />
<pages:StopWatchPage Title="{DynamicResource IDS_CLOCK_BODY_STOPWATCH}"
Icon="tabs/clock_tabs_ic_stopwatch.png" />
- <ContentPage Title="{DynamicResource IDS_CLOCK_BODY_TIMER}"
- Icon="tabs/clock_tabs_ic_timer.png">
- <StackLayout>
- <Label Text="Timer Stub" />
- </StackLayout>
- </ContentPage>
+ <pages:TimerPage Title="{DynamicResource IDS_CLOCK_BODY_TIMER}"
+ Icon="tabs/clock_tabs_ic_timer.png"/>
</TabbedPage.Children>
-</TabbedPage>
+</TabbedPage>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:controls="clr-namespace:Clock.Tizen.Mobile.Controls;assembly=Clock.Tizen.Mobile"
+ xmlns:viewmodels="clr-namespace:Clock.ViewModels;assembly=Clock"
+ xmlns:converters="clr-namespace:Clock.Tizen.Mobile.Tools.Converters;assembly=Clock.Tizen.Mobile"
+ xmlns:triggerActions="clr-namespace:Clock.Tizen.Mobile.Tools.TriggerActions;assembly=Clock.Tizen.Mobile"
+ xmlns:enums="clr-namespace:Clock.Common.Enums;assembly=Clock"
+ x:Class="Clock.Tizen.Mobile.Views.TimerPage"
+ BindingContext="{Binding Path=TimerViewModel, Source={StaticResource ViewModelLocator}}">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+ <Style TargetType="controls:CustomStyleButton">
+ <Setter Property="WidthRequest" Value="320"/>
+ <Setter Property="VerticalOptions" Value="Center"/>
+ <Setter Property="HorizontalOptions" Value="CenterAndExpand"/>
+ <Setter Property="CustomStyle" Value="bottom"/>
+ </Style>
+ <Style TargetType="controls:ExtendedEntry">
+ <Setter Property="FontSize" Value="80"/>
+ <Setter Property="HorizontalOptions" Value="FillAndExpand"/>
+ <Setter Property="VerticalOptions" Value="FillAndExpand"/>
+ <Setter Property="HorizontalTextAlignment" Value="Center"/>
+ <Setter Property="TextColor" Value="White"/>
+ <Setter Property="SelectAllOnFocused" Value="True"/>
+ <Setter Property="Keyboard" Value="Numeric"/>
+ </Style>
+ <converters:StringToTimePartConverter x:Key="StringToTimePartConverter"/>
+ <converters:TotalHourTimeSpanConverter x:Key="TotalHourTimeSpanConverter"/>
+ <converters:TimerFlowToBoolConverter x:Key="TimerFlowToBoolConverter"/>
+ <enums:TimeFractionName x:Key="TimeFractionName.Hour">Hour</enums:TimeFractionName>
+ <enums:TimeFractionName x:Key="TimeFractionName.Minute">Minute</enums:TimeFractionName>
+ <enums:TimeFractionName x:Key="TimeFractionName.Second">Second</enums:TimeFractionName>
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <StackLayout Orientation="Vertical">
+ <StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand" HorizontalOptions="Fill" Margin="50,0,50,0">
+ <StackLayout x:Name="Headers"
+ Orientation="Horizontal"
+ HorizontalOptions="Fill"
+ VerticalOptions="Center">
+ <Label Text="Hours"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ VerticalTextAlignment="End"
+ HorizontalTextAlignment="Center"
+ TextColor="White"/>
+ <Label Text="Minutes"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ HorizontalTextAlignment="Center"
+ TextColor="White" />
+ <Label Text="Seconds"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ HorizontalTextAlignment="Center"
+ TextColor="White" />
+ </StackLayout>
+ <controls:CustomStyleLabel x:Name="TimeLabel"
+ CustomStyle="Thin"
+ Text="{Binding ElapsedTime, Converter={StaticResource TotalHourTimeSpanConverter}}"
+ TextColor="White"
+ HorizontalTextAlignment="Center"
+ FontSize="154"
+ HorizontalOptions="CenterAndExpand"
+ VerticalOptions="CenterAndExpand"
+ IsVisible="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Processing}}"/>
+ <StackLayout x:Name="TimeSelector"
+ Orientation="Vertical"
+ HorizontalOptions="Fill"
+ VerticalOptions="Center"
+ IsVisible="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Preparing}}">
+ <StackLayout Orientation="Horizontal" HorizontalOptions="Fill">
+ <Button x:Name="ButtonHourIncrease"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ BackgroundColor="Transparent"
+ Command="{Binding IncreaseTimeFractionCommand}"
+ CommandParameter="{StaticResource TimeFractionName.Hour}"
+ Image="images/alarm_picker_arrow_up.png"/>
+ <Button x:Name="ButtonMinuteIncrease"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ BackgroundColor="Transparent"
+ Command="{Binding IncreaseTimeFractionCommand}"
+ CommandParameter="{StaticResource TimeFractionName.Minute}"
+ Image="images/alarm_picker_arrow_up.png" />
+ <Button x:Name="ButtonSecondIncrease"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ BackgroundColor="Transparent"
+ Command="{Binding IncreaseTimeFractionCommand}"
+ CommandParameter="{StaticResource TimeFractionName.Second}"
+ Image="images/alarm_picker_arrow_up.png"/>
+ </StackLayout>
+ <StackLayout Orientation="Horizontal"
+ HorizontalOptions="Fill"
+ VerticalOptions="FillAndExpand">
+ <controls:ExtendedEntry x:Name="EntryHours"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ Text="{Binding Hour, Mode=TwoWay, Converter={StaticResource StringToTimePartConverter}}"
+ Guide="Hour"/>
+ <Label Text=":" FontSize="80"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ VerticalTextAlignment="End"
+ TextColor="White" />
+ <controls:ExtendedEntry x:Name="EntryMinutes"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ Text="{Binding Minute, Mode=TwoWay, Converter={StaticResource StringToTimePartConverter}}"
+ Guide="Minutes">
+ <Entry.Triggers>
+ <DataTrigger TargetType="Entry" Binding="{Binding Source={x:Reference EntryHours}, Path=Text.Length}" Value="2">
+ <DataTrigger.EnterActions>
+ <triggerActions:ForceFocusAction/>
+ </DataTrigger.EnterActions>
+ </DataTrigger>
+ </Entry.Triggers>
+ </controls:ExtendedEntry>
+ <Label Text=":"
+ FontSize="80"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ VerticalTextAlignment="End"
+ TextColor="White"/>
+ <controls:ExtendedEntry x:Name="EntrySeconds"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ Text="{Binding Second, Mode=TwoWay, Converter={StaticResource StringToTimePartConverter}}"
+ Guide="Seconds">
+ <Entry.Triggers>
+ <DataTrigger TargetType="Entry" Binding="{Binding Source={x:Reference EntryMinutes}, Path=Text.Length}" Value="2">
+ <DataTrigger.EnterActions>
+ <triggerActions:ForceFocusAction/>
+ </DataTrigger.EnterActions>
+ </DataTrigger>
+ </Entry.Triggers>
+ </controls:ExtendedEntry>
+ </StackLayout>
+ <StackLayout Orientation="Horizontal"
+ HorizontalOptions="Fill"
+ VerticalOptions="Center">
+ <Button x:Name="ButtonHourDecrease"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ BackgroundColor="Transparent"
+ Command="{Binding DecreaseTimeFractionCommand}"
+ CommandParameter="{StaticResource TimeFractionName.Hour}"
+ Image="images/alarm_picker_arrow_down.png"/>
+ <Button x:Name="ButtonMinuteDecrease"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ BackgroundColor="Transparent"
+ Command="{Binding DecreaseTimeFractionCommand}"
+ CommandParameter="{StaticResource TimeFractionName.Minute}"
+ Image="images/alarm_picker_arrow_down.png" />
+ <Button x:Name="ButtonSecondDecrease"
+ VerticalOptions="Center"
+ HorizontalOptions="CenterAndExpand"
+ BackgroundColor="Transparent"
+ Command="{Binding DecreaseTimeFractionCommand}"
+ CommandParameter="{StaticResource TimeFractionName.Second}"
+ Image="images/alarm_picker_arrow_down.png" />
+ </StackLayout>
+ </StackLayout>
+ </StackLayout>
+ <StackLayout HeightRequest="150"
+ BackgroundColor="White"
+ Orientation="Horizontal"
+ HorizontalOptions="Fill"
+ VerticalOptions="End">
+ <controls:CustomStyleButton x:Name="StartButton"
+ Text="Start"
+ Command="{Binding StartTimerCommand}"
+ IsVisible="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Idle}}"
+ IsEnabled="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Prepared}}">
+ <controls:CustomStyleButton.Triggers>
+ <Trigger TargetType="controls:CustomStyleButton" Property="IsEnabled" Value="False">
+ <Setter Property="WidthRequest" Value="500"/>
+ </Trigger>
+ </controls:CustomStyleButton.Triggers>
+ </controls:CustomStyleButton>
+ <controls:CustomStyleButton x:Name="PauseButton"
+ Text="Pause"
+ Command="{Binding PauseTimerCommand}"
+ IsVisible="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Running}}" />
+ <controls:CustomStyleButton x:Name="ResumeButton"
+ Text="Resume"
+ Command="{Binding ResumeTimerCommand}"
+ IsVisible="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Paused}}" />
+ <controls:CustomStyleButton x:Name="CancelButton"
+ Text="Cancel" Command="{Binding CancelTimerCommand}"
+ IsVisible="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Canceling}}" />
+ <controls:CustomStyleButton x:Name="ResetButton"
+ Text="Reset"
+ Command="{Binding ResetTimerCommand}"
+ IsVisible="{Binding TimerFlow, Converter={StaticResource TimerFlowToBoolConverter}, ConverterParameter={x:Static enums:TimerFlow.Prepared}}" />
+ </StackLayout>
+ </StackLayout>
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using System;
+using Xamarin.Forms;
+using Tizen;
+using Navigation.Tools.Navigation.Interfaces;
+
+namespace Clock.Tizen.Mobile.Views
+{
+ public partial class TimerPage : ContentPage, INavigationServiceAware
+ {
+ public TimerPage()
+ {
+ InitializeComponent();
+ }
+
+ public void DeInitialize()
+ {
+ (BindingContext as INavigationServiceAware)?.DeInitialize();
+ }
+
+ public void Initialize(object parameters)
+ {
+ Initialize();
+ }
+
+ public void Initialize()
+ {
+ (BindingContext as INavigationServiceAware)?.Initialize();
+ }
+
+ public void Initialize<T>()
+ {
+ (BindingContext as INavigationServiceAware)?.Initialize<T>();
+ }
+ }
+}
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
- xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
- xmlns:controls="clr-namespace:Clock.Tizen.Mobile.Controls;assembly=Clock.Tizen.Mobile"
- xmlns:viewmodels="clr-namespace:Clock.ViewModels;assembly=Clock"
- xmlns:converters="clr-namespace:Clock.Tizen.Mobile.Tools.Converters;assembly=Clock.Tizen.Mobile"
- x:Class="Clock.Tizen.Mobile.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>
- <viewmodels:ButtonType x:Key="HourButton">HOUR</viewmodels:ButtonType>
- <viewmodels:ButtonType x:Key="MinuteButton">MINUTE</viewmodels:ButtonType>
- <viewmodels:ButtonType x:Key="SecondButton">SECOND</viewmodels:ButtonType>
- </ResourceDictionary>
- </ContentPage.Resources>
-
- <ContentPage.Content>
- <AbsoluteLayout>
- <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" Command="{Binding IncreaseTime}"
- CommandParameter="{StaticResource HourButton}" Image="images/alarm_picker_arrow_up.png" />
- <controls:ExtendedEntry x:Name="EntryHours"
- Text="{Binding Hour, Mode=TwoWay, StringFormat='{0:D2}'}" FontSize="80"
- HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
- HorizontalTextAlignment="Center" TextColor="White" MaxValue="99" />
- <Button x:Name="ButtonHourDecrease" BackgroundColor="Transparent" Command="{Binding DecreaseTime}"
- CommandParameter="{StaticResource HourButton}" 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"
- Command="{Binding IncreaseTime}" CommandParameter="{StaticResource MinuteButton}"
- Image="images/alarm_picker_arrow_up.png" />
- <controls:ExtendedEntry x:Name="EntryMinutes"
- Text="{Binding Minute, Mode=TwoWay, StringFormat='{0:D2}'}" FontSize="80"
- HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
- HorizontalTextAlignment="Center" TextColor="White" MaxValue="59" />
- <Button x:Name="ButtonMinuteDecrease" BackgroundColor="Transparent"
- Command="{Binding DecreaseTime}" CommandParameter="{StaticResource MinuteButton}"
- 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"
- Command="{Binding IncreaseTime}" CommandParameter="{StaticResource SecondButton}"
- Image="images/alarm_picker_arrow_up.png" />
- <controls:ExtendedEntry x:Name="EntrySeconds"
- Text="{Binding Second, Mode=TwoWay, StringFormat='{0:D2}'}" FontSize="80"
- HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
- HorizontalTextAlignment="Center" TextColor="White" MaxValue="59" />
- <Button x:Name="ButtonSecondDecrease" BackgroundColor="Transparent"
- Command="{Binding DecreaseTime}" CommandParameter="{StaticResource SecondButton}"
- 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" Command="{Binding StartTimer}"
- AbsoluteLayout.LayoutBounds=".5, .5, 0.7, 0.7" AbsoluteLayout.LayoutFlags="All"
- IsVisible="{Binding State, Converter={StaticResource MenuStateConverter}, ConverterParameter={StaticResource StartButton}}"
- IsEnabled="{Binding IsTimeValid}" />
- <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>
- </ContentPage.Content>
-</ContentPage>
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Xamarin.Forms;
-using Clock.Tizen.Mobile.Controls;
-using System.Windows.Input;
-
-namespace Clock.Tizen.Mobile.Views
-{
- public partial class TimerView : ContentPage
- {
- public TimerView()
- {
- InitializeComponent();
-
- EntryHours.NextFocus = EntryMinutes;
- EntryMinutes.NextFocus = EntrySeconds;
- EntrySeconds.NextFocus = EntryHours;
- }
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+namespace Clock.Common.Enums
+{
+ public enum TimeFractionName
+ {
+ Hour,
+ Minute,
+ Second
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright 2018 Samsung Electronics Co., Ltd
+*
+* Licensed under the Flora License, Version 1.1 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://floralicense.org/license/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using System;
+
+namespace Clock.Common.Enums
+{
+ [Flags]
+ public enum TimerFlow
+ {
+ /// <summary>
+ /// Timer is not initialized.
+ /// </summary>
+ Idle = 1 << 0,
+ /// <summary>
+ /// Timer has been initialized and is ready to run.
+ /// </summary>
+ Prepared = 1 << 1,
+ /// <summary>
+ /// Timer is running.
+ /// </summary>
+ Running = 1 << 2,
+ /// <summary>
+ /// Timer is paused.
+ /// </summary>
+ Paused = 1 << 3,
+ /// <summary>
+ /// Timer can be calceled.
+ /// </summary>
+ Canceling = 1 << 4,
+ /// <summary>
+ /// Timer is being initialized.
+ /// </summary>
+ Preparing = 1 << 5,
+ /// <summary>
+ /// Timer is running or paused.
+ /// </summary>
+ Processing = 1 << 6
+ }
+}
\ No newline at end of file
private readonly IPlatformAlarmsService _platformAlarmsService;
private readonly ITimerRepository _timerRepository;
private readonly INavigationService _navigationService;
+ private readonly ILogger _logger;
+ private DateTime _scheduledDateTime;
public TimerState State => _timerRepository.State;
+ public TimeSpan ElapsedTime
+ {
+ get
+ {
+ if (State != TimerState.Running)
+ {
+ return _timerRepository.TimeLeft;
+ }
+ return _scheduledDateTime - _platformAlarmsService.GetCurrentPlatformTime();
+ }
+ }
+
/// <summary>
/// Invoked when timer was started.
/// </summary>
/// <param name="timerRepository">Repository with alarms.</param>
/// <param name="navigationService">Application navigation service.</param>
public TimerManager(IPlatformAlarmsService platformAlarmsService, ITimerRepository timerRepository,
- INavigationService navigationService)
+ INavigationService navigationService, ILogger logger)
{
if (platformAlarmsService == null || timerRepository == null || navigationService == null)
{
_platformAlarmsService = platformAlarmsService;
_timerRepository = timerRepository;
_navigationService = navigationService;
-
+ _logger = logger;
_platformAlarmsService.PlatformAlarmTriggered += PlatformAlarmTriggered;
Synchronize();
time = _timerRepository.TimeLeft;
}
+ _scheduledDateTime = _platformAlarmsService.GetCurrentPlatformTime().AddTicks(time.Ticks);
_timerRepository.PlatformId = _platformAlarmsService
- .CreateNewPlatformAlarm(
- _platformAlarmsService.GetCurrentPlatformTime().AddTicks(time.Ticks),
- DaysOfWeek.Never).Id;
+ .CreateNewPlatformAlarm(_scheduledDateTime, DaysOfWeek.Never).Id;
_timerRepository.State = TimerState.Running;
TimerStarted?.Invoke();
}
{
return;
}
-
_platformAlarmsService.GetPlatformAlarm(_timerRepository.PlatformId)?.Cancel();
- _timerRepository.State = TimerState.Idle;
+ Reset();
TimerStopped?.Invoke();
}
+ private void Reset()
+ {
+ _timerRepository.State = TimerState.Idle;
+ _timerRepository.TimeLeft = TimeSpan.Zero;
+ }
+
/// <summary>
/// Pauses timer.
/// </summary>
return;
}
- var platformAlarm = _platformAlarmsService.GetPlatformAlarm(_timerRepository.PlatformId);
-
- _timerRepository.TimeLeft =
- platformAlarm.GetScheduledDateTime() - _platformAlarmsService.GetCurrentPlatformTime();
+ IPlatformAlarm platformAlarm = _platformAlarmsService.GetPlatformAlarm(_timerRepository.PlatformId);
+ _timerRepository.TimeLeft = ElapsedTime;
platformAlarm.Cancel();
_timerRepository.State = TimerState.Paused;
private void Synchronize()
{
- if (_platformAlarmsService.GetPlatformAlarm(_timerRepository.PlatformId) == null)
+ if (_platformAlarmsService.GetPlatformAlarm(_timerRepository.PlatformId) is IPlatformAlarm _platformAlarm)
+ {
+ _scheduledDateTime = _platformAlarm.GetScheduledDateTime();
+ _timerRepository.State = TimerState.Running;
+ }
+ else
{
- _timerRepository.State = TimerState.Idle;
+ Reset();
}
}
using Clock.Abstractions;
using System;
using System.Windows.Input;
-using Navigation.Tools.Navigation.ViewModels;
using Xamarin.Forms;
+using Navigation.Tools.Navigation.Interfaces;
using Clock.Model;
+using Clock.Common.Enums;
+using Clock.Services;
namespace Clock.ViewModels
{
- public enum MenuState
+ public class TimerViewModel : NavigationServiceAwareViewModel
{
- STARTUP,
- STARTUP_EXTENDED,
- RUNNING,
- PAUSED
- }
-
- public enum ButtonType
- {
- HOUR,
- MINUTE,
- SECOND
- }
-
- public class TimerViewModel : ViewModelBase
- {
- private string time;
+ private TimeSpan _elapsedTime;
private int hour, minute, second;
- private MenuState state = MenuState.STARTUP;
- private bool isTimeValid;
- private IAlarmProvider _alarmProvider = null;
+ private TimerFlow _timerFlow = TimerFlow.Idle | TimerFlow.Preparing;
- 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; }
- public ICommand IncreaseTime { private set; get; }
- public ICommand DecreaseTime { private set; get; }
-
- private Timer model = new Timer();
-
- public bool IsTimeValid
- {
- get { return isTimeValid; }
- set
- {
- if (value != isTimeValid)
- {
- SetPropertyValue(ref isTimeValid, value);
- }
- }
- }
+ public ICommand StartTimerCommand { private set; get; }
+ public ICommand PauseTimerCommand { private set; get; }
+ public ICommand CancelTimerCommand { private set; get; }
+ public ICommand ResumeTimerCommand { private set; get; }
+ public ICommand ResetTimerCommand { private set; get; }
+ public ICommand IncreaseTimeFractionCommand { private set; get; }
+ public ICommand DecreaseTimeFractionCommand { private set; get; }
+ private readonly ILogger _logger;
+ private readonly TimerManager _timerManager;
public int Hour
{
get { return hour; }
set
{
- if (value != hour)
+ if (value != hour && value != -1)
{
- ValidateTime();
SetPropertyValue(ref hour, value);
+ ElapsedTime = ReplaceTimeFraction(value, TimeFractionName.Hour, ElapsedTime);
}
}
}
get { return minute; }
set
{
- if (value != minute)
+ if (value != minute && value != -1)
{
- ValidateTime();
SetPropertyValue(ref minute, value);
+ ElapsedTime = ReplaceTimeFraction(value, TimeFractionName.Minute, ElapsedTime);
}
}
}
get { return second; }
set
{
- if (value != second)
+ if (value != second && value != -1)
{
- ValidateTime();
SetPropertyValue(ref second, value);
+ ElapsedTime = ReplaceTimeFraction(value, TimeFractionName.Second, ElapsedTime);
}
}
}
- private void ValidateTime()
+ private void ValidateTime(TimeSpan timeSpan)
{
- if (hour == 0 && minute == 0 && second == 0)
+ if (timeSpan == TimeSpan.Zero)
{
- IsTimeValid = false;
+ TimerFlow = TimerFlow.Idle | TimerFlow.Preparing;
}
else
{
- IsTimeValid = true;
+ if (TimerFlow.HasFlag(TimerFlow.Idle))
+ {
+ TimerFlow = TimerFlow.Prepared | TimerFlow.Idle | TimerFlow.Preparing;
+ }
}
}
- public MenuState State
+ public TimerFlow TimerFlow
{
- get { return state; }
- set
- {
- if (value != state)
- {
- SetPropertyValue(ref state, value);
- }
- }
+ get => _timerFlow;
+ set => SetPropertyValue(ref _timerFlow, value);
}
- public string Time
+ public TimeSpan ElapsedTime
{
- get { return time; }
+ get => _elapsedTime;
set
{
- if (time != value)
+ if (_elapsedTime != value)
{
- SetPropertyValue(ref time, value);
+ SetPropertyValue(ref _elapsedTime, value);
}
}
}
private bool TimersTick()
{
- if (State != MenuState.RUNNING)
+ if (TimerFlow.HasFlag(TimerFlow.Preparing) || TimerFlow.HasFlag(TimerFlow.Paused))
{
return false;
}
- if ((int)model.Time.TotalHours == 0 && model.Time.Minutes == 0 && model.Time.Seconds == 0)
+ ElapsedTime = TimeSpan.FromSeconds(Math.Floor(_timerManager.ElapsedTime.TotalSeconds) + 1);
+ return true;
+ }
+
+ private TimeSpan ReplaceTimeFraction(int timeFraction, TimeFractionName timeFractionName, TimeSpan source)
+ {
+ TimeSpan result;
+
+ switch (timeFractionName)
{
- State = MenuState.STARTUP;
- CancelTimer.Execute(null);
+ case TimeFractionName.Hour: result = TimeSpan.FromSeconds((source.TotalHours - source.Hours + timeFraction) * 3600); break;
+ case TimeFractionName.Minute: result = TimeSpan.FromSeconds((source.TotalMinutes - source.Minutes + GetValidTimeFraction(timeFraction, 60)) * 60); break;
+ default: result = TimeSpan.FromSeconds(source.TotalSeconds - source.Seconds + GetValidTimeFraction(timeFraction, 60)); break;
}
+ ValidateTime(result);
- Time = string.Format("{0:00}:{1:00}:{2:00}", (int)model.Time.TotalHours, model.Time.Minutes,
- model.Time.Seconds);
+ return result;
+ }
- return true;
+ private int GetValidTimeFraction(int timeFraction, int maxValue)
+ {
+ return timeFraction >= maxValue ? maxValue - 1 : timeFraction;
}
- public TimerViewModel()
+ public TimerViewModel(INavigationService navigationService, ILogger logger, TimerManager timerManager)
+ : base(navigationService)
{
- StartTimer = new Command(() =>
- {
- model.Timeout = new TimeSpan(hour, minute, second);
- model.Run();
- Device.StartTimer(TimeSpan.FromMilliseconds(33), TimersTick);
+ _logger = logger;
- State = MenuState.RUNNING;
+ _timerManager = timerManager;
- _alarmProvider?.CreateAlarm(null);
- });
+ StartTimerCommand = new Command(StartTimer);
- PauseTimer = new Command(() =>
- {
- model.Pause();
- State = MenuState.PAUSED;
- _alarmProvider?.DismissAlarm(null);
- });
+ PauseTimerCommand = new Command(_timerManager.Pause);
- CancelTimer = new Command(() =>
- {
- model.Stop();
- model.Reset();
- State = MenuState.STARTUP;
+ CancelTimerCommand = new Command(CancelTimer);
+
+ ResumeTimerCommand = new Command(_timerManager.Start);
+
+ ResetTimerCommand = new Command(ResetTimer);
+
+ IncreaseTimeFractionCommand = new Command<TimeFractionName>((type) => ChangeTimeFraction(type, true));
+ DecreaseTimeFractionCommand = new Command<TimeFractionName>((type) => ChangeTimeFraction(type, false));
+ }
+
+ private void ResetTimer()
+ {
+ TimerFlow = TimerFlow.Idle | TimerFlow.Preparing;
+ Reset(TimeSpan.Zero);
+ _timerManager.SetTimerTime(TimeSpan.Zero);
+ }
+
+ private void CancelTimer()
+ {
+ _timerManager.Stop();
+ }
- _alarmProvider?.DismissAlarm(null);
- });
+ private void StartTimer()
+ {
+ _timerManager.SetTimerTime(ElapsedTime);
+ _timerManager.Start();
+ }
- ResumeTimer = new Command(() =>
+ private void SetTimerFlowForStarted()
+ {
+ if (TimerFlow.HasFlag(TimerFlow.Running))
{
- model.Resume();
- Device.StartTimer(TimeSpan.FromMilliseconds(33), TimersTick);
- State = MenuState.RUNNING;
+ return;
+ }
+
+ TimerFlow = TimerFlow.Running | TimerFlow.Canceling | TimerFlow.Processing;
+ Device.StartTimer(TimeSpan.FromMilliseconds(33), TimersTick);
+ }
+
+ private void SetTimerFlowForPaused()
+ {
+ TimerFlow = TimerFlow.Paused | TimerFlow.Canceling | TimerFlow.Processing;
+ }
+
+ private void ResetElapsedTime()
+ {
+ ElapsedTime = new TimeSpan(Hour, Minute, Second);
+ }
- _alarmProvider?.CreateAlarm(null);
- });
+ private void Reset(TimeSpan timeSpan)
+ {
+ Hour = timeSpan.Hours;
+ Minute = timeSpan.Minutes;
+ Second = timeSpan.Seconds;
+ }
- ResetTimer = new Command(() =>
+ private void ChangeTimeFraction(TimeFractionName timeFractionName, bool increment)
+ {
+ switch (timeFractionName)
{
- model.Reset();
- Time = "00:00:00";
- });
+ case TimeFractionName.Hour: Hour = increment ? (Hour + 1) % 100 : (Hour + 99) % 100; break;
+ case TimeFractionName.Minute: Minute = increment ? (Minute + 1) % 60 : (Minute + 59) % 60; break;
+ default: Second = increment ? (Second + 1) % 60 : (Second + 59) % 60; break;
+ }
+ }
- IncreaseTime = new Command<ButtonType>((type) =>
+ private void SynchronizeStates()
+ {
+ switch (_timerManager.State)
{
- switch (type)
- {
- case ButtonType.HOUR:
- Hour = (Hour + 1) % 100;
- break;
- case ButtonType.MINUTE:
- Minute = (Minute + 1) % 60;
- break;
- case ButtonType.SECOND:
- Second = (Second + 1) % 60;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, null);
- }
- });
+ case TimerState.Idle: SetTimerFlowForIdle(); break;
+ case TimerState.Paused: SetTimerFlowForPaused(); break;
+ case TimerState.Running: SetTimerFlowForStarted(); break;
+ default: break;
+ }
+ }
- DecreaseTime = new Command<ButtonType>((type) =>
+ private void SetTimerFlowForIdle()
+ {
+ if (TimerFlow.HasFlag(TimerFlow.Processing))
+ {
+ TimerFlow = TimerFlow.Idle | TimerFlow.Prepared | TimerFlow.Preparing;
+ ResetElapsedTime();
+ }
+ else
{
- switch (type)
+ TimerFlow = TimerFlow.Idle | TimerFlow.Preparing;
+ }
+ }
+
+ public override void DeInitialize()
+ {
+ base.DeInitialize();
+ _timerManager.TimerStarted -= SynchronizeStates;
+ _timerManager.TimerPaused -= SynchronizeStates;
+ _timerManager.TimerStopped -= SynchronizeStates;
+ }
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SynchronizeStates();
+ SynchronizeTime();
+ _timerManager.TimerStarted += SynchronizeStates;
+ _timerManager.TimerPaused += SynchronizeStates;
+ _timerManager.TimerStopped += SynchronizeStates;
+ }
+
+ private void SynchronizeTime()
+ {
+ if (TimerFlow.HasFlag(TimerFlow.Running))
+ {
+ if (_timerManager.GetTimerTime().HasValue)
{
- case ButtonType.HOUR:
- Hour = (Hour + 99) % 100;
- break;
- case ButtonType.MINUTE:
- Minute = (Minute + 59) % 60;
- break;
- case ButtonType.SECOND:
- Second = (Second + 59) % 60;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, null);
+ Reset(_timerManager.GetTimerTime().Value);
}
- });
+ }
+ }
- Time = "00:00:00";
+ public override void Initialize<T>()
+ {
+ if (typeof(T) == typeof(TimerViewModel))
+ {
+ Initialize();
+ }
}
}
}
\ No newline at end of file