Add Alarm app (#139)
author부정균/Common Platform Lab(SR)/Staff Engineer/삼성전자 <jk.pu@samsung.com>
Wed, 15 Jan 2020 05:32:28 +0000 (14:32 +0900)
committer윤정현/Common Platform Lab(SR)/Staff Engineer/삼성전자 <jh0506.yun@samsung.com>
Wed, 15 Jan 2020 05:32:28 +0000 (14:32 +0900)
41 files changed:
Test/Alarm/Alarm/Alarm.cs [new file with mode: 0644]
Test/Alarm/Alarm/Alarm.csproj [new file with mode: 0644]
Test/Alarm/Alarm/AlarmPageController.cs [new file with mode: 0644]
Test/Alarm/Alarm/App.xaml [new file with mode: 0644]
Test/Alarm/Alarm/App.xaml.cs [new file with mode: 0644]
Test/Alarm/Alarm/Controls/ImageButton.cs [new file with mode: 0644]
Test/Alarm/Alarm/Converters/ItemCountToVisibilityConverter.cs [new file with mode: 0644]
Test/Alarm/Alarm/Converters/ScheduledDateTimeToTextConverter.cs [new file with mode: 0644]
Test/Alarm/Alarm/Implements/AlarmNativeHandler.cs [new file with mode: 0644]
Test/Alarm/Alarm/Implements/AlarmPersistentHandler.cs [new file with mode: 0644]
Test/Alarm/Alarm/Implements/FileOpener.cs [new file with mode: 0644]
Test/Alarm/Alarm/Implements/LocaleHandler.cs [new file with mode: 0644]
Test/Alarm/Alarm/Models/AlarmModel.cs [new file with mode: 0644]
Test/Alarm/Alarm/Models/AlarmRecord.cs [new file with mode: 0644]
Test/Alarm/Alarm/Models/MessageKeys.cs [new file with mode: 0644]
Test/Alarm/Alarm/Renderers/ImageButtonRenderer.cs [new file with mode: 0644]
Test/Alarm/Alarm/Resx/AppResources.Designer.cs [new file with mode: 0644]
Test/Alarm/Alarm/Resx/AppResources.ko.resx [new file with mode: 0644]
Test/Alarm/Alarm/Resx/AppResources.resx [new file with mode: 0644]
Test/Alarm/Alarm/ViewModels/AlertPageModel.cs [new file with mode: 0644]
Test/Alarm/Alarm/ViewModels/BasePageModel.cs [new file with mode: 0644]
Test/Alarm/Alarm/ViewModels/MainPageModel.cs [new file with mode: 0644]
Test/Alarm/Alarm/Views/AlarmAlertPage.xaml [new file with mode: 0644]
Test/Alarm/Alarm/Views/AlarmAlertPage.xaml.cs [new file with mode: 0644]
Test/Alarm/Alarm/Views/AlarmEditPage.xaml [new file with mode: 0644]
Test/Alarm/Alarm/Views/AlarmEditPage.xaml.cs [new file with mode: 0644]
Test/Alarm/Alarm/Views/AlarmListCell.cs [new file with mode: 0644]
Test/Alarm/Alarm/Views/CirclePageEx.xaml [new file with mode: 0644]
Test/Alarm/Alarm/Views/CirclePageEx.xaml.cs [new file with mode: 0644]
Test/Alarm/Alarm/Views/MainPage.xaml [new file with mode: 0644]
Test/Alarm/Alarm/Views/MainPage.xaml.cs [new file with mode: 0644]
Test/Alarm/Alarm/Views/SavePopupPage.xaml [new file with mode: 0644]
Test/Alarm/Alarm/Views/SavePopupPage.xaml.cs [new file with mode: 0644]
Test/Alarm/Alarm/res/Notification.ogg [new file with mode: 0644]
Test/Alarm/Alarm/res/alarm_add_icon.png [new file with mode: 0644]
Test/Alarm/Alarm/res/alarm_no_alarm_icon.png [new file with mode: 0644]
Test/Alarm/Alarm/res/alarm_ringing_icon.png [new file with mode: 0644]
Test/Alarm/Alarm/res/ic_popup_btn_check.png [new file with mode: 0644]
Test/Alarm/Alarm/shared/res/Alarm.png [new file with mode: 0644]
Test/Alarm/Alarm/tizen-manifest.xml [new file with mode: 0644]
XSF.sln

diff --git a/Test/Alarm/Alarm/Alarm.cs b/Test/Alarm/Alarm/Alarm.cs
new file mode 100644 (file)
index 0000000..ed91866
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 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 Alarm.Controls;
+using Alarm.Implements;
+using Alarm.Models;
+using Alarm.Tizen.Renderers;
+using Alarm.ViewModels;
+using Alarm.Views;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Threading;
+using Tizen.Applications;
+using Tizen.System;
+using Tizen.Wearable.CircularUI.Forms;
+using Tizen.Wearable.CircularUI.Forms.Renderer;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+namespace Alarm
+{
+    class Program : global::Xamarin.Forms.Platform.Tizen.ApplicationLifecycle
+    {
+        App app;
+        AlertPageModel _alertPageModel;
+
+        /// <summary>
+        /// Called when this application is launched
+        /// </summary>
+        protected override void OnCreate()
+        {
+            AlarmNativeHandler.ResourceDir = FormsApplication.DirectoryInfo.Resource;
+
+            base.OnCreate();
+
+            app = new App();
+
+            FormsApplication.LoadApplication(app);
+        }
+
+        protected override void OnPause()
+        {
+            Console.WriteLine("OnPause");
+            if (_alertPageModel != null)
+            {
+                if(_alertPageModel.AlertSoundState == SoundState.Start)
+                {
+                    Console.WriteLine("Alert sound state is start, pause alert sound!");
+                    _alertPageModel.PauseAlert();
+                }
+            }
+
+            base.OnPause();
+
+        }
+
+        protected override void OnResume()
+        {
+            Console.WriteLine("OnResume");
+            base.OnResume();
+        }
+
+        /// <summary>
+        /// Called when this app control event received.
+        /// </summary>
+        /// <param name="e">AppControlReceivedEventArgs</param>
+        async protected override void OnAppControlReceived(AppControlReceivedEventArgs e)
+        {
+            Console.WriteLine("OnAppControlReceived");
+            base.OnAppControlReceived(e);
+            AppControl appControl = e.ReceivedAppControl;
+            var navi = app.MainPage as NavigationPage;
+            var currentPage = navi.CurrentPage;
+            try
+            {
+                if (appControl.ExtraData.Count() != 0)
+                {
+                    string AlarmCreatedDate = appControl.ExtraData.Get<string>("AlarmRecord.UniqueIdentifier");
+                    Console.WriteLine($"ExtraData.Count() != 0 AlarmCreatedDate:{AlarmCreatedDate}");
+                    if (AlarmModel.AlarmRecordDictionary == null)
+                    {
+                        // Need to retrieve at the page creation time
+                        AlarmModel.AlarmRecordDictionary = AlarmPersistentHandler.DeserializeAlarmRecord();
+                    }
+
+                    AlarmRecord retrievedRecord;
+                    if (AlarmModel.AlarmRecordDictionary != null)
+                    {
+                        if (AlarmModel.AlarmRecordDictionary.TryGetValue(AlarmCreatedDate, out retrievedRecord))
+                        {
+                            if (retrievedRecord == null)
+                            {
+                                Console.WriteLine("[OnAppControlReceived] retrievedRecord is null!!");
+                            }
+
+                            Console.WriteLine("retrievedRecord:" + retrievedRecord);
+                            if (retrievedRecord != null && retrievedRecord.AlarmState < AlarmStates.Inactive)
+                            {
+                                _alertPageModel = new AlertPageModel(retrievedRecord); 
+                                await currentPage.Navigation.PushAsync(new AlarmAlertPage(_alertPageModel));
+                            }
+                        }
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine("[OnAppControlReceived] Exception - Message: " + ex.Message + ", source: " + ex.Source + ", " + ex.StackTrace);
+            }
+        }
+
+        static void Main(string[] args)
+        {
+            var app = new Program();
+            // define your custom handlers
+            var customRenderers = new Dictionary<Type, Func<IRegisterable>>()
+            {
+                { typeof(CirclePage), ()=> new CirclePageRenderer() },
+                { typeof(global:: Tizen.Wearable.CircularUI.Forms.CircleListView), () => new CircleListViewRenderer() },
+                { typeof(CircleDateTimeSelector), () => new CircleDateTimeSelectorRenderer() },
+                { typeof(CustomImageButton), () => new CustomImageButtonRenderer() }
+             };
+
+            var option = new InitializationOptions(app);
+            option.UseStaticRegistrar(StaticRegistrarStrategy.StaticRegistrarOnly, customRenderers, true);
+            Forms.Init(option);
+
+            // It's mandatory to initialize Circular UI for using Tizen Wearable Circular UI API
+            global::Tizen.Wearable.CircularUI.Forms.Renderer.FormsCircularUI.Init();
+            app.FormsApplication.Run(args);
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Alarm.csproj b/Test/Alarm/Alarm/Alarm.csproj
new file mode 100644 (file)
index 0000000..9f4a15a
--- /dev/null
@@ -0,0 +1,98 @@
+<Project Sdk="Tizen.NET.Sdk/1.0.8">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>tizen40</TargetFramework>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugType>portable</DebugType>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>None</DebugType>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Folder Include="lib\" />
+    <Folder Include="res\" />
+  </ItemGroup>
+
+  <ItemGroup>
+      <ProjectReference Include="..\..\..\Tizen.CircularUI\Tizen.Wearable.CircularUI.Forms.Renderer\XSF.CircularUI.Forms.Renderer.csproj" />
+      <ProjectReference Include="..\..\..\Tizen.CircularUI\Tizen.Wearable.CircularUI.Forms\XSF.CircularUI.Forms.csproj" />
+      <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Core\XSF.Core.csproj" />
+      <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Platform.Tizen\XSF.Platform.Tizen.csproj" />
+      <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Platform\XSF.Platform.csproj" />
+      <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Xaml\XSF.Xaml.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="Resx\AppResource - Copy.Designer.cs">
+      <DependentUpon>AppResource - Copy.resx</DependentUpon>
+      <DesignTime>True</DesignTime>
+      <AutoGen>True</AutoGen>
+    </Compile>
+    <Compile Update="Resx\AppResource - Copy.Designer.cs">
+      <DesignTime>True</DesignTime>
+      <AutoGen>True</AutoGen>
+      <DependentUpon>AppResource.resx</DependentUpon>
+    </Compile>
+    <Compile Update="Resx\AppResources.Designer.cs">
+      <DesignTime>True</DesignTime>
+      <AutoGen>True</AutoGen>
+      <DependentUpon>AppResources.resx</DependentUpon>
+    </Compile>
+    <Compile Update="Views\AlarmAlertPage.xaml.cs">
+      <DependentUpon>AlarmAlertPage.xaml</DependentUpon>
+    </Compile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <EmbeddedResource Update="App.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
+    <EmbeddedResource Update="Resx\AppResources.ko.resx">
+      <LastGenOutput>AppResource.Designer.cs</LastGenOutput>
+      <Generator>PublicResXFileCodeGenerator</Generator>
+    </EmbeddedResource>
+    <EmbeddedResource Update="Resx\AppResources.resx">
+      <Generator>PublicResXFileCodeGenerator</Generator>
+      <LastGenOutput>AppResources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <EmbeddedResource Update="Views\AlarmEditPage.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
+    <EmbeddedResource Update="Views\AlarmAlertPage.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
+    <EmbeddedResource Update="Views\CirclePageEx.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
+    <EmbeddedResource Update="Views\MainPage.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </None>
+    <None Update="Views\AlarmAlertPage.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </None>
+    <None Update="Views\AlarmEditPage.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </None>
+    <None Update="Views\CirclePageEx.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </None>
+    <None Update="Views\MainPage.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </None>
+    <None Update="Views\SavePopupPage.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </None>
+  </ItemGroup>
+
+</Project>
+
diff --git a/Test/Alarm/Alarm/AlarmPageController.cs b/Test/Alarm/Alarm/AlarmPageController.cs
new file mode 100644 (file)
index 0000000..d880424
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 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;
+using Alarm.Models;
+using Alarm.Views;
+using System;
+using Alarm.ViewModels;
+
+namespace Alarm
+{
+    public enum AlarmPages
+    {
+        /// <summary>
+        /// alarm main page
+        /// </summary>
+        MainPage,
+        /// <summary>
+        /// alarm edit page
+        /// </summary>
+        EditPage,
+        /// <summary>
+        ///
+        /// </summary>
+        SavePoupPage
+    }
+
+    public static class AlarmPageController
+    {
+        /// <summary>
+        /// alarm list page
+        /// </summary>
+        static MainPage alarmMainPage;
+
+        /// <summary>
+        /// alarm edit page
+        /// </summary>
+        static AlarmEditPage alarmEditPage;
+
+        /// <summary>
+        /// no alarm page
+        /// </summary>
+        static SavePopupPage savePopupPage;
+
+        public static Page GetInstance(AlarmPages page, Object o = null)
+        {
+            switch (page)
+            {
+                case AlarmPages.MainPage:
+                    return AlarmPageController.alarmMainPage ?? (alarmMainPage = new MainPage(o as MainPageModel));
+                case AlarmPages.EditPage:
+                    // parameter 'o' is AlarmRecord object to show in AlarmEditPage.
+                    // 1. Copy values to AlarmModel.BindableAlarmRecord
+                    AlarmModel.BindableAlarmRecord.DeepCopy((AlarmRecord)o);
+                    // 2. Apply values to EditPage (at first, create UI. next time, just show it)
+                    alarmEditPage?.Update((AlarmRecord)o);
+                    return AlarmPageController.alarmEditPage ?? (alarmEditPage = new AlarmEditPage(o as AlarmRecord));
+                case AlarmPages.SavePoupPage:
+                    return AlarmPageController.savePopupPage ?? (savePopupPage = new SavePopupPage(o as AlarmRecord));
+                default:
+                    return null;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/App.xaml b/Test/Alarm/Alarm/App.xaml
new file mode 100644 (file)
index 0000000..9d7b48a
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<Application
+       x:Class="Alarm.App"
+       xmlns="http://xamarin.com/schemas/2014/forms"
+       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
+       <Application.Resources>
+
+               <!--  Application resource dictionary  -->
+
+       </Application.Resources>
+</Application>
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/App.xaml.cs b/Test/Alarm/Alarm/App.xaml.cs
new file mode 100644 (file)
index 0000000..fd38f3f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 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 Alarm.Implements;
+using Alarm.Models;
+using Alarm.ViewModels;
+using Alarm.Views;
+using System;
+using System.Globalization;
+using Tizen.Applications;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+using Xamarin.Forms.Xaml;
+
+namespace Alarm
+{
+    /// <summary>
+    /// Alarm Application class
+    /// </summary>
+    [XamlCompilation(XamlCompilationOptions.Compile)]
+    public partial class App : Xamarin.Forms.Application
+    {
+        MainPage firstPage;
+
+        public static CultureInfo Culture { get; set; }
+
+        public App()
+        {
+            UpdateLocale();
+
+            InitializeComponent();
+
+            var alarmModel = new AlarmModel();
+            firstPage = (MainPage)AlarmPageController.GetInstance(AlarmPages.MainPage, new MainPageModel());
+            MainPage = new NavigationPage(firstPage);
+        }
+
+        protected override void OnStart()
+        {
+            // Handle when your app starts
+        }
+
+        protected override void OnSleep()
+        {
+            // Handle when your app sleeps
+        }
+
+        protected override void OnResume()
+        {
+            // Handle when your app resumes
+        }
+
+        /// <summary>
+        /// Get the current locale and apply it.
+        /// </summary>
+        public void UpdateLocale()
+        {
+            // determine the correct, supported .NET culture
+            var handler = new LocaleHandler();
+
+            Culture = handler.CurrentCultureInfo;
+            // set the RESX for resource localization
+            Resx.AppResources.Culture = Culture;
+
+            // Whenever language has been changed, CurrentCulture will be updated.
+            MessagingCenter.Subscribe<LocaleHandler, CultureInfo>(this, MessageKeys.LanguageChanged, (obj, culture) =>
+            {
+                Console.WriteLine($"Received LanguageChanged  culture:{culture.ToString()}");
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Controls/ImageButton.cs b/Test/Alarm/Alarm/Controls/ImageButton.cs
new file mode 100644 (file)
index 0000000..e698ee4
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 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.Diagnostics;
+using System.Windows.Input;
+using Xamarin.Forms;
+
+namespace Alarm.Controls
+{
+    /// <summary>
+    /// A element button which is implemented with a custom renderer based on a native image. 
+    /// </summary>
+    public class CustomImageButton : Image
+    {
+        public static readonly BindableProperty BlendColorProperty = BindableProperty.Create("BlendColor", typeof(Color), typeof(CustomImageButton), Color.Default);
+
+        public event EventHandler Clicked;
+
+        public event EventHandler Released;
+
+        /// <summary>
+        /// A color when the button is pressed. 
+        /// </summary>
+        public Color BlendColor
+        {
+            get { return (Color)GetValue(BlendColorProperty); }
+            set { SetValue(BlendColorProperty, value); }
+        }
+
+        /// <summary>
+        /// To broadcast a click event to subscribers
+        /// </summary>
+        public void SendClicked()
+        {
+            Clicked?.Invoke(this, EventArgs.Empty);
+        }
+
+        /// <summary>
+        /// To broadcast a release event to subscribers
+        /// </summary>
+        public void SendReleased()
+        {
+            Released?.Invoke(this, EventArgs.Empty);
+        }
+
+        /// <summary>
+        /// CustomImageButton constructor
+        /// </summary>
+        public CustomImageButton()
+        {
+            BlendColor = Color.FromRgba(255, 255, 255, 100);
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Converters/ItemCountToVisibilityConverter.cs b/Test/Alarm/Alarm/Converters/ItemCountToVisibilityConverter.cs
new file mode 100644 (file)
index 0000000..29aee45
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 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 Alarm.Converters
+{
+    public enum MainPageType
+    {
+        // Empty layout
+        Empty,
+        // ListView
+        ListView,
+        //Action Button
+        Button,
+    }
+
+    /// <summary>
+    /// Converter class
+    /// This class converts bindable source value to proper target value
+    /// Depending on the number of items in ListView, control ListView's visibility
+    /// </summary>
+    class ItemCountToVisibilityConverter : IValueConverter
+    {
+        /// <summary>
+        /// Converting source value to target value
+        /// </summary>
+        /// <param name="value">Source object</param>
+        /// <param name="targetType">The target type to convert</param>
+        /// <param name="parameter">parameter object</param>
+        /// <param name="culture">The culture info</param>
+        /// <returns>Returns converted bool to decide UI widget's visibility</returns>
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            int count = System.Convert.ToInt32(value);
+            MainPageType type = (MainPageType)parameter;
+            bool flag = false;
+            switch (type)
+            {
+                case MainPageType.ListView:
+                case MainPageType.Button:
+                    flag = false;
+                    break;
+                case MainPageType.Empty:
+                default:
+                    flag = true;
+                    break;
+            }
+
+            //Console.WriteLine($"[ItemCountToVisibilityConverter] type:{type}, flag:{flag}, count:{count}");
+            if (count == 0)
+            {
+                return (bool)System.Convert.ToBoolean(flag);
+            }
+
+            return (bool)!System.Convert.ToBoolean(flag);
+        }
+
+        /// <summary>
+        /// Converting back source value to target value
+        /// This method is not being used in this app.
+        /// </summary>
+        /// <param name="value">Source object</param>
+        /// <seealso cref="System.object">
+        /// <param name="targetType">The target type to convert</param>
+        /// <seealso cref="Type">
+        /// <param name="CultureInfo">The culture info</param>
+        /// <seealso cref="CultureInfo">
+        /// <returns>Returns null</returns>
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return null;
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Converters/ScheduledDateTimeToTextConverter.cs b/Test/Alarm/Alarm/Converters/ScheduledDateTimeToTextConverter.cs
new file mode 100644 (file)
index 0000000..c9fe4b0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 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 Alarm.Converters
+{
+    /// <summary>
+    /// Converter class
+    /// This class converts bindable source value to proper target value
+    /// Depending on the Alarm time label's text value
+    /// </summary>
+    class ScheduledDateTimeToTextConverter : IValueConverter
+    {
+        /// <summary>
+        /// Converting source value to target value
+        /// </summary>
+        /// <param name="value">Source object</param>
+        /// <seealso cref="System.object">
+        /// <param name="targetType">The target type to convert</param>
+        /// <seealso cref="Type">
+        /// <param name="CultureInfo">The culture info</param>
+        /// <seealso cref="CultureInfo">
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            DateTime time = (DateTime)value;
+            string text = "";
+            text = time.ToString("hh") + ":" + time.ToString("mm") + " " + time.ToString("tt");
+            //Console.WriteLine($"ScheduledDateTimeToTextConverter {text}");
+            return text;
+        }
+
+        /// <summary>
+        /// Converting back source value to target value
+        /// This method is not being used in this app.
+        /// </summary>
+        /// <param name="value">Source object</param>
+        /// <seealso cref="System.object">
+        /// <param name="targetType">The target type to convert</param>
+        /// <seealso cref="Type">
+        /// <param name="CultureInfo">The culture info</param>
+        /// <seealso cref="CultureInfo">
+        /// <returns>Returns null</returns>
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Implements/AlarmNativeHandler.cs b/Test/Alarm/Alarm/Implements/AlarmNativeHandler.cs
new file mode 100644 (file)
index 0000000..058c5ef
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 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 Alarm.Models;
+using Native = Tizen.Applications;
+using Tizen.Multimedia;
+using Tizen.System;
+using System;
+
+namespace Alarm.Implements
+{
+    /// <summary>
+    /// The Alarm class
+    /// </summary>
+    public static class AlarmNativeHandler
+    {
+        static AudioStreamPolicy audioStreamPolicy;
+        static Feedback feedback;
+        static Player player;
+        static string SoundNotificationUri = "Notification.ogg";
+
+        public static string ResourceDir { get; set; }
+
+        /// <summary>
+        /// Gets system alarm ID from alarm object
+        /// </summary>
+        /// <param name="alarm">AlarmRecord object to get system alarm ID from</param>
+        /// <seealso cref="AlarmRecord">
+        /// <returns> Returns system alarm ID </returns>
+        public static int GetAlarmID(object alarm)
+        {
+            Native.Alarm nativeAlarm = alarm as Native.Alarm;
+            return nativeAlarm.AlarmId;
+        }
+
+        /// <summary>
+        /// Creates a system alarm of AlarmRecord
+        /// </summary>
+        /// <param name="binableAlarmRecord">AlarmRecord object to create a system alarm based on</param>
+        /// <seealso cref="AlarmRecord">
+        /// <returns> Returns native alarm object </returns>
+        public static int CreateAlarm(AlarmRecord record)
+        {
+            Native.AppControl appControl = new Native.AppControl()
+            {
+                ApplicationId = Native.Application.Current.ApplicationInfo.ApplicationId
+            };
+            appControl.ExtraData.Add("AlarmRecord.UniqueIdentifier", record.GetUniqueIdentifier());
+            //temporary set AllDays , After implement select day of week. additional implementation should be add.
+            Native.Alarm nativeAlarm = Native.AlarmManager.CreateAlarm(record.ScheduledDateTime, Native.AlarmWeekFlag.AllDays, appControl);
+            Console.WriteLine("@@ [Alarm.CreateAlarm] UID : " + record.GetUniqueIdentifier()
+               + ", ScheduledDateTime : " + record.ScheduledDateTime + " NativeAlarmID : " + nativeAlarm.AlarmId);
+            return nativeAlarm.AlarmId;
+        }
+
+        /// <summary>
+        /// Cancel a system alarm of AlarmRecord
+        /// </summary>
+        /// <param name="binableAlarmRecord">AlarmRecord object relevant to a system alarm to cancel</param>
+        /// <seealso cref="AlarmRecord">
+        public static void DeleteAlarm(AlarmRecord record)
+        {
+            var allAlarms = Native.AlarmManager.GetAllScheduledAlarms();
+            foreach (Native.Alarm item in allAlarms)
+            {
+                if (item.AlarmId == record.NativeAlarmID)
+                {
+                    Console.WriteLine("@@ [Alarm.DeleteAlarm] Cancel NativeAlarmID:" + record.NativeAlarmID + ", UID:" + record.GetUniqueIdentifier());
+                    item.Cancel();
+                    record.NativeAlarmID = 0;
+                    break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Update the system alarm
+        /// </summary>
+        /// <param name="record">AlarmRecord</param>
+        public static void UpdateAlarm(ref AlarmRecord record)
+        {
+            DeleteAlarm(record);
+            record.NativeAlarmID = CreateAlarm(record);
+        }
+
+        /// <summary>
+        /// Starts to vibrate
+        /// </summary>
+        public static void StartVibration()
+        {
+            feedback = new Feedback();
+            if (feedback.IsSupportedPattern(FeedbackType.Vibration, "Timer"))
+            {
+                feedback.Play(FeedbackType.Vibration, "Timer");
+            }
+        }
+
+        /// <summary>
+        /// Stops to vibrate
+        /// </summary>
+        public static void StopVibration()
+        {
+            if (feedback != null)
+            {
+                feedback.Stop();
+            }
+        }
+
+        /// <summary>
+        /// Asynchronously plays ringing sound
+        /// </summary>
+        async public static void PlaySound()
+        {
+            if (player == null)
+            {
+                audioStreamPolicy = new AudioStreamPolicy(AudioStreamType.Alarm);
+                audioStreamPolicy.AcquireFocus(AudioStreamFocusOptions.Playback, AudioStreamBehaviors.Fading, null);
+
+                player = new Player();
+                string uri = ResourceDir + SoundNotificationUri;
+                MediaUriSource soudSource = new MediaUriSource(uri);
+                player.SetSource(soudSource);
+
+                player.ApplyAudioStreamPolicy(audioStreamPolicy);
+                await player.PrepareAsync();
+                player.IsLooping = true;
+                player.Volume = 1;
+                if (player.State == PlayerState.Ready)
+                {
+                    player.Start();
+                }
+            }
+        }
+
+        public static void PauseSound()
+        {
+            if (player.State == PlayerState.Playing)
+            {
+                player.Stop();
+            }
+        }
+
+        public static void ResumeSound()
+        {
+            if (player.State == PlayerState.Paused || player.State == PlayerState.Ready)
+            {
+                player.Start();
+            }
+        }
+
+        /// <summary>
+        /// Asynchronously stops ringing
+        /// </summary>
+        public static void StopSound()
+        {
+            if (player != null)
+            {
+                player.Stop();
+                player.Unprepare();
+                audioStreamPolicy.ReleaseFocus(AudioStreamFocusOptions.Playback, AudioStreamBehaviors.Fading, null);
+                player.Dispose();
+                player = null;
+            }
+
+            if (audioStreamPolicy != null)
+            {
+                audioStreamPolicy.Dispose();
+            }
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Implements/AlarmPersistentHandler.cs b/Test/Alarm/Alarm/Implements/AlarmPersistentHandler.cs
new file mode 100644 (file)
index 0000000..e7e7213
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 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.Collections.Generic;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Threading.Tasks;
+using System.Xml;
+using Alarm.Models;
+
+namespace Alarm.Implements
+{
+    /// <summary>
+    /// The Deserializer class to deserialize an AlarmRecord object
+    /// </summary>
+    public static class AlarmPersistentHandler
+    {
+        const string AlarmRecordStoreFile = "AlarmRecordFile.xml";
+
+        /// <summary>
+        /// Deserializes an AlarmRecord object from XML document
+        /// </summary>
+        /// <returns> a collection of <string, AlarmRecord></string> </returns>
+        public static IDictionary<string, AlarmRecord> DeserializeAlarmRecord()
+        {
+            Console.WriteLine("DeserializeAlarmRecord");
+            Stream alarmFileStream = null;
+            var fileOpener = new FileOpener();
+            alarmFileStream = fileOpener.OpenFile(AlarmRecordStoreFile, System.IO.FileMode.OpenOrCreate);
+            if (alarmFileStream.Length == 0)
+            {
+                return null;
+            }
+
+            using (XmlDictionaryReader alarmReader = XmlDictionaryReader.CreateBinaryReader(alarmFileStream, XmlDictionaryReaderQuotas.Max))
+            {
+                var dataContractSerializer = new DataContractSerializer(typeof(Dictionary<string, AlarmRecord>));
+                Object ret1 = dataContractSerializer.ReadObject(alarmReader);
+                alarmFileStream = null;
+                return (IDictionary<string, AlarmRecord>)ret1;
+            }
+        }
+
+        /// <summary>
+        /// Serializes an AlarmRecord object to XML document
+        /// </summary>
+        /// <param name="properties">AlarmRecordDictionary object to serialize</param>
+        /// <seealso cref="IDictionary<string, AlarmRecord>">
+        /// <returns> Task return type </returns>
+        public static Task SerializeAlarmRecordAsync(IDictionary<string, AlarmRecord> properties)
+        {
+            Console.WriteLine("SerializeAlarmRecordAsync");
+            //properties = new Dictionary<string, object>(properties);
+            // Serialize property dictionary to local storage
+            // Make sure to use Internal
+            return Task.Run(() =>
+            {
+                Stream alarmFileStream = null;
+                var fileOpener = new FileOpener();
+                try
+                {
+                    alarmFileStream = fileOpener.OpenFile(AlarmRecordStoreFile, System.IO.FileMode.Create);
+                    using (XmlDictionaryWriter alarmWriter = XmlDictionaryWriter.CreateBinaryWriter(alarmFileStream))
+                    {
+                        var dataContractSerializer = new DataContractSerializer(typeof(Dictionary<string, AlarmRecord>));
+                        dataContractSerializer.WriteObject(alarmWriter, properties);
+                        alarmFileStream = null;
+                        alarmWriter.Flush();
+                    }
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("[Alarm serialization error] " + e.Message);
+                }
+                finally
+                {
+                    if (alarmFileStream != null)
+                    {
+                        alarmFileStream.Dispose();
+                    }
+                }
+
+                return;
+            });
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Implements/FileOpener.cs b/Test/Alarm/Alarm/Implements/FileOpener.cs
new file mode 100644 (file)
index 0000000..40641b3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 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.IO;
+using TApplication = Tizen.Applications.Application;
+
+namespace Alarm.Implements
+{
+    /// <summary>
+    /// The FileAccessor class
+    /// </summary>
+    class FileOpener
+    {
+        string _path;
+
+        internal FileOpener()
+        {
+            _path = TApplication.Current.DirectoryInfo.Data;
+        }
+
+        public Stream OpenFile(string filePath, System.IO.FileMode fileMode)
+        {
+            if (filePath == null || filePath.Trim().Length == 0)
+            {
+                throw new ArgumentNullException("File path arguments are invalid.");
+            }
+
+            return new FileStream(Path.Combine(_path, filePath), fileMode, System.IO.FileAccess.ReadWrite, System.IO.FileShare.Read);
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Implements/LocaleHandler.cs b/Test/Alarm/Alarm/Implements/LocaleHandler.cs
new file mode 100644 (file)
index 0000000..9a4ba66
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 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 Tizen.System;
+using System.Threading;
+using Xamarin.Forms;
+using Alarm.Models;
+using Xamarin.Forms.Platform.Tizen;
+
+namespace Alarm.Implements
+{
+    public class LocaleHandler
+    {
+        CultureInfo _currentCultureInfo;
+
+        public LocaleHandler()
+        {
+            _currentCultureInfo = GetCurrentCultureInfo();
+
+            // To get notified when system locale settings has been changed
+            SystemSettings.LocaleLanguageChanged += OnLanguageChanged;
+        }
+
+        /// <summary>
+        /// Get the current CultureInfo
+        /// </summary>
+        public CultureInfo CurrentCultureInfo
+        {
+            get
+            {
+                return _currentCultureInfo;
+            }
+        }
+
+        /// <summary>
+        /// Set the current CultureInfo
+        /// </summary>
+        /// <param name="info">CultureInfo</param>
+        public void SetLocale(CultureInfo info)
+        {
+            Thread.CurrentThread.CurrentCulture = info;
+            Thread.CurrentThread.CurrentUICulture = info;
+        }
+
+        /// <summary>
+        /// Get CultureInfo from the locale information
+        /// </summary>
+        /// <returns>CultureInfo</returns>
+        CultureInfo GetCurrentCultureInfo()
+        {
+            var netLanguage = "en";
+            var TizenLocale = SystemSettings.LocaleLanguage;
+
+            netLanguage = TizenToDotnetLanguage(TizenLocale.ToString().Replace("_", "-"));
+            CultureInfo info = null;
+            try
+            {
+                info = new CultureInfo(netLanguage);
+            }
+            catch (CultureNotFoundException e1)
+            {
+                Console.WriteLine($"cannot find the current cultureInfo. so use 'en'. {e1.Message}");
+                info = new CultureInfo("en");
+            }
+
+            return info;
+        }
+
+        /// <summary>
+        /// Invoked when system locale setting has been changed
+        /// </summary>
+        /// <param name="sender">object</param>
+        /// <param name="e">LocaleLanguageChangedEventArgs</param>
+        private void OnLanguageChanged(object sender, LocaleLanguageChangedEventArgs e)
+        {
+            Console.WriteLine($"OnLanguageChanged");
+            CultureInfo info = GetCurrentCultureInfo();
+            _currentCultureInfo = info;
+            SetLocale(info);
+
+            // set the RESX for resource localization
+            Resx.AppResources.Culture = info;
+
+            // Notify the change of locale information
+            MessagingCenter.Send<LocaleHandler,CultureInfo>(this, MessageKeys.LanguageChanged, info);
+        }
+
+        string TizenToDotnetLanguage(string tizenLanguage)
+        {
+            var netLanguage = tizenLanguage;
+            //certain languages need to be converted to CultureInfo equivalent
+            switch (tizenLanguage)
+            {
+                case "zh-CN":   // Chinese Simplified (People's Republic of China)
+                    netLanguage = "zh-Hans"; // correct code for .NET
+                    break;
+                case "zh-HK":  // Chinese Traditional (Hong Kong)
+                case "zh-hk":
+                case "zh-tw":  // Chinese Traditional (Taiwan)
+                case "zh-TW":
+                    netLanguage = "zh-Hant"; // correct code for .NET
+                    break;
+            }
+
+            //Console.WriteLine($".NET Language/Locale:{netLanguage}");
+            return netLanguage;
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Models/AlarmModel.cs b/Test/Alarm/Alarm/Models/AlarmModel.cs
new file mode 100644 (file)
index 0000000..2c24e6f
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 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.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using Alarm.Implements;
+
+namespace Alarm.Models
+{
+    /// <summary>
+    /// AlarmModel class
+    /// </summary>
+    public class AlarmModel : INotifyPropertyChanged
+    {
+        /// <summary>
+        /// Gets/Sets AlarmRecordDictionary
+        /// </summary>
+        public static IDictionary<string, AlarmRecord> AlarmRecordDictionary { get; set; }
+
+        /// <summary>
+        /// ObservableAlarmList property
+        /// </summary>
+        public static ObservableCollection<AlarmRecord> ObservableAlarmList { get; set; }
+
+        /// <summary>
+        /// This static field holds current alarm record which is being displayed for alarm info input.
+        /// Through this record, edit view is automatically updated when users select certain option in sub pages.
+        /// </summary>
+        public static AlarmRecord BindableAlarmRecord { get; set; }
+
+        /// <summary>
+        /// AlarmModel constructor
+        /// </summary>
+        public AlarmModel()
+        {
+            ObservableAlarmList = new ObservableCollection<AlarmRecord>();
+            AlarmRecordDictionary = AlarmPersistentHandler.DeserializeAlarmRecord();
+            BindableAlarmRecord = new AlarmRecord();
+            if (AlarmRecordDictionary == null)
+            {
+                AlarmRecordDictionary = new Dictionary<string, AlarmRecord>();
+            }
+            else
+            {
+                foreach (var alarmItem in AlarmRecordDictionary)
+                {
+                    Console.WriteLine("alarmItem key:" + alarmItem.Key);
+                    // Key is DateCreated
+                    var keyString = alarmItem.Key;
+                    // Retrieve alarm record
+                    AlarmRecord retrieved = alarmItem.Value;
+                    Console.WriteLine("retrieved:" + retrieved.ToString());
+                    // Add the retrieved alarm to list for AlarmListUI
+                    ObservableAlarmList.Add(retrieved);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Update alarm record
+        /// </summary>
+        /// <param name="record">AlarmRecord</param>
+        public static void UpdateAlarm(AlarmRecord record)
+        {
+            var obj = record;
+            // Update native alarm
+            AlarmNativeHandler.UpdateAlarm(ref obj);
+            record = obj;
+            string alarmUID = record.GetUniqueIdentifier();
+
+            // Update list
+            for (int i = 0; i < ObservableAlarmList.Count; i++)
+            {
+                AlarmRecord item = ObservableAlarmList[i];
+                if (item.GetUniqueIdentifier() == alarmUID)
+                {
+                    Console.WriteLine("Found AlarmRecord(UID: " + item.GetUniqueIdentifier() + ") in ObservableAlarmList.");
+                    AlarmRecordDictionary.Remove(alarmUID);
+                    item.DeepCopy(record);
+                    AlarmRecordDictionary.Add(alarmUID, item);
+                    SaveDictionary();
+                    break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Create native alarm
+        /// </summary>
+        public static void CreateAlarm()
+        {
+            // create a native alarm using AlarmModel.BindableAlarmRecord
+            // After then, update Native alarm ID.
+            BindableAlarmRecord.NativeAlarmID = AlarmNativeHandler.CreateAlarm(BindableAlarmRecord);
+
+            AlarmRecord record = new AlarmRecord();
+            record.DeepCopy(BindableAlarmRecord);
+            ObservableAlarmList.Add(record);
+            AlarmRecordDictionary.Add(record.GetUniqueIdentifier(), record);
+            SaveDictionary();
+        }
+
+        /// <summary>
+        /// Cancel native alarm
+        /// and then remove AlarmRecord from ObservableAlarmList and AlarmRecordDictionary
+        /// </summary>
+        /// <param name="alarm">AlarmRecord</param>
+        public static void DeleteAlarm(AlarmRecord alarm)
+        {
+            Console.WriteLine("DeleteAlarm:" + alarm.ToString());
+            // Cancel the native alarm
+            AlarmNativeHandler.DeleteAlarm(alarm);
+            string UID = alarm.GetUniqueIdentifier();
+            // Delete alarm from dictionary
+            AlarmModel.AlarmRecordDictionary.Remove(UID);
+            // Delete alarm from List
+            for (int i = AlarmModel.ObservableAlarmList.Count - 1; i >= 0; i--)
+            {
+                if (AlarmModel.ObservableAlarmList[i].GetUniqueIdentifier() == UID)
+                {
+                    ObservableAlarmList.RemoveAt(i);
+                    break;
+                }
+            }
+
+            AlarmModel.SaveDictionary();
+        }
+
+        /// <summary>
+        /// Cancel native alarm
+        /// and then deactivate AlarmRecord from AlarmRecordDictionary
+        /// </summary>
+        /// <param name="record">AlarmRecord</param>
+        public static void DeactivatelAlarm(AlarmRecord record)
+        {
+            //1. cancel native alarm
+            AlarmNativeHandler.DeleteAlarm(record);
+            //2. Make AlarmRecord's AlarmState
+            string UID = record.GetUniqueIdentifier();
+            UpdateDictionaryAndList(UID, 0, false);
+        }
+
+        /// <summary>
+        /// Reactivate AlarmRecord
+        /// </summary>
+        /// <param name="record">AlarmRecord for reactivating</param>
+        public static void ReactivatelAlarm(AlarmRecord record)
+        {
+            //1. register native alarm
+            int NativeAlarmID = AlarmNativeHandler.CreateAlarm(record);
+            //2. Update NativeAlarmID and AlarmState
+            string UID = record.GetUniqueIdentifier();
+        }
+
+        /// <summary>
+        /// Update AlarmRecord Dictionary and ObservableAlarmList
+        /// </summary>
+        /// <param name="UID">AlarmRecord unique ID</param>
+        /// <param name="NativeAlarmID">Native AlarmID</param>
+        /// <param name="active">active state of AlarmRecord</param>
+        private static void UpdateDictionaryAndList(string UID, int NativeAlarmID, bool active)
+        {
+            for (int i = 0; i < AlarmModel.ObservableAlarmList.Count; i++)
+            {
+                AlarmRecord item = AlarmModel.ObservableAlarmList[i];
+                if (item.GetUniqueIdentifier() == UID)
+                {
+                    AlarmModel.AlarmRecordDictionary.Remove(UID);
+                    item.NativeAlarmID = NativeAlarmID;
+                    if (active)
+                    {
+                        item.AlarmState = AlarmStates.Active;
+                    }
+                    else
+                    {
+                        item.AlarmState = AlarmStates.Inactive;
+                    }
+
+                    AlarmModel.AlarmRecordDictionary.Add(UID, item);
+                    AlarmModel.SaveDictionary();
+                    break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Serializes alarm record dictionary object and save it to file
+        /// </summary>
+        public static void SaveDictionary()
+        {
+           AlarmPersistentHandler.SerializeAlarmRecordAsync(AlarmModel.AlarmRecordDictionary).Wait();
+        }
+
+        ///<summary>
+        ///Event that is raised when the properties of AlarmRecord change
+        ///</summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        /// <summary>
+        /// When property is changed, registered event callback is invoked
+        /// </summary>
+        /// <param name="propertyName">Property name of the change event occured</param>
+        protected virtual void OnPropertyChanged(string propertyName)
+        {
+            if (PropertyChanged != null)
+            {
+                PropertyChanged(this,
+                    new PropertyChangedEventArgs(propertyName));
+            }
+        }
+
+        /// <summary>
+        /// PrintAll method for debugging.
+        /// </summary>
+        /// <param name="methodName">method name for printing</param>
+        public static void PrintAll(string methodName)
+        {
+            Console.WriteLine("");
+            Console.WriteLine("+++++++++++++++      PrintAll      ++++++++++");
+            Console.WriteLine("  START " + methodName);
+            if (AlarmModel.AlarmRecordDictionary != null)
+            {
+                Console.WriteLine("   AlarmRecordDictionary");
+                foreach (var item in AlarmModel.AlarmRecordDictionary)
+                {
+                    Console.WriteLine(" Key: " + item.Key + ", Value:" + item.Value.ToString());
+                }
+            }
+
+            Console.WriteLine("");
+            int count = 1;
+            if (AlarmModel.ObservableAlarmList != null)
+            {
+                Console.WriteLine("   ObservableAlarmList");
+                foreach (AlarmRecord item in AlarmModel.ObservableAlarmList)
+                {
+                    Console.WriteLine(count++ + ")" + item.ToString());
+                }
+            }
+
+            Console.WriteLine("");
+            count = 1;
+            Console.WriteLine("  END   " + methodName);
+        }
+
+        /// <summary>
+        /// Determines whether ObservableAlarmList contains an alarm which is scheduled at the same time.
+        /// </summary>
+        /// <param name="duplicate">AlarmRecord</param>
+        /// <returns> true if a same alarm is found in the list; otherwise, false.</returns>
+        public static bool CheckAlarmExist(ref AlarmRecord duplicate)
+        {
+            foreach (AlarmRecord item in AlarmModel.ObservableAlarmList)
+            {
+                // Check scheduled time and alarm name
+                if (item.ScheduledDateTime.Equals(AlarmModel.BindableAlarmRecord.ScheduledDateTime))
+                {
+                    // a same alarm is found.
+                    duplicate.DeepCopy(item);
+                    return true;
+                }
+            }
+            // there's no same alarm.
+            duplicate = null;
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Models/AlarmRecord.cs b/Test/Alarm/Alarm/Models/AlarmRecord.cs
new file mode 100644 (file)
index 0000000..b18ce11
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 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;
+
+namespace Alarm.Models
+{
+    [Flags]
+    public enum AlarmWeekFlag
+    {
+        /// <summary>
+        /// Identifier for Never
+        /// </summary>
+        Never = 0x00,
+
+        /// <summary>
+        /// Identifier for Sunday.
+        /// </summary>
+        Sunday = 0x01,
+
+        /// <summary>
+        /// Identifier for Monday.
+        /// </summary>
+        Monday = 0x02,
+
+        /// <summary>
+        /// Identifier for Tuesday.
+        /// </summary>
+        Tuesday = 0x04,
+
+        /// <summary>
+        /// Identifier for Wednesday.
+        /// </summary>
+        Wednesday = 0x08,
+
+        /// <summary>
+        /// Identifier for Thursday.
+        /// </summary>
+        Thursday = 0x10,
+
+        /// <summary>
+        /// Identifier for Friday.
+        /// </summary>
+        Friday = 0x20,
+
+        /// <summary>
+        /// Identifier for Saturday.
+        /// </summary>
+        Saturday = 0x40,
+
+        /// <summary>
+        /// All Days of the Week.
+        /// </summary>
+        AllDays = Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday,
+
+        /// <summary>
+        /// Only Weekdays
+        /// </summary>
+        WeekDays = Monday | Tuesday | Wednesday | Thursday | Friday
+    }
+
+    /// <summary>
+    /// The enumeration of AlarmStates
+    /// </summary>
+    public enum AlarmStates
+    {
+        /// <summary>
+        /// Identifier for Snooze
+        /// </summary>
+        Snooze,
+        /// <summary>
+        /// Identifier for Active
+        /// </summary>
+        Active,
+        /// <summary>
+        /// Identifier for Inactive
+        /// </summary>
+        Inactive,
+    }
+
+    public class AlarmRecord : INotifyPropertyChanged
+    {
+        private DateTime _scheduledDateTime;
+        /// <summary>
+        /// Gets/Sets ScheduledDateTime
+        /// </summary>
+        public DateTime ScheduledDateTime
+        {
+            get
+            {
+                return _scheduledDateTime;
+            }
+
+            set
+            {
+                if (_scheduledDateTime != value)
+                {
+                    _scheduledDateTime = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ScheduledDateTime"));
+                }
+            }
+        }
+
+        private AlarmWeekFlag _weekFlag;
+        /// <summary>
+        /// Gets/Sets the day of week when an alarm recurs
+        /// </summary>
+        public AlarmWeekFlag WeekFlag
+        {
+            get
+            {
+                return _weekFlag;
+            }
+
+            set
+            {
+                if (_weekFlag != value)
+                {
+                    _weekFlag = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("WeekFlag"));
+                }
+            }
+        }
+
+        private AlarmStates _alarmstate;
+        /// <summary>
+        /// Gets/Sets the state of alarm
+        /// </summary>
+        public AlarmStates AlarmState
+        {
+            get { return _alarmstate; }
+            set
+            {
+                if (_alarmstate != value)
+                {
+                    _alarmstate = value;
+                    OnPropertyChanged("AlarmState");
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets/Sets alarm creation time
+        /// It is used as a Key in AlarmRecordDictionary.
+        /// </summary>
+        public TimeSpan DateCreated { get; set; }
+
+        /// <summary>
+        /// Gets/Sets system alarm ID
+        /// </summary>
+        public int NativeAlarmID { get; set; }
+
+
+        private bool _isSerialized;
+
+        /// <summary>
+        /// Gets/Sets whether the AlarmRecord object has been serialized
+        /// This indicates whether it is newly created or not.
+        /// </summary>
+        public bool IsSerialized
+        {
+            get
+            {
+                return _isSerialized;
+            }
+
+            set
+            {
+                if (_isSerialized != value)
+                {
+                    _isSerialized = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsSerialized"));
+                }
+            }
+        }
+
+        ///<summary>
+        ///Event that is raised when the properties of AlarmRecord change
+        ///</summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        /// <summary>
+        /// Initializes a new instance of the AlarmRecord class
+        /// </summary>
+        public AlarmRecord()
+        {
+        }
+
+        /// <summary>
+        /// Converts the string of the creation date time
+        /// </summary>
+        /// <returns> Returns a string representation of value of the alarm creation time</returns>
+        public string GetUniqueIdentifier()
+        {
+            return DateCreated.ToString();
+        }
+
+        /// <summary>
+        /// Return a string that represents the current AlarmRecord object.
+        /// </summary>
+        /// <returns>string</returns>
+        public override string ToString()
+        {
+            return "Alarm " + ScheduledDateTime
+                + ", NativeID[" + NativeAlarmID + "]"
+                + ", (" + WeekFlag + ")"
+                + ", (" + DateCreated + ")"
+                + ", (" + AlarmState + ")"
+                + ", (" + IsSerialized + ")";
+        }
+
+        /// <summary>
+        /// Sets the default value for AlarmRecord object
+        /// </summary>
+        public void SetDefault()
+        {
+            /// Sets current time to scheduled date time (Alarm creation time)
+            ScheduledDateTime = System.DateTime.Now;
+
+            /// Sets Never to Repeat weekday flag
+            WeekFlag = AlarmWeekFlag.AllDays;
+
+            /// This field is used to uniquely identify alarm by created time
+            DateCreated = ScheduledDateTime.TimeOfDay;
+
+            AlarmState = AlarmStates.Active;
+
+            /// This indicates whether it is newly created or not.
+            /// This value should set to false at default creation time
+            IsSerialized = false;
+        }
+
+        /// <summary>
+        /// Deep copy alarm record
+        /// </summary>
+        /// <param name="bindableAlarmRecord">Alarm record to deep copy</param>
+        /// <seealso cref="AlarmRecord">
+        public void DeepCopy(AlarmRecord Record, bool includeUID = true)
+        {
+            // All properties should be copied to this alarm record object
+            ScheduledDateTime = Record.ScheduledDateTime;
+            WeekFlag = Record.WeekFlag;
+            AlarmState = Record.AlarmState;
+            IsSerialized = Record.IsSerialized;
+            if (includeUID)
+            {
+                DateCreated = Record.DateCreated;
+                NativeAlarmID = Record.NativeAlarmID;
+            }
+        }
+
+        /// <summary>
+        /// When property is changed, registered event callback is invoked
+        /// </summary>
+        /// <param name="propertyName">Property name of the change event occured</param>
+        protected virtual void OnPropertyChanged(string propertyName)
+        {
+            if (PropertyChanged != null)
+            {
+                PropertyChanged(this,
+                    new PropertyChangedEventArgs(propertyName));
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Models/MessageKeys.cs b/Test/Alarm/Alarm/Models/MessageKeys.cs
new file mode 100644 (file)
index 0000000..9d5f905
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 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 Alarm.Models
+{
+    public class MessageKeys
+    {
+        // Notify that language has been changed
+        public const string LanguageChanged = "LanguageChanged";
+    }
+}
diff --git a/Test/Alarm/Alarm/Renderers/ImageButtonRenderer.cs b/Test/Alarm/Alarm/Renderers/ImageButtonRenderer.cs
new file mode 100644 (file)
index 0000000..b6d23d5
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 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 ElmSharp;
+using Xamarin.Forms.Platform.Tizen;
+
+using Alarm.Controls;
+using Alarm.Tizen.Renderers;
+using Xamarin.Forms;
+
+[assembly: ExportRenderer(typeof(CustomImageButton), typeof(Alarm.Tizen.Renderers.CustomImageButtonRenderer))]
+namespace Alarm.Tizen.Renderers
+{
+    /// <summary>
+    /// Calculator element (Operator, numbers) button custom renderer
+    /// Actually to implement command button, A image is used instead a button to display as a calculator button.
+    /// </summary>
+    /// <remarks>
+    /// Please refer to Xamarin Custom Renderer
+    /// https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/
+    /// </remarks>
+    class CustomImageButtonRenderer : ImageRenderer//ViewRenderer<Xamarin.Forms.Image, Image>
+    {
+        /// <summary>
+        /// Tizen's gesture recognizer for Tap gesture, Long Tap gesture, Line gesture and so on.
+        /// </summary>
+        private ElmSharp.GestureLayer GestureRecognizer;
+
+        /// <summary>
+        /// A flags that indicates whether the touch is handled or not.
+        /// </summary>
+        private volatile bool isTouched;
+
+        public CustomImageButtonRenderer()
+        {
+            RegisterPropertyHandler(CustomImageButton.BlendColorProperty, UpdateBlendColor);
+        }
+
+        /// <summary>
+        /// Making a button with a image and set the image's color and blending color by inputted background color.
+        /// Register touch event callback for the Tap, the Long Tap and the Line behavior. </summary>
+        /// <remarks>
+        /// When the button is touched, This class should change the image for each touch down/up situation.
+        /// Even a button touching  starts at the Tap touch down, but touch up will be happen in several situations such as the Tap, the Long Tap, the Line.
+        /// </remarks>
+        /// <param name="args"> A Image element changed event's argument </param>
+        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Image> args)
+        {
+            base.OnElementChanged(args);
+
+            if (Control == null)
+            {
+                return;
+            }
+
+            if (GestureRecognizer == null)
+            {
+                GestureRecognizer = new ElmSharp.GestureLayer(Control);
+                GestureRecognizer.Attach(Control);
+            }
+
+            if (args.NewElement == null)
+            {
+                GestureRecognizer.ClearCallbacks();
+                return;
+            }
+            else
+            {
+                Control.Clicked += SendClicked;
+            }
+
+            CustomImageButton BtnElement = args.NewElement as CustomImageButton;
+            if (BtnElement == null)
+            {
+                return;
+            }
+
+            GestureRecognizer.SetTapCallback(ElmSharp.GestureLayer.GestureType.Tap, ElmSharp.GestureLayer.GestureState.Start, x =>
+            {
+                KeyDown();
+            });
+            GestureRecognizer.SetTapCallback(ElmSharp.GestureLayer.GestureType.Tap, ElmSharp.GestureLayer.GestureState.End, x =>
+            {
+                KeyUp();
+            });
+            GestureRecognizer.SetTapCallback(ElmSharp.GestureLayer.GestureType.LongTap, ElmSharp.GestureLayer.GestureState.End, x =>
+            {
+                KeyUp();
+            });
+            GestureRecognizer.SetTapCallback(ElmSharp.GestureLayer.GestureType.LongTap, ElmSharp.GestureLayer.GestureState.Abort, x =>
+            {
+                KeyUp();
+            });
+
+        }
+
+        /// <summary>
+        /// A Action delegate which is restore button image as default
+        /// and execute button's Command with CommandParameter. </summary>
+        private void KeyUp()
+        {
+            if (Control == null)
+            {
+                return;
+            }
+
+            if (isTouched)
+            {
+                //invoke click event when a user releases image button.
+                ((CustomImageButton)Element).SendReleased();
+            }
+
+            Control.Color = ElmSharp.Color.Default;
+            isTouched = false;
+        }
+
+        /// <summary>
+        /// A Action delegate which is restore button image as pressed situation. </summary>
+        private void KeyDown()
+        {
+            CustomImageButton BtnElement = Element as CustomImageButton;
+
+            if (BtnElement == null ||
+                Control == null)
+            {
+                return;
+            }
+
+            Control.Color = BtnElement.BlendColor.ToNative();
+            isTouched = true;
+        }
+
+        private void UpdateBlendColor(bool obj)
+        {
+            CustomImageButton BtnElement = Element as CustomImageButton;
+            Control.Color = BtnElement.BlendColor.ToNative();
+        }
+
+        /// <summary>
+        /// To send a click event to the element
+        /// </summary>
+        /// <param name="sender">sender</param>
+        /// <param name="e">event</param>
+        void SendClicked(object sender, EventArgs e)
+        {
+            ((CustomImageButton)Element).SendClicked();
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Resx/AppResources.Designer.cs b/Test/Alarm/Alarm/Resx/AppResources.Designer.cs
new file mode 100644 (file)
index 0000000..7157d8d
--- /dev/null
@@ -0,0 +1,180 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Alarm.Resx {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    public class AppResources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal AppResources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        public static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Alarm.Resx.AppResources", typeof(AppResources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        public static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Add.
+        /// </summary>
+        public static string Add {
+            get {
+                return ResourceManager.GetString("Add", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Add alarm.
+        /// </summary>
+        public static string AddAlarm {
+            get {
+                return ResourceManager.GetString("AddAlarm", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to after.
+        /// </summary>
+        public static string After {
+            get {
+                return ResourceManager.GetString("After", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Alarm.
+        /// </summary>
+        public static string Alarm {
+            get {
+                return ResourceManager.GetString("Alarm", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Alarm set.
+        /// </summary>
+        public static string AlarmSet {
+            get {
+                return ResourceManager.GetString("AlarmSet", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Dismiss.
+        /// </summary>
+        public static string Dismiss {
+            get {
+                return ResourceManager.GetString("Dismiss", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to from now.
+        /// </summary>
+        public static string FromNow {
+            get {
+                return ResourceManager.GetString("FromNow", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to one hour.
+        /// </summary>
+        public static string Hour {
+            get {
+                return ResourceManager.GetString("Hour", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to hours.
+        /// </summary>
+        public static string Hours {
+            get {
+                return ResourceManager.GetString("Hours", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to one minute.
+        /// </summary>
+        public static string Minute {
+            get {
+                return ResourceManager.GetString("Minute", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to minutes.
+        /// </summary>
+        public static string Minutes {
+            get {
+                return ResourceManager.GetString("Minutes", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to now.
+        /// </summary>
+        public static string Now {
+            get {
+                return ResourceManager.GetString("Now", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Set alarm.
+        /// </summary>
+        public static string SetAlarm {
+            get {
+                return ResourceManager.GetString("SetAlarm", resourceCulture);
+            }
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Resx/AppResources.ko.resx b/Test/Alarm/Alarm/Resx/AppResources.ko.resx
new file mode 100644 (file)
index 0000000..8a399ba
--- /dev/null
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Add" xml:space="preserve">
+    <value>추가</value>
+    <comment>Add button text</comment>
+  </data>
+  <data name="AddAlarm" xml:space="preserve">
+    <value>알람 추가</value>
+    <comment>Add alarm text</comment>
+  </data>
+  <data name="After" xml:space="preserve">
+    <value>후</value>
+  </data>
+  <data name="Alarm" xml:space="preserve">
+    <value>알람</value>
+    <comment>Alarm List header label text</comment>
+  </data>
+  <data name="AlarmSet" xml:space="preserve">
+    <value>알람 설정</value>
+    <comment>Alarm set</comment>
+  </data>
+  <data name="Dismiss" xml:space="preserve">
+    <value>해제</value>
+    <comment>Dismiss text</comment>
+  </data>
+  <data name="FromNow" xml:space="preserve">
+    <value>지금부터</value>
+  </data>
+  <data name="Hour" xml:space="preserve">
+    <value>1 시간</value>
+  </data>
+  <data name="Hours" xml:space="preserve">
+    <value>시간</value>
+  </data>
+  <data name="Minute" xml:space="preserve">
+    <value>1 분</value>
+  </data>
+  <data name="Minutes" xml:space="preserve">
+    <value>분</value>
+  </data>
+  <data name="Now" xml:space="preserve">
+    <value>현재시간으로</value>
+  </data>
+  <data name="SetAlarm" xml:space="preserve">
+    <value>알람 설정</value>
+    <comment>EditAlarm header text</comment>
+  </data>
+</root>
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Resx/AppResources.resx b/Test/Alarm/Alarm/Resx/AppResources.resx
new file mode 100644 (file)
index 0000000..a9ca025
--- /dev/null
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Add" xml:space="preserve">
+    <value>Add</value>
+    <comment>Add button text</comment>
+  </data>
+  <data name="AddAlarm" xml:space="preserve">
+    <value>Add alarm</value>
+    <comment>Add alarm text</comment>
+  </data>
+  <data name="After" xml:space="preserve">
+    <value>after</value>
+  </data>
+  <data name="Alarm" xml:space="preserve">
+    <value>Alarm</value>
+    <comment>Alarm List header label text</comment>
+  </data>
+  <data name="AlarmSet" xml:space="preserve">
+    <value>Alarm set</value>
+    <comment>Alarm set</comment>
+  </data>
+  <data name="Dismiss" xml:space="preserve">
+    <value>Dismiss</value>
+    <comment>Dismiss text</comment>
+  </data>
+  <data name="FromNow" xml:space="preserve">
+    <value>from now</value>
+  </data>
+  <data name="Hour" xml:space="preserve">
+    <value>one hour</value>
+  </data>
+  <data name="Hours" xml:space="preserve">
+    <value>hours</value>
+  </data>
+  <data name="Minute" xml:space="preserve">
+    <value>one minute</value>
+  </data>
+  <data name="Minutes" xml:space="preserve">
+    <value>minutes</value>
+  </data>
+  <data name="Now" xml:space="preserve">
+    <value>now</value>
+  </data>
+  <data name="SetAlarm" xml:space="preserve">
+    <value>Set alarm</value>
+    <comment>EditAlarm header text</comment>
+  </data>
+</root>
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/ViewModels/AlertPageModel.cs b/Test/Alarm/Alarm/ViewModels/AlertPageModel.cs
new file mode 100644 (file)
index 0000000..a12000f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 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 Alarm.Implements;
+using Alarm.Models;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Alarm.ViewModels
+{
+    public enum SoundState
+    {
+        Stop = 0,
+        Start = 1,
+        Pause = 2,
+    }
+
+    public class AlertPageModel : BasePageModel
+    {
+        private AlarmRecord _alarmRecord;
+
+        private SoundState _alertSoundState;
+
+        /// <summary>
+        /// Alarm record to display in Alert Page
+        /// </summary>
+        public AlarmRecord Record
+        {
+            get
+            {
+                return _alarmRecord;
+            }
+
+            set
+            {
+                SetProperty(ref _alarmRecord, value, "Record");
+            }
+        }
+
+        public SoundState AlertSoundState
+        {
+            get
+            {
+                return _alertSoundState;
+            }
+
+            set
+            {
+                SetProperty(ref _alertSoundState, value, "AlertState");
+            }
+
+        }
+        /// <summary>
+        /// AlertPageModel constructor with AlarmRecord
+        /// </summary>
+        /// <param name="record">Record to be shown</param>
+        public AlertPageModel(AlarmRecord record = null)
+        {
+            Record = record;
+        }
+
+        public void StartAlert()
+        {
+            AlarmNativeHandler.PlaySound();
+            AlarmNativeHandler.StartVibration();
+            _alertSoundState = SoundState.Start;
+        }
+
+        public void PauseAlert()
+        {
+            AlarmNativeHandler.PauseSound();
+            AlarmNativeHandler.StopVibration();
+            _alertSoundState = SoundState.Pause;
+        }
+
+        public void ResumeAlert()
+        {
+            AlarmNativeHandler.ResumeSound();
+            _alertSoundState = SoundState.Start;
+        }
+
+        /// <summary>
+        /// Delete current AlarmRecord
+        /// </summary>
+        public void Dismiss()
+        {
+            AlarmNativeHandler.StopSound();
+            AlarmNativeHandler.StopVibration();
+            _alertSoundState = SoundState.Stop;
+
+            if (_alarmRecord !=  null)
+            {
+                AlarmModel.DeleteAlarm(_alarmRecord);
+            }
+        }
+
+    }
+}
diff --git a/Test/Alarm/Alarm/ViewModels/BasePageModel.cs b/Test/Alarm/Alarm/ViewModels/BasePageModel.cs
new file mode 100644 (file)
index 0000000..f911b70
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Alarm.ViewModels
+{
+    public class BasePageModel : INotifyPropertyChanged
+    {
+        protected bool SetProperty<T>(ref T backingStore, T value,
+            [CallerMemberName]string propertyName = "",
+            Action onChanged = null)
+        {
+            if (EqualityComparer<T>.Default.Equals(backingStore, value))
+            {
+                return false;
+            }
+
+            backingStore = value;
+            onChanged?.Invoke();
+            OnPropertyChanged(propertyName);
+            return true;
+        }
+
+        #region INotifyPropertyChanged
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        /// <summary>
+        /// Called to notify that a change of property happened
+        /// </summary>
+        /// <param name="propertyName">The name of the property that changed</param>
+        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
+        {
+            var changed = PropertyChanged;
+            if (changed == null)
+            {
+                return;
+            }
+
+            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
+        }
+        #endregion
+    }
+}
diff --git a/Test/Alarm/Alarm/ViewModels/MainPageModel.cs b/Test/Alarm/Alarm/ViewModels/MainPageModel.cs
new file mode 100644 (file)
index 0000000..c304e07
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 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 Alarm.Models;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+using Xamarin.Forms;
+
+namespace Alarm.ViewModels
+{
+    public class MainPageModel : BasePageModel
+    {
+        /// <summary>
+        /// RecordList to display in MainPage
+        /// </summary>
+        public ObservableCollection<AlarmRecord> RecordList
+        {
+            get
+            {
+                return AlarmModel.ObservableAlarmList;
+            }
+        }
+
+        /// <summary>
+        /// MainPageModel constructor 
+        /// </summary>
+        public MainPageModel()
+        {
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Views/AlarmAlertPage.xaml b/Test/Alarm/Alarm/Views/AlarmAlertPage.xaml
new file mode 100644 (file)
index 0000000..c157f23
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<views:CirclePageEx
+       x:Class="Alarm.Views.AlarmAlertPage"
+       xmlns="http://xamarin.com/schemas/2014/forms"
+       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+       xmlns:converters="clr-namespace:Alarm.Converters"
+       xmlns:resx="clr-namespace:Alarm.Resx"
+       xmlns:views="clr-namespace:Alarm.Views"
+       xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms">
+       <ContentPage.Resources>
+               <ResourceDictionary>
+                       <converters:ScheduledDateTimeToTextConverter x:Key="TimeConverter" />
+               </ResourceDictionary>
+       </ContentPage.Resources>
+       <w:CirclePage.Content>
+               <AbsoluteLayout BackgroundColor="Black">
+                       <Image
+                               x:Name="AlarmRingImg"
+                               AbsoluteLayout.LayoutBounds="133, 66, 94, 94"
+                               AbsoluteLayout.LayoutFlags="None"
+                               Source="alarm_ringing_icon.png" />
+                       <Label
+                               x:Name="TimeLabel"
+                               AbsoluteLayout.LayoutBounds="0, 170, 360, 45"
+                               AbsoluteLayout.LayoutFlags="None"
+                               FontSize="12"
+                               HorizontalTextAlignment="Center"
+                               Text="{Binding Record.ScheduledDateTime, Converter={StaticResource TimeConverter}}"
+                               TextColor="White" />
+               </AbsoluteLayout>
+       </w:CirclePage.Content>
+       <w:CirclePage.ActionButton>
+               <w:ActionButtonItem
+                       x:Name="DismissButton"
+                       Clicked="OnDismissButtonClicked"
+                       Text="{x:Static resx:AppResources.Dismiss}" />
+       </w:CirclePage.ActionButton>
+</views:CirclePageEx>
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/AlarmAlertPage.xaml.cs b/Test/Alarm/Alarm/Views/AlarmAlertPage.xaml.cs
new file mode 100644 (file)
index 0000000..f09794f
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 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 Alarm.Implements;
+using Alarm.Models;
+using Alarm.Resx;
+using Alarm.ViewModels;
+using System;
+using System.Globalization;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Alarm.Views
+{
+    /// <summary>
+    /// AlarmAlertPage class
+    /// It shows the Alert View of alarm
+    /// User can dismiss alarm alert and remove record using dismiss button in the bottom of view.
+    /// </summary>
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+    public partial class AlarmAlertPage : CirclePageEx
+    {
+        AlertPageModel _viewModel;
+        /// <summary>
+        /// AlarmAlertPage constructor.
+        /// </summary>
+        /// <param name="viewModel">AlertPageModel</param>
+        public AlarmAlertPage(AlertPageModel viewModel)
+        {
+            Console.WriteLine("AlarmAlertPage");
+
+            InitializeComponent();
+            BindingContext = _viewModel = viewModel;
+
+            _viewModel.StartAlert();
+
+            // Subscribe notification of locale changes to update text based on locale
+            MessagingCenter.Subscribe<LocaleHandler, CultureInfo>(this, MessageKeys.LanguageChanged, (obj, culture) =>
+            {
+                Console.WriteLine($"AlarmAlertPage received LanguageChanged");
+                // Update text that has been translated into the current language.
+                TimeLabel.Text = _viewModel.Record.ScheduledDateTime.ToString("hh:mm tt");
+                DismissButton.Text = AppResources.Dismiss;
+            });
+        }
+
+        /// <summary>
+        /// Request to dissmiss alarm alert and remove alarm record.
+        /// </summary>
+        /// <param name="sender">object</param>
+        /// <param name="args">EventArgs</param>
+        void OnDismissButtonClicked(object sender, EventArgs args)
+        {
+            Console.WriteLine("OnDismissButtonClicked()");
+
+            _viewModel.Dismiss();
+            PopAsyncAlertPage();
+        }
+
+        /// <summary>
+        /// Invoked when backbutton is pressed
+        /// Alarm record is dismissed.
+        /// </summary>
+        /// <returns>bool value</returns>
+        protected override bool OnBackButtonPressed()
+        {
+            Console.WriteLine("[AlarmAlertPage]  OnBackButtonPressed PopAsync");
+            _viewModel.Dismiss();
+            PopAsyncAlertPage();
+
+            return true;
+        }
+
+        async void PopAsyncAlertPage()
+        {
+            await Navigation.PopAsync();
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/AlarmEditPage.xaml b/Test/Alarm/Alarm/Views/AlarmEditPage.xaml
new file mode 100644 (file)
index 0000000..38858ea
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<views:CirclePageEx
+       x:Class="Alarm.Views.AlarmEditPage"
+       xmlns="http://xamarin.com/schemas/2014/forms"
+       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+       xmlns:views="clr-namespace:Alarm.Views"
+       xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms"
+       RotaryFocusObject="{x:Reference TimeSelector}">
+       <w:CirclePage.Content>
+               <StackLayout
+                       BackgroundColor="Black"
+                       HorizontalOptions="FillAndExpand"
+                       Orientation="Vertical"
+                       VerticalOptions="FillAndExpand">
+                       <w:CircleDateTimeSelector
+                               x:Name="TimeSelector"
+                               DateTime="{Binding SelectedDateTime, Mode=TwoWay}"
+                               IsVisibleOfAmPm="True"
+                               ValueType="Time" />
+               </StackLayout>
+       </w:CirclePage.Content>
+       <w:CirclePage.ActionButton>
+               <w:ActionButtonItem
+                       x:Name="SaveButton"
+                       Clicked="OnSaveButtonClicked"
+                       Icon="ic_popup_btn_check.png" />
+       </w:CirclePage.ActionButton>
+</views:CirclePageEx>
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/AlarmEditPage.xaml.cs b/Test/Alarm/Alarm/Views/AlarmEditPage.xaml.cs
new file mode 100644 (file)
index 0000000..171e274
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 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 Alarm.Models;
+using System;
+using Tizen.Wearable.CircularUI.Forms;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Alarm.Views
+{
+    /// <summary>
+    /// AlarmEditPage class
+    /// It shows the Edit View of alarm
+    /// User can set alarm time using CircleDateTimeSelector. and then save alarm record.
+    /// </summary>
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+       public partial class AlarmEditPage : CirclePageEx
+    {
+        public static readonly BindableProperty SelectedDateTimeProperty = BindableProperty.Create("SelectedDateTime", typeof(DateTime), typeof(AlarmEditPage), DateTime.Now);
+
+        private bool _alarmSaving;
+
+        /// <summary>
+        /// Selected date time for saved record or current time.
+        /// </summary>
+        public DateTime SelectedDateTime
+        {
+            get
+            {
+                return (DateTime)GetValue(SelectedDateTimeProperty);
+            }
+
+            set
+            {
+                SetValue(SelectedDateTimeProperty, value);
+            }
+        }
+
+        /// <summary>
+        /// AlarmEditPage constructor.
+        /// </summary>
+        /// <param name="alarmRecordData">AlarmRecord</param>
+        public AlarmEditPage(AlarmRecord alarmRecordData)
+               {
+            InitializeComponent();
+            TimeSelector.BindingContext = this;
+            _alarmSaving = false;
+            SelectedDateTime = alarmRecordData.ScheduledDateTime;
+        }
+
+        /// <summary>
+        /// Update SelectedDateTime with alarmRecordData time.
+        /// </summary>
+        /// <param name="alarmRecordData">Alarm record data to set SelectedDateTime</param>
+        internal void Update(AlarmRecord alarmRecordData)
+        {
+            Console.WriteLine("Update" + alarmRecordData.ToString());
+            SelectedDateTime = alarmRecordData.ScheduledDateTime;
+            _alarmSaving = false;
+        }
+
+        /// <summary>
+        /// Request to save alarm event.
+        /// </summary>
+        /// <param name="sender">object</param>
+        /// <param name="args">EventArgs</param>
+        async void OnSaveButtonClicked(object sender, EventArgs args)
+        {
+            Console.WriteLine("OnSaveButtonClicked");
+            if (_alarmSaving)
+            {
+                return;
+            }
+
+            _alarmSaving = true;
+            //subtract second from selectedTime.
+            SelectedDateTime = SelectedDateTime.AddSeconds(-SelectedDateTime.Second);
+            Console.WriteLine($"SelectedDateTime :{SelectedDateTime}");
+            AlarmModel.BindableAlarmRecord.ScheduledDateTime = SelectedDateTime;
+
+            AlarmRecord duplicate = new AlarmRecord();
+            bool existSameAlarm = AlarmModel.CheckAlarmExist(ref duplicate);
+            if (existSameAlarm)
+            {
+                // Use alarm created date for unique identifier for an alarm record
+                string alarmUID = AlarmModel.BindableAlarmRecord.GetUniqueIdentifier();
+                if (!AlarmModel.BindableAlarmRecord.IsSerialized)
+                {
+                    // in case that AlarmEditPage is shown by clicking at add alarm
+                    // when trying to create a system alarm and save it, find the same alarm
+                    // expected behavior : update the previous one and do not create a new alarm
+                    AlarmModel.BindableAlarmRecord.IsSerialized = true;
+                    duplicate.DeepCopy(AlarmModel.BindableAlarmRecord, false);
+                    // Update the existing alarm(duplicate)
+                    Console.WriteLine("exist same alarm! update previous alarm, not create new alarm:" + AlarmModel.BindableAlarmRecord.ToString());
+                    AlarmModel.UpdateAlarm(duplicate);
+                }
+                else if (alarmUID.Equals(duplicate.GetUniqueIdentifier()))
+                {
+                    // in case that AlarmEditPage is shown by selecting an item of ListView in AlarmListPage
+                    // At saving time, just update itself. (It doesn't affect other alarms)
+                    Console.WriteLine("exist same alarm! update current alarm:" + AlarmModel.BindableAlarmRecord.ToString());
+                    AlarmModel.UpdateAlarm(AlarmModel.BindableAlarmRecord);
+                }
+                else
+                {
+                    // in case that AlarmEditPage is shown by selecting an item of ListView in AlarmListPage
+                    // At saving time, the same alarm is found.
+                    // In case that this alarm is not new, the existing alarm(duplicate) will be deleted and it will be updated.
+                    // 1. delete duplicate alarm
+                    Console.WriteLine("exist same alarm! delete duplicate alarm and update current alarm:" + AlarmModel.BindableAlarmRecord.ToString());
+                    AlarmModel.DeleteAlarm(duplicate);
+                    // 2. update bindableAlarmRecord
+                    AlarmModel.UpdateAlarm(AlarmModel.BindableAlarmRecord);
+                }
+            }
+            else
+            {
+                if (!AlarmModel.BindableAlarmRecord.IsSerialized)
+                {
+                    // In case that AlarmEditPage is shown by clicking FloatingButton
+                    // There's no same alarm. So, just create a new alarm and add to list and dictionary.
+                    AlarmModel.BindableAlarmRecord.IsSerialized = true;
+                    Console.WriteLine("new Alarm create:" + AlarmModel.BindableAlarmRecord.ToString());
+                    AlarmModel.CreateAlarm();
+                }
+                else
+                {
+                    // in case that AlarmEditPage is shown by selecting an item of ListView in AlarmListPage
+                    // There's no same alarm. So, just update itself.
+                    Console.WriteLine("update current alarm:" + AlarmModel.BindableAlarmRecord.ToString());
+                    AlarmModel.UpdateAlarm(AlarmModel.BindableAlarmRecord);
+                }
+            }
+
+            //Create SavePopupPage, and then close current EditPage.
+            Navigation.InsertPageBefore(new SavePopupPage(AlarmModel.BindableAlarmRecord), this);
+            await Navigation.PopAsync();
+            _alarmSaving = false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/AlarmListCell.cs b/Test/Alarm/Alarm/Views/AlarmListCell.cs
new file mode 100644 (file)
index 0000000..629daf8
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 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 Alarm.Converters;
+using Alarm.Models;
+using System;
+using Tizen.Wearable.CircularUI;
+using Tizen.Wearable.CircularUI.Forms;
+using Xamarin.Forms;
+
+
+namespace Alarm.Views
+{
+    /// <summary>
+    /// This class defines list cell for Alarm.
+    /// This inherits from ViewCell which provides custom UI for a ListView
+    /// It is commonly used for any cell for this ListView
+    /// </summary>
+    public class AlarmListCell : ViewCell
+    {
+
+        public StackLayout alarmItemLayout;
+
+        /// <summary>
+        /// time label for the alarm list
+        /// </summary>
+        public Label timeLabel;
+
+        /// <summary>
+        /// Active/Inactive switch object
+        /// </summary>
+        public Switch switchObj;
+
+        /// <summary>
+        /// Draws alarm list
+        /// </summary>
+        /// <returns>Returns RelativeLayout</returns>
+        protected virtual StackLayout Draw()
+        {
+
+            /// Need to get bindable context to assign list value
+            AlarmRecord alarmData = (AlarmRecord)BindingContext;
+
+            /// If binding context is null, can't proceed further action
+            if (alarmData == null)
+            {
+                return null;
+            }
+
+
+            /// Alarm item layout should be set if null
+            if (alarmItemLayout == null)
+            {
+                // The layout of item cell
+                alarmItemLayout = new StackLayout
+                {
+                    HeightRequest = 120,
+                    Orientation = StackOrientation.Horizontal,
+                    HorizontalOptions = LayoutOptions.FillAndExpand,
+                    VerticalOptions = LayoutOptions.FillAndExpand
+                };
+
+                // Time Label
+                timeLabel = new Label()
+                {
+                    WidthRequest = 280,
+                    FontSize = 12,
+                    TextColor = Color.White,
+                    HorizontalOptions = LayoutOptions.Center,
+                    VerticalOptions = LayoutOptions.Center,
+                    HorizontalTextAlignment = TextAlignment.Center,
+                    VerticalTextAlignment = TextAlignment.Center,
+                };
+
+                timeLabel.SetBinding(Label.TextProperty, new Binding("ScheduledDateTime", BindingMode.Default, new ScheduledDateTimeToTextConverter()));
+                alarmItemLayout.Children.Add(timeLabel);
+
+                switchObj = new Check
+                {
+                    HeightRequest = 80,
+                    WidthRequest = 80,
+                    DisplayStyle = CheckDisplayStyle.Default,
+                    IsToggled = alarmData.AlarmState == AlarmStates.Inactive ? false : true,
+                    HorizontalOptions = LayoutOptions.End,
+                    VerticalOptions = LayoutOptions.Center,
+                };
+
+                alarmItemLayout.Children.Add(switchObj);
+
+                /// Adds an event
+                switchObj.Toggled += (s, e) =>
+                {
+                    AlarmRecord am = (AlarmRecord)BindingContext;
+                    /// Modify state and re-draw it. Redraw must be called to redraw
+                    if (e.Value)
+                    {
+                        AlarmModel.ReactivatelAlarm(am);
+                    }
+                    else
+                    {
+                        AlarmModel.DeactivatelAlarm(am);
+                    }
+                };
+            }
+
+            return alarmItemLayout;
+        }
+
+        /// <summary>
+        /// When binding context is changed, need to redraw
+        /// </summary>
+        protected override void OnBindingContextChanged()
+        {
+            base.OnBindingContextChanged();
+            if (BindingContext == null)
+            {
+                return;
+            }
+            else
+            {
+                View = Draw();
+            }
+        }
+    }
+}
diff --git a/Test/Alarm/Alarm/Views/CirclePageEx.xaml b/Test/Alarm/Alarm/Views/CirclePageEx.xaml
new file mode 100644 (file)
index 0000000..133b629
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<w:CirclePage
+       x:Class="Alarm.Views.CirclePageEx"
+       xmlns="http://xamarin.com/schemas/2014/forms"
+       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+       xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms" />
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/CirclePageEx.xaml.cs b/Test/Alarm/Alarm/Views/CirclePageEx.xaml.cs
new file mode 100644 (file)
index 0000000..a335d25
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 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 Tizen.Wearable.CircularUI.Forms;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Alarm.Views
+{
+    /// <summary>
+    /// Base Page class
+    /// </summary>
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+       public partial class CirclePageEx : CirclePage
+    {
+               public CirclePageEx()
+               {
+            // Hide navigation bar
+            NavigationPage.SetHasNavigationBar(this, false);
+
+            InitializeComponent();
+        }
+       }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/MainPage.xaml b/Test/Alarm/Alarm/Views/MainPage.xaml
new file mode 100644 (file)
index 0000000..a4c01bb
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<views:CirclePageEx
+       x:Class="Alarm.Views.MainPage"
+       xmlns="http://xamarin.com/schemas/2014/forms"
+       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+       xmlns:control="clr-namespace:Alarm.Controls"
+       xmlns:converters="clr-namespace:Alarm.Converters"
+       xmlns:resx="clr-namespace:Alarm.Resx"
+       xmlns:views="clr-namespace:Alarm.Views"
+       xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms"
+       Title="MainPage"
+       RotaryFocusObject="{x:Reference AlarmList}">
+       <ContentPage.Resources>
+               <ResourceDictionary>
+                       <converters:ItemCountToVisibilityConverter x:Key="VisibilityConverter" />
+               </ResourceDictionary>
+       </ContentPage.Resources>
+       <ContentPage.Content>
+               <StackLayout Orientation="Vertical">
+                       <StackLayout
+                               x:Name="NoAlarmLayout"
+                               HorizontalOptions="Center"
+                               IsVisible="{Binding RecordList.Count, Converter={StaticResource VisibilityConverter}, ConverterParameter={x:Static converters:MainPageType.Empty}}"
+                               VerticalOptions="FillAndExpand">
+                               <Label
+                                       x:Name="EmptyHeaderLabel"
+                                       Margin="0,50,0,30"
+                                       FontAttributes="Bold"
+                                       FontSize="Medium"
+                                       HorizontalTextAlignment="Center"
+                                       Text="{x:Static resx:AppResources.Alarm}"
+                                       TextColor="#368AFF" />
+                               <control:CustomImageButton
+                                       x:Name="noAlarmImg"
+                                       Released="OnButtonReleased"
+                                       Source="alarm_no_alarm_icon.png" />
+                               <Label
+                                       x:Name="EmptyBottomLabel"
+                                       Margin="0,30,0,50"
+                                       FontSize="Small"
+                                       HorizontalTextAlignment="Center"
+                                       Text="{x:Static resx:AppResources.AddAlarm}" />
+                       </StackLayout>
+                       <StackLayout
+                               x:Name="AlarmListLayout"
+                               HorizontalOptions="FillAndExpand"
+                               IsVisible="{Binding RecordList.Count, Converter={StaticResource VisibilityConverter}, ConverterParameter={x:Static converters:MainPageType.ListView}}"
+                               VerticalOptions="Center">
+                               <w:CircleListView
+                                       x:Name="AlarmList"
+                                       HasUnevenRows="True"
+                                       HorizontalOptions="FillAndExpand"
+                                       ItemTapped="OnItemTapped"
+                                       ItemsSource="{Binding RecordList}"
+                                       VerticalOptions="Center">
+                                       <ListView.Header>
+                                               <StackLayout HeightRequest="115">
+                                                       <Label
+                                                               x:Name="HeaderLabel"
+                                                               FontAttributes="Bold"
+                                                               FontSize="16"
+                                                               HorizontalOptions="CenterAndExpand"
+                                                               Text="{x:Static resx:AppResources.Alarm}"
+                                                               TextColor="#368AFF"
+                                                               VerticalOptions="FillAndExpand" />
+                                               </StackLayout>
+                                       </ListView.Header>
+                                       <ListView.Footer>
+                                               <StackLayout HeightRequest="115">
+                                                       <BoxView />
+                                               </StackLayout>
+                                       </ListView.Footer>
+                                       <ListView.ItemTemplate>
+                                               <DataTemplate>
+                                                       <views:AlarmListCell />
+                                               </DataTemplate>
+                                       </ListView.ItemTemplate>
+                               </w:CircleListView>
+                       </StackLayout>
+               </StackLayout>
+       </ContentPage.Content>
+       <views:CirclePageEx.ActionButton>
+               <w:ActionButtonItem
+                       x:Name="AddButton"
+                       Clicked="OnAddButtonClicked"
+                       IsVisible="{Binding RecordList.Count, Converter={StaticResource VisibilityConverter}, ConverterParameter={x:Static converters:MainPageType.Button}}"
+                       Text="{x:Static resx:AppResources.Add}" />
+       </views:CirclePageEx.ActionButton>
+</views:CirclePageEx>
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/MainPage.xaml.cs b/Test/Alarm/Alarm/Views/MainPage.xaml.cs
new file mode 100644 (file)
index 0000000..910f76d
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 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 Alarm.Converters;
+using Alarm.Implements;
+using Alarm.Models;
+using Alarm.Resx;
+using Alarm.ViewModels;
+using System;
+using System.Globalization;
+using System.Windows.Input;
+using Tizen.Wearable.CircularUI.Forms;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Alarm.Views
+{
+    /// <summary>
+    /// Main Page class
+    /// </summary>
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+       public partial class MainPage : CirclePageEx
+    {
+        /// <summary>
+        /// Constructor of MainPage class
+        /// </summary>
+        /// <param name="viewModel">MainPageModel</param>
+        public MainPage(MainPageModel viewModel)
+               {
+            BindingContext = viewModel;
+            InitializeComponent();
+
+            // Subscribe notification of locale changes to update text based on locale
+            MessagingCenter.Subscribe<LocaleHandler, CultureInfo>(this, MessageKeys.LanguageChanged, (obj, culture) =>
+            {
+                Console.WriteLine($"MainPage received LanguageChanged");
+                // Update text that has been translated into the current language.
+                HeaderLabel.Text = EmptyHeaderLabel.Text = AppResources.Alarm;
+                EmptyBottomLabel.Text = AppResources.AddAlarm;
+                AddButton.Text = AppResources.Add;
+
+                //This is workaround solution for refresh Listview following to language change.
+                AlarmList.ItemsSource = null;
+                AlarmList.ItemsSource = viewModel.RecordList;
+            });
+        }
+
+        /// <summary>
+        /// ListItem tapped event handler
+        /// </summary>
+        /// <param name="sender">object</param>
+        /// <param name="e">SelectedItemChangedEventArgs</param>
+        void OnItemTapped(object sender, SelectedItemChangedEventArgs e)
+        {
+            /// skips for no selected item case
+            if (e.SelectedItem == null)
+            {
+                return;
+            }
+
+            /// check selected item
+            AlarmRecord alarm = e.SelectedItem as AlarmRecord;
+            /// de-select first item
+            ((ListView)sender).SelectedItem = null; // de-select the row
+            EditAlarm(alarm);
+        }
+
+        /// <summary>
+        /// Image Button event handler
+        /// </summary>
+        /// <param name="sender">object</param>
+        /// <param name="args">EventArgs</param>
+        void OnButtonReleased(object sender, EventArgs args)
+        {
+            CreateNewAlarm();
+        }
+
+        /// <summary>
+        /// Action button event handler
+        /// </summary>
+        /// <param name="sender">object</param>
+        /// <param name="args">EventArgs</param>
+        void OnAddButtonClicked(object sender, EventArgs args)
+        {
+            CreateNewAlarm();
+        }
+
+        /// <summary>
+        /// Launch AlarmEdit page
+        /// </summary>
+        async void CreateNewAlarm()
+        {
+            /// Creates default alarm record
+            AlarmRecord defaultAlarmRecord = new AlarmRecord();
+            defaultAlarmRecord.SetDefault();
+            await Navigation.PushAsync(AlarmPageController.GetInstance(AlarmPages.EditPage, defaultAlarmRecord));
+        }
+
+        /// <summary>
+        /// Launch Edit page
+        /// </summary>
+        /// <param name="record">AlarmRecord</param>
+        async void EditAlarm(AlarmRecord record)
+        {
+            await Navigation.PushAsync(AlarmPageController.GetInstance(AlarmPages.EditPage, record));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/SavePopupPage.xaml b/Test/Alarm/Alarm/Views/SavePopupPage.xaml
new file mode 100644 (file)
index 0000000..daccff7
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage
+       x:Class="Alarm.Views.SavePopupPage"
+       xmlns="http://xamarin.com/schemas/2014/forms"
+       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
+       <ContentPage.Content>
+               <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
+                       <Label x:Name="AlarmSetLabel" Text="Set Alarm now!" />
+               </StackLayout>
+       </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/Views/SavePopupPage.xaml.cs b/Test/Alarm/Alarm/Views/SavePopupPage.xaml.cs
new file mode 100644 (file)
index 0000000..0186bc1
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 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 Alarm.Models;
+using Alarm.Resx;
+using System;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Alarm.Views
+{
+    /// <summary>
+    /// SavePopupPage class
+    /// It shows the remaining time from the current time to the alarm time. 
+    /// </summary>
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+       public partial class SavePopupPage : ContentPage
+       {
+        ///Korean CultureInfo name
+        const string KoreanCultureInfoName = "ko-KR";
+
+        private AlarmRecord _record;
+
+        /// <summary>
+        /// SavePopupPage constructor
+        /// </summary>
+        /// <param name="record">AlarmRecord</param>
+        public SavePopupPage(AlarmRecord record)
+               {
+            InitializeComponent();
+
+            NavigationPage.SetHasNavigationBar(this, false);
+            _record = record;
+            SetPopupLabel();
+            ClosePopup();
+        }
+
+        /// <summary>
+        /// Calculate the remaining time and then set AlarmSetLabel.
+        /// </summary>
+        private void SetPopupLabel()
+        {
+            DateTime savedTime = _record.ScheduledDateTime;
+            DateTime now = System.DateTime.Now;
+            int hour = savedTime.Hour - now.Hour;
+            int min = savedTime.Minute - now.Minute;
+            string text = "";
+
+            if (hour >= 0)
+            {
+                if (min < 0)
+                {
+                    min = 60 + min;
+                    if (hour == 0)
+                    {
+                        hour = 24 - 1;
+                    }
+                    else
+                    {
+                        hour = hour - 1;
+                    }
+                }
+            }
+            else   //hour < 0
+            {
+                hour = 24 + hour;
+                if (min < 0)
+                {
+                    min = 60 + min;
+                    hour = hour - 1;
+                }
+            }
+
+            //Console.WriteLine($"App.Culture.Name:{App.Culture.Name}");
+            if (hour == 0)
+            {
+                //If hour is 0, display only remain minutes.
+                if (min == 0)
+                {
+                    text = GetSetNowText();
+                }
+                else
+                {
+                    text = GetRemainMinutesText(min);
+                }
+            }
+            else
+            {
+                 text = GetRemainHourMinutesText(hour, min);
+            }
+
+            AlarmSetLabel.Text = text;
+        }
+
+
+        /// <summary>
+        /// Get localized string of alarm set now string .
+        /// </summary>
+        /// <returns>localized string </returns>
+        private string GetSetNowText()
+        {
+            string text;
+            if (App.Culture.Name == KoreanCultureInfoName)
+            {
+                text = AppResources.Now + " " + AppResources.AlarmSet;
+            }
+            else
+            {
+                text = AppResources.AlarmSet + " " + AppResources.Now;
+            }
+
+            return text;
+        }
+
+        /// <summary>
+        /// Get localized string of remaining minutes.
+        /// </summary>
+        /// <param name="minutes">remain minutes</param>
+        /// <returns>Localized string of remaining minutes</returns>
+        private string GetRemainMinutesText(int minutes)
+        {
+            string text;
+            if (App.Culture.Name == KoreanCultureInfoName)
+            {
+                text = AppResources.FromNow + " " + minutes + " " + AppResources.Minutes + " " + AppResources.After + " " + AppResources.AlarmSet;
+            }
+            else
+            {
+                if (minutes == 1)
+                {
+                    text = AppResources.AlarmSet + " " + AppResources.Minute + " " + AppResources.After + " " + AppResources.FromNow;
+                }
+                else
+                {
+                    text = AppResources.AlarmSet + " " + minutes + " " + AppResources.Minutes + " " + AppResources.After + " " + AppResources.FromNow;
+                }
+            }
+
+            return text;
+        }
+
+        /// <summary>
+        /// Get localized string of remaining hours and minutes.
+        /// </summary>
+        /// <param name="hours">remain hours</param>
+        /// <param name="minutes">remain minutes</param>
+        /// <returns>Localized string of remaining hours and minutes</returns>
+        private string GetRemainHourMinutesText(int hours, int minutes)
+        {
+            string text;
+            if (App.Culture.Name == KoreanCultureInfoName)
+            {
+                if (minutes == 0)
+                {
+                    text = AppResources.FromNow + " " + hours + " " + AppResources.Hours + " " + AppResources.After + " " + AppResources.AlarmSet;
+                }
+                else
+                {
+                    text = AppResources.FromNow + " " + hours + " " + AppResources.Hours + " " + minutes + " " + AppResources.Minutes + " " + AppResources.After + " " + AppResources.AlarmSet;
+                }
+            }
+            else
+            {
+                
+                if (hours == 1)
+                {
+                    text = AppResources.AlarmSet  + " " + AppResources.Hour;
+                }
+                else
+                {
+                    text = AppResources.AlarmSet + " " + hours + " " + AppResources.Hours;
+                }
+
+                if (minutes == 0)
+                {
+                    text = text + " " + AppResources.After + " " + AppResources.FromNow;
+                }
+                else if (minutes == 1)
+                {
+                    text = text + " " + AppResources.Minute + " " + AppResources.After + " " + AppResources.FromNow;
+                }
+                else
+                {
+                    text = text + " " + minutes + " " + AppResources.Minutes + " " + AppResources.After + " " + AppResources.FromNow;
+                }
+            }
+
+            return text;
+        }
+
+        /// <summary>
+        /// After 2 seconds. this page closed and then go to the Mainpage
+        /// </summary>
+        async private void ClosePopup()
+        {
+            await Task.Delay(2000);
+            await Navigation.PopToRootAsync();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Test/Alarm/Alarm/res/Notification.ogg b/Test/Alarm/Alarm/res/Notification.ogg
new file mode 100644 (file)
index 0000000..24d9e68
Binary files /dev/null and b/Test/Alarm/Alarm/res/Notification.ogg differ
diff --git a/Test/Alarm/Alarm/res/alarm_add_icon.png b/Test/Alarm/Alarm/res/alarm_add_icon.png
new file mode 100644 (file)
index 0000000..52b03a4
Binary files /dev/null and b/Test/Alarm/Alarm/res/alarm_add_icon.png differ
diff --git a/Test/Alarm/Alarm/res/alarm_no_alarm_icon.png b/Test/Alarm/Alarm/res/alarm_no_alarm_icon.png
new file mode 100644 (file)
index 0000000..8de17cd
Binary files /dev/null and b/Test/Alarm/Alarm/res/alarm_no_alarm_icon.png differ
diff --git a/Test/Alarm/Alarm/res/alarm_ringing_icon.png b/Test/Alarm/Alarm/res/alarm_ringing_icon.png
new file mode 100644 (file)
index 0000000..3bbe63d
Binary files /dev/null and b/Test/Alarm/Alarm/res/alarm_ringing_icon.png differ
diff --git a/Test/Alarm/Alarm/res/ic_popup_btn_check.png b/Test/Alarm/Alarm/res/ic_popup_btn_check.png
new file mode 100644 (file)
index 0000000..f20f62e
Binary files /dev/null and b/Test/Alarm/Alarm/res/ic_popup_btn_check.png differ
diff --git a/Test/Alarm/Alarm/shared/res/Alarm.png b/Test/Alarm/Alarm/shared/res/Alarm.png
new file mode 100644 (file)
index 0000000..9e31f00
Binary files /dev/null and b/Test/Alarm/Alarm/shared/res/Alarm.png differ
diff --git a/Test/Alarm/Alarm/tizen-manifest.xml b/Test/Alarm/Alarm/tizen-manifest.xml
new file mode 100644 (file)
index 0000000..54b7ded
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="org.tizen.example.Alarm" version="1.0.0" api-version="4" xmlns="http://tizen.org/ns/packages">
+       <profile name="wearable" />
+       <ui-application appid="org.tizen.example.Alarm" exec="Alarm.dll" multiple="false" nodisplay="false" taskmanage="true" splash-screen-display="true" type="dotnet" launch_mode="single">
+               <label>.NET Alarm</label>
+               <icon>Alarm.png</icon>
+               <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+        <label xml:lang="ko-kr">닷넷 알람</label>
+        <label xml:lang="en-us">.NET Alarm</label>
+               <splash-screens />
+       </ui-application>
+       <shortcut-list />
+       <privileges>
+               <privilege>http://tizen.org/privilege/alarm.set</privilege>
+               <privilege>http://tizen.org/privilege/alarm.get</privilege>
+               <privilege>http://tizen.org/privilege/haptic</privilege>
+               <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+       </privileges>
+       <provides-appdefined-privileges />
+</manifest>
diff --git a/XSF.sln b/XSF.sln
index d8b7719..f9e480c 100644 (file)
--- a/XSF.sln
+++ b/XSF.sln
@@ -33,6 +33,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XStopWatch", "Test\XStopWat
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XStopWatch", "XStopWatch", "{CE5F5031-BE26-42D9-8B86-87770CB01226}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Alarm", "Alarm", "{232D191B-486D-4EC3-BCB2-F776124AF705}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Alarm", "Test\Alarm\Alarm\Alarm.csproj", "{4653BFED-31B5-412F-8076-A609A4E5D851}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
@@ -71,6 +75,10 @@ Global
                {38700235-FC85-41F6-8801-5BBFDE5BCD65}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {38700235-FC85-41F6-8801-5BBFDE5BCD65}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {38700235-FC85-41F6-8801-5BBFDE5BCD65}.Release|Any CPU.Build.0 = Release|Any CPU
+               {4653BFED-31B5-412F-8076-A609A4E5D851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {4653BFED-31B5-412F-8076-A609A4E5D851}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {4653BFED-31B5-412F-8076-A609A4E5D851}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {4653BFED-31B5-412F-8076-A609A4E5D851}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -85,6 +93,8 @@ Global
                {1D58F961-6843-474B-91AB-07140137C707} = {32A6D9FF-AFD0-4CAF-A6E9-A3BBE8C4AEBA}
                {38700235-FC85-41F6-8801-5BBFDE5BCD65} = {CE5F5031-BE26-42D9-8B86-87770CB01226}
                {CE5F5031-BE26-42D9-8B86-87770CB01226} = {087187F9-A361-4269-88EB-47A44185FA7F}
+               {232D191B-486D-4EC3-BCB2-F776124AF705} = {087187F9-A361-4269-88EB-47A44185FA7F}
+               {4653BFED-31B5-412F-8076-A609A4E5D851} = {232D191B-486D-4EC3-BCB2-F776124AF705}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {81158F7F-D003-4C6D-9937-5002C54D57EA}