--- /dev/null
+# Voicememo2020
+
+This is the Voicememo application for Galaxy Watch product in C#.
--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29215.179
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoiceMemo", "VoiceMemo\VoiceMemo.csproj", "{E85DA711-1972-4A14-B80F-D354415A0F09}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E85DA711-1972-4A14-B80F-D354415A0F09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E85DA711-1972-4A14-B80F-D354415A0F09}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E85DA711-1972-4A14-B80F-D354415A0F09}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E85DA711-1972-4A14-B80F-D354415A0F09}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C4DF83AB-49AF-4F12-8BF5-22D1A7A720C8}
+ EndGlobalSection
+EndGlobal
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<Application xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="VoiceMemo.App">
+ <Application.Resources>
+ <!-- Application resource dictionary -->
+ <ResourceDictionary>
+ <Style x:Key="LabelStyle-Base" TargetType="Label">
+ <Setter Property="HorizontalTextAlignment" Value="Center" />
+ <Setter Property="VerticalTextAlignment" Value="Center" />
+ <Setter Property="TextColor" Value="White" />
+ </Style>
+ <Style x:Key="BaseLabelStyle" TargetType="Label">
+ <Setter Property="HorizontalOptions" Value="Center" />
+ <Setter Property="VerticalOptions" Value="Center" />
+ <Setter Property="VerticalTextAlignment" Value="Center" />
+ <Setter Property="HorizontalTextAlignment" Value="Center" />
+ <Setter Property="TextColor" Value="White" />
+ </Style>
+ </ResourceDictionary>
+ </Application.Resources>
+</Application>
\ No newline at end of file
--- /dev/null
+/*
+ * 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.Globalization;
+using System.Threading;
+using VoiceMemo.Data;
+using VoiceMemo.Services;
+using VoiceMemo.ViewModels;
+using VoiceMemo.Views;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
+namespace VoiceMemo
+{
+ /// <summary>
+ /// VoiceMemo Application class
+ /// </summary>
+ public partial class App : Application
+ {
+ MainPage firstPage;
+ //MainPageCS firstPageCS;
+ public MainPageModel mainPageModel;
+ public App()
+ {
+ // Get locale information
+ UpdateLocale();
+ InitializeComponent();
+
+ firstPage = (MainPage)PageFactory.GetInstance(Pages.StandBy);
+ MainPage = new NavigationPage(firstPage);
+ mainPageModel = (MainPageModel)firstPage.BindingContext;
+
+ //firstPageCS = (MainPageCS)PageFactory.GetInstance(Pages.StandByCS);
+ //MainPage = new NavigationPage(firstPageCS);
+
+ //mainPageModel = (MainPageModel)firstPageCS.BindingContext;
+ }
+
+ // database for voice records
+ static RecordDatabase database;
+ public static RecordDatabase Database
+ {
+ get
+ {
+ if (database == null)
+ {
+ database = new RecordDatabase(DeviceInformationService.Instance.GetLocalDBFilePath("DotnetVoiceMemo.db3"));
+ }
+
+ return database;
+ }
+ }
+
+ protected override void OnStart()
+ {
+
+ }
+
+ 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 ci = LocaleService.Instance.CurrentCultureInfo;
+ SetCultureInfo(ci);
+
+ // Whenever language has been changed, CurrentCulture will be updated.
+ //MessagingCenter.Subscribe<LocaleService, CultureInfo>(this, MessageKeys.LanguageChanged, (obj, culture) =>
+ //{
+ // SetCultureInfo(culture);
+ // MessagingCenter.Send<App>(this, MessageKeys.UpdateByLanguageChange);
+ //});
+ //Trace.End();
+ }
+
+ // Set the current culture
+ // It will be used by the Resource Manager
+ void SetCultureInfo(CultureInfo info)
+ {
+ Resx.AppResources.Culture = info; // set the RESX for resource localization
+ Thread.CurrentThread.CurrentCulture = info;
+ Thread.CurrentThread.CurrentUICulture = info;
+ }
+
+ public void Terminate()
+ {
+ MessagingCenter.Unsubscribe<LocaleService, CultureInfo>(this, MessageKeys.LanguageChanged);
+ //((MainPageModel)firstPage?.BindingContext).Dispose();
+ PageFactory.DestoryPage();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Converters
+{
+ /// <summary>
+ /// NameType enum
+ /// </summary>
+ public enum NameType
+ {
+ /// <summary>
+ /// The name of Cultural Region
+ /// </summary>
+ RegionName,
+ /// <summary>
+ /// English Name
+ /// </summary>
+ EnglishName,
+ }
+
+ /// <summary>
+ /// Class CountryCodeToNameConverter
+ /// It converts country code to name.
+ /// </summary>
+ class CountryCodeToNameConverter : 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)
+ {
+ string countryCode = (string)value;
+ if (countryCode == null)
+ {
+ return null;
+ }
+
+ var type = (NameType)parameter;
+
+ switch (type)
+ {
+ case NameType.EnglishName:
+ CultureInfo cultureInfo = new CultureInfo(countryCode);
+ return cultureInfo.DisplayName;
+ case NameType.RegionName:
+ RegionInfo regionInfo = new RegionInfo(countryCode.Replace("_", "-"));
+ return regionInfo.EnglishName;
+ default:
+ return null;
+ }
+ }
+
+ /// <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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Converters
+{
+ public enum RemainingTimeType
+ {
+ TimeText,
+ }
+
+ /// <summary>
+ /// Class DurationToRemainingTimeConverter
+ /// It converts duration information to remaining time.
+ /// format - minutes:seconds
+ /// </summary>
+ public class DurationToRemainingTimeConverter : 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 remains = System.Convert.ToInt32(value);
+ RemainingTimeType type = (RemainingTimeType)parameter;
+ switch (type)
+ {
+ case RemainingTimeType.TimeText:
+ int minutes = remains / 60000;
+ int seconds = (remains - minutes * 60000) / 1000;
+ //Console.WriteLine("[DurationToRemainingTimeConverter - TimeText] remaining time : " + remains);
+ // return the remaining time, formatted as {minutes}:{seconds}
+ return String.Format("{0:00}:{1:00}", minutes, seconds);
+ default:
+ return null;
+ }
+ }
+
+ /// <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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Effects;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Converters
+{
+ class RecordImageSourceColorConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ string source = (string)value;
+ Image imageOjb = (Image)parameter;
+
+ if (source == "record_stop_icon.png")
+ {
+ ImageAttributes.SetBlendColor(imageOjb, Color.Red);
+ }
+ else if (source == "recording_icon_pause.png")
+ {
+ ImageAttributes.SetBlendColor(imageOjb, Color.FromHex("#FF4F4F4F"));
+ }
+
+ return source;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Converters
+{
+ /// <summary>
+ /// The type of view
+ /// </summary>
+ public enum ViewType
+ {
+ /// <summary>
+ /// View which is shown when there is no record
+ /// </summary>
+ NoRecordView,
+ /// <summary>
+ /// List view when there are some records
+ /// </summary>
+ RecordListView,
+ RecordCheckView,
+ }
+
+ /// <summary>
+ /// converter class
+ /// Change the visibility of view based on the number of voice records
+ /// </summary>
+ public class RecordsCountToViewVisibilityConverter : 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 RecordsCnt = System.Convert.ToInt32(value);
+ ViewType viewType = (ViewType)parameter;
+ //Console.WriteLine("[RecordsCountToViewVisibilityConverter] RecordsCount = " + RecordsCnt + ", viewType :" + viewType);
+
+ switch (viewType)
+ {
+ case ViewType.NoRecordView:
+ return RecordsCnt == 0 ? true : false;
+ case ViewType.RecordListView:
+ return RecordsCnt == 0 ? false : true;
+ case ViewType.RecordCheckView:
+ return RecordsCnt == 0 ? false : true;
+ default:
+ return true;
+ }
+ }
+
+ /// <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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Resx;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Converters
+{
+ public enum SttPropertyType
+ {
+ // Text : "On", "Off"
+ TextString,
+ // Image
+ ImageSource,
+ RecordImageSource,
+ }
+
+ /// <summary>
+ /// Converter class
+ /// According to whether or not STT feature usability is on,
+ /// this converter class will provider the proper text and the path of image file .
+ /// </summary>
+ public class SttToPropertyConverter : 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)
+ {
+ bool isSttOn = (bool)System.Convert.ToBoolean(value);
+ SttPropertyType type = (SttPropertyType)parameter;
+ //Console.WriteLine("[SttToPropertyConverter] isSttOn: " + isSttOn + ", type:" + type);
+ switch (type)
+ {
+ case SttPropertyType.TextString:
+ return isSttOn ? AppResources.SttOn : AppResources.SttOff;
+ case SttPropertyType.ImageSource:
+ return isSttOn ? "more_option_icon_stt_on.png" : "more_option_icon_stt_off.png";
+ case SttPropertyType.RecordImageSource:
+ return isSttOn ? "voicerecorder_icon_stt.png" : "voicerecorder_icon_stt_off.png";
+ default:
+ return null;
+ }
+ }
+
+ /// <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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 SQLite;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using VoiceMemo.Models;
+
+namespace VoiceMemo.Data
+{
+ /// <summary>
+ /// Database class
+ /// It stores collection of records
+ /// </summary>
+ public class RecordDatabase
+ {
+ // SQLite connection
+ readonly SQLiteAsyncConnection database;
+
+ public RecordDatabase(string dbPath)
+ {
+ database = new SQLiteAsyncConnection(dbPath);
+ database.CreateTableAsync<Record>().Wait();
+ }
+
+ /// <summary>
+ /// Get list or records in database
+ /// </summary>
+ /// <returns>Task<List<Record>></returns>
+ public Task<List<Record>> GetItemsAsync()
+ {
+ return database.Table<Record>().ToListAsync();
+ }
+
+ /// <summary>
+ /// Save record in database
+ /// </summary>
+ /// <param name="item">Record</param>
+ /// <returns>Task<int></returns>
+ public Task<int> SaveItemAsync(Record item)
+ {
+ if (item.ID != 0)
+ {
+ // in case that item already exists in database
+ return database.UpdateAsync(item);
+ }
+ else
+ {
+ // for the first time item will be added in database
+ return database.InsertAsync(item);
+ }
+ }
+
+ /// <summary>
+ /// Delete record from database
+ /// </summary>
+ /// <param name="item">Record</param>
+ /// <returns>Task<int></returns>
+ public Task<int> DeleteItemAsync(Record item)
+ {
+ return database.DeleteAsync(item);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 EColor = ElmSharp.Color;
+using EImage = ElmSharp.Image;
+using System;
+using System.ComponentModel;
+using VoiceMemo.EffectRenderers;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+using VoiceMemo.Effects;
+
+[assembly: ResolutionGroupName("SEC")]
+[assembly: ExportEffect(typeof(BlendColorEffect), "BlendColorEffect")]
+
+namespace VoiceMemo.EffectRenderers
+{
+ internal class BlendColorEffect : PlatformEffect
+ {
+ static readonly EColor DefaultBlendColor = EColor.Default;
+
+ public object ImageAtctributes { get; private set; }
+
+ protected override void OnAttached()
+ {
+ try
+ {
+ if (Control is EImage nativeControl)
+ {
+ nativeControl.LoadingCompleted += OnNativeImageLoadingCompleted;
+ }
+
+ UpdateBlendColor();
+ }
+ catch (Exception e)
+ {
+ Log.Error("Cannot set property on attached control. Error: ", e.Message);
+ }
+ }
+
+ protected override void OnDetached()
+ {
+ if (Control is EImage nativeControl)
+ {
+ nativeControl.LoadingCompleted -= OnNativeImageLoadingCompleted;
+ nativeControl.Color = DefaultBlendColor;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
+ {
+ try
+ {
+ if (args.PropertyName == ImageAttributes.BlendColorProperty.PropertyName)
+ {
+ UpdateBlendColor();
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error("Cannot set property on attached control. Error : ", e.Message);
+ }
+
+ base.OnElementPropertyChanged(args);
+ }
+
+ void OnNativeImageLoadingCompleted(object sender, EventArgs e)
+ {
+ UpdateBlendColor();
+ }
+
+ void UpdateBlendColor()
+ {
+ if ((Element as Image).IsLoading)
+ {
+ Device.StartTimer(TimeSpan.Zero, () =>
+ {
+ InternalBlendColorUpdate();
+ return false;
+ });
+ }
+ else
+ {
+ InternalBlendColorUpdate();
+ }
+ }
+
+ void InternalBlendColorUpdate()
+ {
+ if (Control is EImage image)
+ {
+ var blendColor = (Color)Element.GetValue(ImageAttributes.BlendColorProperty);
+ image.Color = blendColor == Color.Default ? DefaultBlendColor : blendColor.ToNative();
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.ComponentModel;
+using VoiceMemo.EffectRenderers;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportEffect(typeof(TizenEventPropagationEffect), "TizenEventPropagationEffect")]
+
+namespace VoiceMemo.EffectRenderers
+{
+ class TizenEventPropagationEffect : PlatformEffect
+ {
+ protected override void OnAttached()
+ {
+ DoEnable();
+ }
+
+ protected override void OnDetached()
+ {
+ DoEnable(false);
+ }
+
+ protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
+ {
+ base.OnElementPropertyChanged(args);
+ if (args.PropertyName == VoiceMemo.Effects.TizenEventPropagationEffect.EnablePropagationProperty.PropertyName)
+ {
+ DoEnable();
+ }
+ }
+
+ void DoEnable()
+ {
+ var enablePropagation = VoiceMemo.Effects.TizenEventPropagationEffect.GetEnablePropagation(Element);
+ DoEnable(enablePropagation);
+ }
+
+ void DoEnable(bool enablePropagation)
+ {
+ Control.RepeatEvents = enablePropagation;
+ Control.PropagateEvents = enablePropagation;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.EffectRenderers;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportEffect(typeof(TizenItemLongPressEffect), "ItemLongPressEffect")]
+
+namespace VoiceMemo.EffectRenderers
+{
+ public class TizenItemLongPressEffect : PlatformEffect
+ {
+ protected override void OnAttached()
+ {
+ var genlist = Control as ElmSharp.GenList;
+ if (genlist != null)
+ {
+ genlist.ItemLongPressed += ItemLongPressed;
+ }
+ }
+
+ protected override void OnDetached()
+ {
+ var genlist = this.Control as ElmSharp.GenList;
+ if (genlist != null)
+ {
+ genlist.ItemLongPressed -= ItemLongPressed;
+ }
+ }
+
+ void ItemLongPressed(object sender, ElmSharp.GenListItemEventArgs e)
+ {
+ var command = VoiceMemo.Effects.ItemLongPressEffect.GetCommand(Element);
+ command?.Execute(VoiceMemo.Effects.ItemLongPressEffect.GetCommandParameter(Element));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.ComponentModel;
+using VoiceMemo.EffectRenderers;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportEffect(typeof(TizenStyleEffect), "TizenStyleEffect")]
+
+namespace VoiceMemo.EffectRenderers
+{
+ public class TizenStyleEffect : PlatformEffect
+ {
+ string oldStyle;
+
+ protected override void OnAttached()
+ {
+ DoSetStyle();
+ }
+
+ protected override void OnDetached()
+ {
+ var view = Control as ElmSharp.Widget;
+ if (view != null)
+ {
+ view.Style = oldStyle;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
+ {
+ base.OnElementPropertyChanged(args);
+ if (args.PropertyName == VoiceMemo.Effects.TizenStyleEffect.StyleProperty.PropertyName)
+ {
+ DoSetStyle();
+ }
+ }
+
+ void DoSetStyle()
+ {
+ var view = Control as ElmSharp.Widget;
+ if (view != null)
+ {
+ var style = VoiceMemo.Effects.TizenStyleEffect.GetStyle(Element);
+ oldStyle = view.Style;
+ view.Style = style;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 Xamarin.Forms;
+
+namespace VoiceMemo.Effects
+{
+ public static class ImageAttributes
+ {
+ public static readonly BindableProperty BlendColorProperty = BindableProperty.CreateAttached("BlendColor", typeof(Color), typeof(ImageAttributes), Color.Default, propertyChanged: OnBlendColorPropertyChanged);
+ /// <summary>
+ /// Get blending color
+ /// </summary>
+ /// <param name="element">Image object</param>
+ /// <returns>Color</returns>
+ public static Color GetBlendColor(BindableObject element)
+ {
+ return (Color)element.GetValue(BlendColorProperty);
+ }
+
+ /// <summary>
+ /// Set blending color
+ /// </summary>
+ /// <param name="element">Image object</param>
+ /// <param name="color">Color</param>
+ public static void SetBlendColor(BindableObject element, Color color)
+ {
+ element.SetValue(BlendColorProperty, color);
+ }
+
+ static void OnBlendColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ InternalExtension.InternalPropertyChanged(bindable, BlendColorProperty, () => (Color)newValue == Color.Default, new List<Type> { typeof(Image) });
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 Xamarin.Forms;
+
+namespace VoiceMemo.Effects
+{
+ public class ImageBlendEffect : RoutingEffect
+ {
+ public static readonly BindableProperty BlendColorProperty =
+ BindableProperty.CreateAttached("Color", typeof(Color), typeof(ImageBlendEffect), Color.Default);
+
+ public static Color GetBlendColor(BindableObject element)
+ {
+ return (Color)element.GetValue(BlendColorProperty);
+ }
+
+ public static void SetBlendColor(BindableObject element, Color color)
+ {
+ element.SetValue(BlendColorProperty, color);
+ }
+
+ static void OnBlendColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ InternalExtension.InternalPropertyChanged(bindable, BlendColorProperty, () => (Color)newValue == Color.Default, new List<Type> { typeof(Image) });
+ }
+
+ public ImageBlendEffect() : base("SEC.BlendColorEffect")
+ {
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 Xamarin.Forms;
+
+namespace VoiceMemo.Effects
+{
+ internal static class InternalExtension
+ {
+ internal static void InternalPropertyChanged(BindableObject bindable, BindableProperty property, Func<bool> removalConditionFunc, IList<Type> supportedTypes = null)
+ {
+ if (supportedTypes != null && !supportedTypes.Contains(bindable.GetType()))
+ {
+ return;
+ }
+
+ var element = bindable as Element;
+ string effectName = GetEffectName(property.PropertyName);
+ Effect toRemove = null;
+
+ foreach (var effect in element.Effects)
+ {
+ if (effect.ResolveId == effectName)
+ {
+ toRemove = effect;
+ break;
+ }
+ }
+
+ if (toRemove == null)
+ {
+ element.Effects.Add(Effect.Resolve(effectName));
+ }
+ else
+ {
+ if (removalConditionFunc())
+ {
+ element.Effects.Remove(toRemove);
+ }
+ }
+ }
+
+ internal static string GetEffectName(string propertyName)
+ {
+ return string.Format("SEC.{0}Effect", propertyName);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.Windows.Input;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Effects
+{
+ /// <summary>
+ /// ItemLongPressEffect class
+ /// </summary>
+ public class ItemLongPressEffect : RoutingEffect
+ {
+ public static readonly BindableProperty CommandProperty = BindableProperty.CreateAttached("Command", typeof(ICommand), typeof(ItemLongPressEffect), null);
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.CreateAttached("CommandParameter", typeof(object), typeof(ItemLongPressEffect), null);
+
+ public static Command GetCommand(BindableObject view) => (Command)view.GetValue(CommandProperty);
+ public static void SetCommand(BindableObject view, ICommand value) => view.SetValue(CommandProperty, value);
+
+ public static object GetCommandParameter(BindableObject view) => view.GetValue(CommandParameterProperty);
+ public static void SetCommandParameter(BindableObject view, object value) => view.SetValue(CommandParameterProperty, value);
+
+ public ItemLongPressEffect() : base("SEC.ItemLongPressEffect")
+ {
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+namespace VoiceMemo.Effects
+{
+ public class TizenEventPropagationEffect : RoutingEffect
+ {
+ public static BindableProperty EnablePropagationProperty = BindableProperty.CreateAttached("EnablePropagation", typeof(bool), typeof(TizenEventPropagationEffect), false);
+
+ public static bool GetEnablePropagation(BindableObject view) => (bool)view.GetValue(EnablePropagationProperty);
+ public static void SetEnablePropagation(BindableObject view, bool value) => view.SetValue(EnablePropagationProperty, value);
+
+ public TizenEventPropagationEffect() : base("SEC.TizenEventPropagationEffect")
+ {
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+namespace VoiceMemo.Effects
+{
+ public class TizenStyleEffect : RoutingEffect
+ {
+ public static readonly BindableProperty StyleProperty = BindableProperty.CreateAttached("Style", typeof(string), typeof(TizenStyleEffect), null);
+
+ public static string GetStyle(BindableObject velement) => (string)velement.GetValue(StyleProperty);
+ public static void SetStyle(BindableObject velement, string value) => velement.SetValue(StyleProperty, value);
+
+ public TizenStyleEffect() : base("SEC.TizenStyleEffect")
+ {
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+namespace VoiceMemo.Effects
+{
+ public static class VisualAttributes
+ {
+ public static readonly BindableProperty ThemeStyleProperty = BindableProperty.CreateAttached("ThemeStyle", typeof(string), typeof(VisualAttributes), string.Empty, propertyChanged: OnThemeStylePropertyChanged);
+ public static readonly BindableProperty TooltipProperty = BindableProperty.CreateAttached("Tooltip", typeof(string), typeof(VisualAttributes), string.Empty, propertyChanged: OnTooltipPropertyChanged);
+
+ public static string GetThemeStyle(BindableObject element)
+ {
+ return (string)element.GetValue(ThemeStyleProperty);
+ }
+
+ public static void SetThemeStyle(BindableObject element, string themeStyle)
+ {
+ element.SetValue(ThemeStyleProperty, themeStyle);
+ }
+
+ public static string GetTooltip(BindableObject element)
+ {
+ return (string)element.GetValue(TooltipProperty);
+ }
+
+ public static void SetTooltip(BindableObject element, string tooltipText)
+ {
+ element.SetValue(TooltipProperty, tooltipText);
+ }
+
+ static void OnTooltipPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ InternalExtension.InternalPropertyChanged(bindable, TooltipProperty, () => (string)newValue == string.Empty);
+ }
+
+ static void OnThemeStylePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ InternalExtension.InternalPropertyChanged(bindable, ThemeStyleProperty, () => (string)newValue == string.Empty);
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+
+namespace VoiceMemo.Interop
+{
+ internal static partial class Interop
+ {
+ internal static partial class Libraries
+ {
+ internal const string Libc = "libc.so.6";
+ }
+ }
+}
+
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace VoiceMemo.Interop
+{
+ internal static partial class Interop
+ {
+ internal static partial class SystemCall
+ {
+ [DllImport(Libraries.Libc, EntryPoint = "getenv")]
+ internal static extern IntPtr GetEnvInteral(string name);
+
+ internal static string GetEnv(string name)
+ {
+ return Marshal.PtrToStringAnsi(GetEnvInteral(name));
+ }
+
+ [DllImport(Libraries.Libc, EntryPoint = "setenv")]
+ internal static extern int SetEnv(string name, string value, int overwrite);
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace VoiceMemo.Models
+{
+ public class LatestRecord : Record
+ {
+ public Record Record
+ {
+ get;
+ set;
+ }
+
+ public LatestRecord(Record record) : base()
+ {
+ Record = record;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 SQLite;
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace VoiceMemo.Models
+{
+ /// <summary>
+ /// Record class
+ /// It presents information about voice recording file
+ /// </summary>
+ public class Record : INotifyPropertyChanged
+ {
+ [PrimaryKey, AutoIncrement]
+ public int ID { get; set; }
+ public int _id { get; set; }
+ // the title of audio file
+ public string Title { get; set; }
+ // the date of audio file creation
+ public string Date { get; set; }
+ // the duration of recorded file
+ public int Duration { get; set; }
+ // the file path
+ public string Path { get; set; }
+ // indicate that speech-to-text service is enabled or not when recording the audio file
+ public bool SttEnabled { get; set; }
+
+ string _Text;
+ // Text converted by Speech-to-text service
+ public string Text
+ {
+ get
+ {
+ return _Text;
+ }
+
+ set
+ {
+ SetProperty(ref _Text, value, "Text");
+ }
+ }
+
+ bool _Checked;
+ /// <summary>
+ /// Indicate that it's selected or not to delete
+ /// If it's true, it will be deleted.
+ /// It's not stored in database
+ /// </summary>
+ [Ignore]
+ public bool Checked
+ {
+ get { return _Checked; }
+ set
+ {
+ bool changed = SetProperty(ref _Checked, value, "Checked");
+ if (changed)
+ {
+ //Console.WriteLine("Record.Checked : " + Checked + " --> CheckedNamesCount changed..");
+ ((App)App.Current).mainPageModel.CheckedNamesCount += Checked ? 1 : -1;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return "Record[" + ID + "," + _id + "] " + Title + ", " + Path + ", " + Date + ", Stt(" + SttEnabled + ") " + "(" + Text + ")";
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected bool SetProperty<T>(ref T storage, T value,
+ [CallerMemberName] string propertyName = null)
+ {
+ if (Object.Equals(storage, value))
+ {
+ return false;
+ }
+
+ storage = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+
+ /// <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 = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+using System.Runtime.CompilerServices;
+
+namespace VoiceMemo.Models
+{
+ /// <summary>
+ /// Class SttLanguage
+ /// It presents language (locale) information for Speech-to-Text service
+ /// </summary>
+ public class SttLanguage : INotifyPropertyChanged
+ {
+ bool _isOn;
+ string _lang, _combo, _name, _country;
+
+ public SttLanguage(string lang, string name, string country)
+ {
+ Lang = lang;
+ Name = name;
+ Country = country;
+ Combo = name + "----" + country;
+ IsOn = false;
+ }
+
+ /// <summary>
+ /// Language
+ /// </summary>
+ public string Lang
+ {
+ set { SetProperty(ref _lang, value, "Lang"); }
+ get { return _lang; }
+ }
+
+ /// <summary>
+ /// combination of the full localized culture name and the full name of the country/region in English
+ /// It's used to figure out which language is selected for Speech-to-Text service
+ /// </summary>
+ public string Combo
+ {
+ set { SetProperty(ref _combo, value, "Combo"); }
+ get { return _combo; }
+ }
+
+ /// <summary>
+ /// Name
+ /// It's CultureInfo.DisplayName property and the full localized culture name
+ /// </summary>
+ public string Name
+ {
+ set { SetProperty(ref _name, value, "Name"); }
+ get { return _name; }
+ }
+
+ /// <summary>
+ /// Country name
+ /// It's RegionInfo.EnglishName property and the full name of the country/region in English.
+ /// </summary>
+ public string Country
+ {
+ set
+ {
+ SetProperty(ref _country, value, "Country");
+ }
+
+ get { return _country; }
+ }
+
+ // Indicate that this language is set for Stt service.
+ public bool IsOn
+ {
+ get { return _isOn; }
+ set
+ {
+ SetProperty(ref _isOn, value, "IsOn");
+ }
+ }
+
+ public override string ToString()
+ {
+ return "SttLanguage Name:" + Name + ", Country:" + Country + ", IsOn: " + IsOn;
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected bool SetProperty<T>(ref T storage, T value,
+ [CallerMemberName] string propertyName = null)
+ {
+ if (Object.Equals(storage, value))
+ {
+ return false;
+ }
+
+ storage = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+
+ /// <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 = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 ElmSharp;
+using System;
+using VoiceMemo.Tizen.Wearable.Renderers;
+using VoiceMemo.Views;
+using Xamarin.Forms.Platform.Tizen;
+// @OLD VERSION OF XAMARIN.FORMS
+//using TForms = Xamarin.Forms.Platform.Tizen.Forms;
+using TForms = Xamarin.Forms.Forms;
+using XForms = Xamarin.Forms;
+
+namespace VoiceMemo.Tizen.Wearable.Renderers
+{
+ class GraphicPopUpRenderer : IGraphicPopup, IDisposable
+ {
+ Popup _popUp;
+ bool _isDisposed = false;
+ public event EventHandler BackButtonPressed;
+ public event EventHandler TimedOut;
+
+ public GraphicPopUpRenderer()
+ {
+ Console.WriteLine("GraphicPopUpRenderer() GetHashCode:" + this.GetHashCode());
+ _popUp = new Popup(TForms.NativeParent)
+ {
+ Style = "toast/circle",
+ Orientation = PopupOrientation.Center,
+ AllowEvents = true,
+ };
+ var path = ResourcePath.GetPath("tw_ic_popup_btn_check.png");
+ var image = new Image(_popUp);
+ image.LoadAsync(path);
+ image.Show();
+ _popUp.SetPartContent("toast,icon", image);
+ _popUp.BackButtonPressed += BackButtonPressedHandler;
+ _popUp.TimedOut += TimedOutHandler;
+ _popUp.Dismissed += _popUp_Dismissed;
+ }
+
+ private void _popUp_Dismissed(object sender, EventArgs e)
+ {
+ Console.WriteLine("[GraphicPopUpRenderer._popUp_Dismissed] ");
+ }
+
+ private void TimedOutHandler(object sender, EventArgs e)
+ {
+ Console.WriteLine("[GraphicPopUpRenderer.TimedOutHandler] ");
+ TimedOut?.Invoke(this, EventArgs.Empty);
+ _popUp.Dismiss();
+ }
+
+ private void BackButtonPressedHandler(object sender, EventArgs e)
+ {
+ Console.WriteLine("[GraphicPopUpRenderer.BackButtonPressedHandler] ");
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ _popUp.Dismiss();
+ }
+
+ string _Text;
+ public string Text
+ {
+ get
+ {
+ return _Text;
+ }
+
+ set
+ {
+ if (_Text != value)
+ {
+ _Text = value;
+ Console.WriteLine("[GraphicPopUpRenderer--Update] Text (" + Text + ")");
+ _popUp.SetPartText("elm.text", Text);
+ }
+ }
+ }
+
+ double _Duration;
+ public double Duration
+ {
+ get
+ {
+ return _Duration;
+ }
+
+ set
+ {
+ if (_Duration != value)
+ {
+ _Duration = value;
+ Console.WriteLine("[GraphicPopUpRenderer--Update] Duration (" + Duration + ")");
+ _popUp.Timeout = Duration;
+ }
+ }
+ }
+
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ Console.WriteLine("[GraphicPopUpRenderer.Dispose] ");
+ if (disposing)
+ {
+ if (_popUp != null)
+ {
+ _popUp.BackButtonPressed -= BackButtonPressedHandler;
+ _popUp.TimedOut -= TimedOutHandler;
+ _popUp.Unrealize();
+ _popUp = null;
+ }
+ }
+
+ _isDisposed = true;
+ }
+
+ public void Show()
+ {
+ Console.WriteLine("[GraphicPopUpRenderer.Show] ");
+ _popUp.Show();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 ElmSharp;
+using System;
+using VoiceMemo.Views;
+using TForms = Xamarin.Forms.Forms;
+// @OLD VERSION OF XAMARIN.FORMS
+//using TForms = Xamarin.Forms.Platform.Tizen.Forms;
+
+namespace VoiceMemo.Tizen.Wearable.Renderers
+{
+ class ProgressbarPopupRenderer : IProgressbarPopup, IDisposable
+ {
+ Popup _popUp;
+ Popup _popUp2;
+ Layout _layout;
+ ProgressBar _progressbar;
+ bool _isDisposed = false;
+ public event EventHandler BackButtonPressed;
+ public event EventHandler TimedOut;
+
+ public ProgressbarPopupRenderer()
+ {
+ _popUp = new Popup(TForms.NativeParent)
+ {
+ Style = "circle",
+ Orientation = PopupOrientation.Center,
+ };
+ _popUp.BackButtonPressed += BackButtonPressedHandler;
+ _popUp.TimedOut += ProgressbarPopup_TimedOutHandler;
+ _popUp.Dismissed += _popUp_Dismissed;
+ _popUp.ShowAnimationFinished += _popUp_ShowAnimationFinished;
+
+ _layout = new Layout(_popUp);
+ _layout.SetTheme("layout", "popup", "content/circle");
+ _popUp.SetContent(_layout);
+
+ _progressbar = new ProgressBar(TForms.NativeParent)
+ {
+ Color = Color.FromRgb(77, 207, 255),
+ //BackgroundColor = Color.FromRgb(100, 255, 0),
+ //SpanSize = 50,
+ Style = "process",
+ IsPulseMode = true,
+ };
+ _progressbar.Deleted += _progressbar_Deleted;
+ _popUp.SetPartContent("elm.swallow.progress", _progressbar);
+
+ ///////////////
+ _popUp2 = new Popup(TForms.NativeParent)
+ {
+ Style = "toast/circle/check",
+ Orientation = PopupOrientation.Bottom,
+ //Timeout = Duration,
+ };
+ //_popUp2.SetPartText("elm.text", Text);
+ _popUp2.TimedOut += TimedOutHandler;
+ _popUp2.Dismissed += _popUp2_Dismissed;
+ }
+
+ private void _popUp_ShowAnimationFinished(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer._popUp_ShowAnimationFinished] ");
+ }
+
+ private void _popUp_Resized(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer._popUp_Resized] ");
+ }
+
+ private void _popUp_RenderPost(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer._popUp_RenderPost] ");
+ }
+
+ private void _popUp_Moved(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer._popUp_Moved] ");
+ }
+
+ private void _popUp2_Dismissed(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer._popUp2_Dismissed] ");
+ }
+
+ private void _popUp_Dismissed(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer._popUp_Dismissed] ");
+ }
+
+ /// <summary>
+ /// Invoked when progressbar is removed from Popup
+ /// </summary>
+ /// <param name="sender">sender object</param>
+ /// <param name="e">EventArgs</param>
+ private void _progressbar_Deleted(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer._progressbar_Deleted] ");
+ _popUp2.Show();
+ }
+
+ private void ProgressbarPopup_TimedOutHandler(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer.ProgressbarPopup_TimedOutHandler] ");
+ // Remove Progressbar from Popup when doing progressbar is done
+ _popUp.SetPartContent("elm.swallow.progress", null);
+ }
+
+ private void TimedOutHandler(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer.TimedOutHandler] 0");
+ TimedOut?.Invoke(this, EventArgs.Empty);
+ _popUp.Dismiss();
+ }
+
+ private void BackButtonPressedHandler(object sender, EventArgs e)
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer.BackButtonPressedHandler] ");
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ _popUp.Dismiss();
+ }
+
+ string _Text;
+ public string Text
+ {
+ get
+ {
+ return _Text;
+ }
+
+ set
+ {
+ if (_Text != value)
+ {
+ _Text = value;
+ Console.WriteLine("[Update] Text (" + _Text + ")");
+ _popUp2.SetPartText("elm.text", _Text);
+ }
+ }
+ }
+
+ double _Duration;
+ public double Duration
+ {
+ get
+ {
+ return _Duration;
+ }
+
+ set
+ {
+ if (_Duration != value)
+ {
+ _Duration = value;
+ Console.WriteLine("[Update] Duration (" + Duration + ")");
+ _popUp2.Timeout = _Duration;
+ }
+ }
+ }
+
+ string _ProgressbarText;
+ public string ProgressbarText
+ {
+ get
+ {
+ return _ProgressbarText;
+ }
+
+ set
+ {
+ if (_ProgressbarText != value)
+ {
+ _ProgressbarText = value;
+ Console.WriteLine("[Update & Apply] ProgressbarText (" + _ProgressbarText + ")");
+ _layout.SetPartText("elm.text", _ProgressbarText);
+ }
+ }
+ }
+
+ double _ProgressbarDuration;
+ public double ProgressbarDuration
+ {
+ get
+ {
+ return _ProgressbarDuration;
+ }
+
+ set
+ {
+ if (_ProgressbarDuration != value)
+ {
+ _ProgressbarDuration = value;
+ Console.WriteLine("[Update & Apply] ProgressbarDuration (" + _ProgressbarDuration + ")");
+ _popUp.Timeout = _ProgressbarDuration;
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ Console.WriteLine("[ProgressbarPopupRenderer.Dispose] ");
+ if (disposing)
+ {
+ if (_popUp != null)
+ {
+ _layout.Unrealize();
+ _layout = null;
+ _popUp.BackButtonPressed -= BackButtonPressedHandler;
+ _popUp.TimedOut -= TimedOutHandler;
+ _popUp.Unrealize();
+ _popUp = null;
+ }
+
+ if (_popUp2 != null)
+ {
+ _popUp2.TimedOut -= TimedOutHandler;
+ _popUp2.Unrealize();
+ _popUp2 = null;
+ }
+ }
+
+ _isDisposed = true;
+ }
+
+ public void Show()
+ {
+ Console.WriteLine("[ProgressbarPopupRenderer.Show] ");
+ _progressbar.Value = 0;
+ _popUp.Show();
+ _progressbar.PlayPulse();
+ }
+ }
+}
--- /dev/null
+//------------------------------------------------------------------------------
+// <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 VoiceMemo.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("VoiceMemo.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 Recording will be cancelled..
+ /// </summary>
+ public static string CancelRecording {
+ get {
+ return ResourceManager.GetString("CancelRecording", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to DELETE.
+ /// </summary>
+ public static string DELETE {
+ get {
+ return ResourceManager.GetString("DELETE", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Deleted..
+ /// </summary>
+ public static string Deleted {
+ get {
+ return ResourceManager.GetString("Deleted", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Deleting....
+ /// </summary>
+ public static string Deleting {
+ get {
+ return ResourceManager.GetString("Deleting", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Languages.
+ /// </summary>
+ public static string Languages {
+ get {
+ return ResourceManager.GetString("Languages", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No Recordings.
+ /// </summary>
+ public static string NoRecordings {
+ get {
+ return ResourceManager.GetString("NoRecordings", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Recognition failed.
+ /// </summary>
+ public static string RecognitionFailed {
+ get {
+ return ResourceManager.GetString("RecognitionFailed", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Recordings.
+ /// </summary>
+ public static string Recordings {
+ get {
+ return ResourceManager.GetString("Recordings", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Speech-to-text:.
+ /// </summary>
+ public static string SpeechToText {
+ get {
+ return ResourceManager.GetString("SpeechToText", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Voice memo.
+ /// </summary>
+ public static string StandByTitleMemo {
+ get {
+ return ResourceManager.GetString("StandByTitleMemo", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Voice recorder.
+ /// </summary>
+ public static string StandByTitleRecorder {
+ get {
+ return ResourceManager.GetString("StandByTitleRecorder", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Off.
+ /// </summary>
+ public static string SttOff {
+ get {
+ return ResourceManager.GetString("SttOff", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to On.
+ /// </summary>
+ public static string SttOn {
+ get {
+ return ResourceManager.GetString("SttOn", resourceCulture);
+ }
+ }
+ }
+}
--- /dev/null
+<?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="CancelRecording" xml:space="preserve">
+ <value>रिकॉर्डिंग रद्द की जाएगी।</value>
+ </data>
+ <data name="DELETE" xml:space="preserve">
+ <value>हटाएँ</value>
+ </data>
+ <data name="Deleted" xml:space="preserve">
+ <value>हटाया गया।</value>
+ </data>
+ <data name="Deleting" xml:space="preserve">
+ <value>हटाया जा रहा है...</value>
+ </data>
+ <data name="Languages" xml:space="preserve">
+ <value>भाषा</value>
+ </data>
+ <data name="NoRecordings" xml:space="preserve">
+ <value>कोई रिकॉर्डिंग्स नहीं हैं</value>
+ </data>
+ <data name="RecognitionFailed" xml:space="preserve">
+ <value>पहचान विफल रही</value>
+ </data>
+ <data name="Recordings" xml:space="preserve">
+ <value>रिकॉर्डिंग्स</value>
+ </data>
+ <data name="SpeechToText" xml:space="preserve">
+ <value>स्पीच-टू-टेक्स्ट:</value>
+ </data>
+ <data name="StandByTitleMemo" xml:space="preserve">
+ <value>वॉइस मेमो</value>
+ </data>
+ <data name="StandByTitleRecorder" xml:space="preserve">
+ <value>वॉइस रिकॉर्डर</value>
+ </data>
+ <data name="SttOff" xml:space="preserve">
+ <value>बंद</value>
+ </data>
+ <data name="SttOn" xml:space="preserve">
+ <value>चालू</value>
+ </data>
+</root>
\ No newline at end of file
--- /dev/null
+<?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="CancelRecording" xml:space="preserve">
+ <value>녹음을 취소합니다.</value>
+ </data>
+ <data name="DELETE" xml:space="preserve">
+ <value>삭제</value>
+ </data>
+ <data name="Deleted" xml:space="preserve">
+ <value>삭제했습니다.</value>
+ </data>
+ <data name="Deleting" xml:space="preserve">
+ <value>삭제 중...</value>
+ </data>
+ <data name="Languages" xml:space="preserve">
+ <value>언어</value>
+ </data>
+ <data name="NoRecordings" xml:space="preserve">
+ <value>녹음 파일 없음</value>
+ </data>
+ <data name="RecognitionFailed" xml:space="preserve">
+ <value>인식하지 못했습니다</value>
+ </data>
+ <data name="Recordings" xml:space="preserve">
+ <value>녹음 목록</value>
+ </data>
+ <data name="SpeechToText" xml:space="preserve">
+ <value>음성을 문자로 변환:</value>
+ </data>
+ <data name="StandByTitleMemo" xml:space="preserve">
+ <value>음성 메모</value>
+ </data>
+ <data name="StandByTitleRecorder" xml:space="preserve">
+ <value>음성 녹음</value>
+ </data>
+ <data name="SttOff" xml:space="preserve">
+ <value>사용 안 함</value>
+ </data>
+ <data name="SttOn" xml:space="preserve">
+ <value>사용 중</value>
+ </data>
+</root>
\ No newline at end of file
--- /dev/null
+<?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="CancelRecording" xml:space="preserve">
+ <value>Recording will be cancelled.</value>
+ </data>
+ <data name="DELETE" xml:space="preserve">
+ <value>DELETE</value>
+ </data>
+ <data name="Deleted" xml:space="preserve">
+ <value>Deleted.</value>
+ <comment>.</comment>
+ </data>
+ <data name="Deleting" xml:space="preserve">
+ <value>Deleting...</value>
+ </data>
+ <data name="Languages" xml:space="preserve">
+ <value>Languages</value>
+ </data>
+ <data name="NoRecordings" xml:space="preserve">
+ <value>No Recordings</value>
+ </data>
+ <data name="RecognitionFailed" xml:space="preserve">
+ <value>Recognition failed</value>
+ </data>
+ <data name="Recordings" xml:space="preserve">
+ <value>Recordings</value>
+ </data>
+ <data name="SpeechToText" xml:space="preserve">
+ <value>Speech-to-text:</value>
+ </data>
+ <data name="StandByTitleMemo" xml:space="preserve">
+ <value>Voice memo</value>
+ </data>
+ <data name="StandByTitleRecorder" xml:space="preserve">
+ <value>Voice recorder</value>
+ </data>
+ <data name="SttOff" xml:space="preserve">
+ <value>Off</value>
+ </data>
+ <data name="SttOn" xml:space="preserve">
+ <value>On</value>
+ </data>
+</root>
\ No newline at end of file
--- /dev/null
+/*
+ * 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 Tizen.Applications;
+
+
+namespace VoiceMemo.Services
+{
+ class AppDataService
+ {
+ private static readonly Lazy<AppDataService> instance = new Lazy<AppDataService>(() => new AppDataService());
+ public static AppDataService Instance
+ {
+ get => instance.Value;
+ }
+
+ readonly object _locker;
+ private AppDataService()
+ {
+ _locker = new object();
+ }
+
+ /// <summary>
+ /// Check if the specified key exists or not
+ /// </summary>
+ /// <param name="key">key string</param>
+ /// <returns>true if it exists</returns>
+ public bool Contain(string key)
+ {
+ return Preference.Contains(key);
+ }
+ /// <summary>
+ /// Get value for the specific key
+ /// </summary>
+ /// <param name="key">key string</param>
+ /// <returns>value string</returns>
+ public string GetValue(string key)
+ {
+ lock (_locker)
+ {
+ try
+ {
+ return Preference.Get<string>(key);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("[Preference.GetValue] key : " + key + ", exception:" + ex.Message);
+ return null;
+ }
+ }
+ }
+ /// <summary>
+ /// Set key-value pair
+ /// </summary>
+ /// <param name="key">key to save</param>
+ /// <param name="value">value to save</param>
+ public void SetValue(string key, string value)
+ {
+ lock (_locker)
+ {
+ try
+ {
+ Preference.Set(key, value);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("[Preference.SetValue] key : " + key + ", exception:" + ex.Message);
+ }
+ }
+ }
+
+ public T GetValue<T>(string key)
+ {
+ object result = null;
+ lock (_locker)
+ {
+ try
+ {
+ if (typeof(T) == typeof(bool))
+ {
+ result = Preference.Get<bool>(key);
+ }
+ else if (typeof(T) == typeof(int))
+ {
+ result = Preference.Get<int>(key);
+ }
+ else if (typeof(T) == typeof(double))
+ {
+ result = Preference.Get<double>(key);
+ }
+ else if (typeof(T) == typeof(string))
+ {
+ result = Preference.Get<string>(key);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("[Preference.GetValue] key : " + key + ", exception:" + ex.Message);
+ }
+ }
+
+ return (result != null) ? (T)result : default(T);
+ }
+
+ /// <summary>
+ /// Set key-value pair
+ /// </summary>
+ /// <param name="key">key to save</param>
+ /// <param name="value">value to save</param>
+ public void SetValue(string key, object value)
+ {
+ lock (_locker)
+ {
+ try
+ {
+ Preference.Set(key, value);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("[Preference.SetValue] key : " + key + ", exception:" + ex.Message);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 Native = Tizen.Applications;
+using VoiceMemo.Services;
+//using VoiceMemo.Tizen.Wearable.Services;
+using Xamarin.Forms;
+
+
+namespace VoiceMemo.Services
+{
+ /// <summary>
+ /// class AppTerminator
+ /// It provides a way to kill this application
+ /// </summary>
+ class AppTerminator
+ {
+ public AppTerminator()
+ {
+ }
+
+ /// <summary>
+ /// Terminate the the current application.
+ /// </summary>
+ public void TerminateApp()
+ {
+ Native.Application.Current.Exit();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.Threading.Tasks;
+using Tizen.Multimedia;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Services
+{
+ /// <summary>
+ /// AudioPlayerService
+ /// The main role is playing the recorded voice memo.
+ /// Use Tizen.Multimedia.Player
+ /// </summary>
+ class AudioPlayService
+ {
+ private static readonly Lazy<AudioPlayService> instance = new Lazy<AudioPlayService>(() => new AudioPlayService());
+ public static AudioPlayService Instance
+ {
+ get => instance.Value;
+ }
+ // Constructor
+ private AudioPlayService()
+ {
+ if (audioVolume == null)
+ {
+ audioVolume = AudioManager.VolumeController;
+ _state = AudioPlayState.Init;
+ }
+
+ // Create a player
+ if (player == null)
+ {
+ player = new Player();
+ if (player.State == PlayerState.Idle)
+ {
+ _state = StateConvert(player.State);
+ MessagingCenter.Send<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged, StateConvert(player.State));
+ }
+
+ player.Volume = (float)Volume / GetMaxVolume();
+ player.PlaybackCompleted += Player_PlaybackCompleted;
+ player.PlaybackInterrupted += Player_PlaybackInterrupted;
+ player.ErrorOccurred += Player_ErrorOccurred;
+ }
+
+ if (player.State == PlayerState.Ready)
+ {
+ _state = StateConvert(player.State);
+ MessagingCenter.Send<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged, StateConvert(player.State));
+ }
+ }
+
+ /// <summary>
+ /// Initialize Player
+ /// When PlaybackPage is shown,
+ /// </summary>
+ /// <param name="path">audio file path to play</param>
+ /// <returns>Task</returns>
+ async public Task Init(string path)
+ {
+ player.SetSource(new MediaUriSource(path));
+ await Task.Run(() => player.PrepareAsync());
+ Start();
+ }
+
+ AudioVolume audioVolume;
+ Player player;
+ AudioPlayState _state;
+ /// <summary>
+ /// The state of audio play
+ /// </summary>
+ public AudioPlayState State
+ {
+ get
+ {
+ return _state;
+ }
+ }
+
+ // Indicate that Mute is on or off
+ public bool Muted
+ {
+ get { return player.Muted; }
+ set
+ {
+ if (player.Muted != value)
+ {
+ player.Muted = value;
+ }
+ }
+ }
+ /// <summary>
+ /// Media volume
+ /// </summary>
+ public int Volume
+ {
+ get
+ {
+ return audioVolume.Level[AudioVolumeType.Media];
+ }
+ }
+ /// <summary>
+ /// Make volume level up
+ /// </summary>
+ public void IncreaseVolume()
+ {
+ // if volume level is already max, ignore it.
+ if (audioVolume.Level[AudioVolumeType.Media] == audioVolume.MaxLevel[AudioVolumeType.Media])
+ {
+ return;
+ }
+
+ AudioManager.VolumeController.Level[AudioVolumeType.Media] = AudioManager.VolumeController.Level[AudioVolumeType.Media] + 1;
+ }
+ /// <summary>
+ /// Make volume level down
+ /// </summary>
+ public void DecreaseVolume()
+ {
+ // ignore when volume level has already reached zero
+ if (audioVolume.Level[AudioVolumeType.Media] == 0)
+ {
+ return;
+ }
+
+ AudioManager.VolumeController.Level[AudioVolumeType.Media] = AudioManager.VolumeController.Level[AudioVolumeType.Media] - 1;
+ }
+ /// <summary>
+ /// Get the maximum volume value
+ /// </summary>
+ /// <returns>maximum volume value</returns>
+ public int GetMaxVolume()
+ {
+ return audioVolume.MaxLevel[AudioVolumeType.Media];
+ }
+ /// <summary>
+ /// Register callback to get notified when the volume has been changed
+ /// </summary>
+ public void RegisterVolumeChangedCallback()
+ {
+ audioVolume.Changed += AudioVolume_Changed;
+ }
+ // <summary>
+ /// Unregister callback to get notified when the volume has been changed
+ /// </summary>
+ public void UnregisterVolumeChangedCallback()
+ {
+ audioVolume.Changed -= AudioVolume_Changed;
+ }
+ /// <summary>
+ /// Start to play voice audio file
+ /// </summary>
+ public void Start()
+ {
+ if (player.State == PlayerState.Ready || player.State == PlayerState.Paused)
+ {
+ // Player's state will be "Playing"
+ player.Start();
+ if (player.State == PlayerState.Playing)
+ {
+ _state = StateConvert(player.State);
+ MessagingCenter.Send<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged, StateConvert(player.State));
+ }
+ }
+ }
+ /// <summary>
+ /// Pause to play audio file
+ /// </summary>
+ public void Pause()
+ {
+ if (player.State == PlayerState.Playing)
+ {
+ // Player's state will be "Paused"
+ player.Pause();
+ if (player.State == PlayerState.Paused)
+ {
+ _state = StateConvert(player.State);
+ MessagingCenter.Send<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged, StateConvert(player.State));
+ }
+
+ Console.WriteLine(" AudioPlayService.Pause() : ", (player.State != PlayerState.Paused) ? "Failed to Pause()" : "Successfully finish to Pause()");
+ }
+ }
+
+ /// <summary>
+ /// If you want to leave PlaybackPage, you need to
+ /// </summary>
+ public void Stop()
+ {
+ if (player.State == PlayerState.Playing || player.State == PlayerState.Paused)
+ {
+ // Player's state will be "Ready"
+ player.Stop();
+ if (player.State == PlayerState.Ready)
+ {
+ _state = StateConvert(player.State);
+ MessagingCenter.Send<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged, StateConvert(player.State));
+ }
+ }
+ // Player's state will be "Idle"
+ player.Unprepare();
+ if (player.State == PlayerState.Idle)
+ {
+ _state = StateConvert(player.State);
+ MessagingCenter.Send<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged, StateConvert(player.State));
+ }
+ }
+ /// <summary>
+ /// Destroy Audio Play Service
+ /// </summary>
+ public void Destroy()
+ {
+ if (player.State == PlayerState.Playing || player.State == PlayerState.Paused)
+ {
+ Stop();
+ }
+
+ player.Dispose();
+ }
+ /// <summary>
+ /// Invoked when audio volume level has been changed
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">VolumeChangedEventArgs</param>
+ private void AudioVolume_Changed(object sender, VolumeChangedEventArgs e)
+ {
+ //MessagingCenter.Send<IPlayService, int>(this, "VOLUME_CHANGE", e.Level);
+ VolumeChanged?.Invoke(this, new AudioVolumeChangedEventArgs(e.Level));
+ }
+ /// <summary>
+ /// Invoked when an error occurs at playing
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">PlayerErrorOccurredEventArgs</param>
+ private void Player_ErrorOccurred(object sender, PlayerErrorOccurredEventArgs e)
+ {
+ Console.WriteLine(" Player_ErrorOccurred :" + e.Error);
+ }
+ /// <summary>
+ /// Invoked when an interruption occurs
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">PlaybackInterruptedEventArgs</param>
+ private void Player_PlaybackInterrupted(object sender, PlaybackInterruptedEventArgs e)
+ {
+ Console.WriteLine(" Player_PlaybackInterrupted :" + e.Reason);
+ }
+ /// <summary>
+ /// Invoked when playing audio is done
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">EventArgs</param>
+ private void Player_PlaybackCompleted(object sender, EventArgs e)
+ {
+ Stop();
+ AudioPlayFinished?.Invoke(this, e);
+ }
+ /// <summary>
+ /// player's state converter
+ /// </summary>
+ /// <param name="state">PlayerState</param>
+ /// <returns>AudioPlayState</returns>
+ AudioPlayState StateConvert(PlayerState state)
+ {
+ switch (state)
+ {
+ case PlayerState.Idle:
+ return AudioPlayState.Idle;
+ case PlayerState.Paused:
+ return AudioPlayState.Paused;
+ case PlayerState.Playing:
+ return AudioPlayState.Playing;
+ case PlayerState.Preparing:
+ return AudioPlayState.Preparing;
+ case PlayerState.Ready:
+ return AudioPlayState.Ready;
+ default:
+ return AudioPlayState.Init;
+ }
+ }
+ /// <summary>
+ /// Called whenever volume level has been changed
+ /// </summary>
+ public event EventHandler<AudioVolumeChangedEventArgs> VolumeChanged;
+ /// <summary>
+ /// Called when playing audio file is done
+ /// </summary>
+ public event EventHandler AudioPlayFinished;
+ }
+
+ public class AudioVolumeChangedEventArgs : EventArgs
+ {
+ public AudioVolumeChangedEventArgs(int level)
+ {
+ Level = level;
+ }
+
+ /// <summary>
+ /// Gets the new volume level.
+ /// </summary>
+ /// <value>The new volume level.</value>
+ /// <since_tizen> 3 </since_tizen>
+ public int Level { get; }
+ }
+
+ public enum AudioPlayState
+ {
+ Init,
+ Idle,
+ Ready,
+ Playing,
+ Preparing,
+ Paused,
+ }
+}
--- /dev/null
+/*
+ * 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.Linq;
+using System.Threading.Tasks;
+using Tizen.Multimedia;
+using Tizen.System;
+using SystemStorage = Tizen.System.Storage;
+using VoiceMemo.Services;
+//using VoiceMemo.Tizen.Wearable.Services;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+using System.IO;
+using Tizen.Wearable.CircularUI.Forms;
+
+
+namespace VoiceMemo.Services
+{
+ /// <summary>
+ /// AudioRecordService
+ /// The main role is recording voice.
+ /// Use Tizen.Multimedia and Tizen.System classes
+ /// </summary>
+ public class AudioRecordService
+ {
+ private static readonly Lazy<AudioRecordService> instance = new Lazy<AudioRecordService>(() => new AudioRecordService());
+ public static AudioRecordService Instance
+ {
+ get => instance.Value;
+ }
+
+ // Time limit of stt recording is 5 minutes.
+ public const int STT_RECORDING_TIME_LIMIT = 5 * 60;
+ // Time limit of recording is 30 minutes.
+ public const int VOICE_ONLY_RECORDING_TIME_LIMIT = 30 * 60;
+
+ // based on document(https://developer.tizen.org/development/guides/.net-application/media-and-camera/media-recording)
+ const int AMC_CODEC_AUDIO_SAMPLE_RATE = 8000;
+ AudioRecorder _recorder;
+ // based on document (https://developer.tizen.org/development/guides/.net-application/media-and-camera/media-recording)
+ // The following file formats are supported:
+ // Audio: M4A and AMR
+ const RecorderFileFormat AudioFileFormat = RecorderFileFormat.Amr;
+ public static int numbering;
+ //string StoragePath;
+ AudioRecordState _state;
+ string audioStoragePath;
+ string filepath;
+ private Action<Object, AudioRecordState, AudioRecordState> stateCallbacks;
+ /// <summary>
+ /// Get the state of audio recording
+ /// </summary>
+ public AudioRecordState State
+ {
+ get
+ {
+ return _state;
+ }
+ }
+
+ private AudioRecordService()
+ {
+ _state = AudioRecordState.Init;
+ numbering = 0;
+
+ // Create an audio recorder
+ if (_recorder == null)
+ {
+ // find out the available audio codec and file format
+ RecorderAudioCodec AudioCodec = RecorderAudioCodec.Amr;
+ foreach (RecorderAudioCodec codec in Recorder.GetSupportedAudioCodecs())
+ {
+ //Console.WriteLine("RecorderAudioCodec : " + codec);
+ foreach (RecorderFileFormat format in codec.GetSupportedFileFormats())
+ {
+ //Console.WriteLine(" RecorderFileFormat : " + format);
+ if (format == AudioFileFormat)
+ {
+ AudioCodec = codec;
+ break;
+ }
+ }
+ }
+ //
+
+ Console.WriteLine("+++ Selected AudioCodec : " + AudioCodec + " Format : " + AudioFileFormat);
+ // 2017-12-08: Todo need to roll back..
+ //_recorder = new AudioRecorder(AudioCodec, AudioFileFormat);
+ //RecorderAudioCodec audioCodec, RecorderFileFormat fileFormat
+ _recorder = new AudioRecorder(AudioCodec, AudioFileFormat);
+ _recorder.StateChanged += AudioRecorder_StateChanged;
+ _recorder.RecordingStatusChanged += AudioRecorder_RecordingStatusChanged;
+ _recorder.RecordingLimitReached += AudioRecorder_RecordingLimitReached;
+ _recorder.ErrorOccurred += AudioRecorder_ErrorOccurred;
+ AudioRecorder.DeviceStateChanged += AudioRecorder_DeviceStateChanged;
+ _recorder.Interrupting += AudioRecorder_Interrupting;
+ //audioRecorder.ApplyAudioStreamPolicy(new AudioStreamPolicy(AudioStreamType.Media));
+ //audioRecorder.AudioChannels = 2;
+ _recorder.AudioDevice = RecorderAudioDevice.Mic;
+ _recorder.AudioBitRate = 128000;
+ _recorder.AudioSampleRate = AMC_CODEC_AUDIO_SAMPLE_RATE;
+
+ if (_recorder.State != RecorderState.Idle)
+ {
+ Console.WriteLine("AudioRecordService() : Invalid State (" + _recorder.State + ")" + "...may retry?");
+ }
+
+ _recorder.Prepare();
+ if (_recorder.State != RecorderState.Ready)
+ {
+ Console.WriteLine("AudioRecordService() : Invalid State (" + _recorder.State + ")" + "...may retry?");
+ }
+
+ try
+ {
+ SystemStorage internalStorage = StorageManager.Storages.Where(s => s.StorageType == StorageArea.Internal).FirstOrDefault();
+ var SoundsDir = internalStorage.GetAbsolutePath(DirectoryType.Sounds);
+ audioStoragePath = Path.Combine(SoundsDir, "DotnetVoiceMemo");
+ // Create directory to save audio files
+ Directory.CreateDirectory(audioStoragePath);
+ }
+ catch (Exception e)
+ {
+ Toast.DisplayText(e.Message + " - DotnetVoiceMemo directory creation failed.");
+ }
+ }
+ }
+
+ // The model class object for RecordingPage
+ RecordingPageModel _viewModel;
+ public RecordingPageModel ViewModel
+ {
+ get
+ {
+ return _viewModel;
+ }
+
+ set
+ {
+ if (_viewModel != value)
+ {
+ _viewModel = value;
+ }
+ }
+ }
+
+ ~AudioRecordService()
+ {
+ Console.WriteLine(" @@@ ~AudioRecordService() ");
+ Destroy();
+ }
+
+ /// <summary>
+ /// Start recording voice
+ /// </summary>
+ /// <param name="title">file path to store audio file</param>
+ /// <param name="sttOn">indicate that Speech-To-Text service is on or not</param>
+ /// <returns>Task<bool></returns>
+ public Task<bool> Start(string title, bool sttOn)
+ {
+ return Task.Run(() =>
+ {
+ if (sttOn)
+ {
+ // Recording time limits 5 minutes when speech-to-text function is enabled
+ _recorder.TimeLimit = STT_RECORDING_TIME_LIMIT;
+ }
+ else
+ {
+ // Recording time limits 30 minutes when speech-to-text function is disabled
+ _recorder.TimeLimit = VOICE_ONLY_RECORDING_TIME_LIMIT;
+ }
+
+ try
+ {
+ filepath = Path.Combine(audioStoragePath, title + "_T_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".amr");
+ _recorder.Start(filepath);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(" _recorder.Start() Exception : " + e.Message);
+ HandleError("Start", e);
+ // TODO: Phase 2
+ // Toast Popup Message should be shown.
+ // Message : Recording failed.
+ }
+
+ return true;
+ });
+ }
+ /// <summary>
+ /// Pause recording voice
+ /// </summary>
+ public void Pause()
+ {
+ if (_recorder.State != RecorderState.Recording)
+ {
+ Console.WriteLine("AudioRecordService.Pause : Invalid State (" + _recorder.State + ")");
+ return;
+ }
+
+ try
+ {
+ _recorder.Pause();
+ if (_recorder.State != RecorderState.Paused)
+ {
+ Console.WriteLine(" AudioRecordService.Pause state error");
+ }
+ }
+ catch (Exception e)
+ {
+ HandleError("Pause", e);
+ }
+ }
+ /// <summary>
+ /// Resume recording voice
+ /// </summary>
+ public void Resume()
+ {
+ if (_recorder.State != RecorderState.Paused)
+ {
+ Console.WriteLine("AudioRecordService.Resume : Invalid State (" + _recorder.State + ")");
+ return;
+ }
+
+ try
+ {
+ _recorder.Resume();
+ if (_recorder.State != RecorderState.Recording)
+ {
+ Console.WriteLine(" AudioRecordService.Resume state error");
+ }
+ }
+ catch (Exception e)
+ {
+ HandleError("Resume", e);
+ }
+ }
+ /// <summary>
+ /// Cancel recording voice
+ /// </summary>
+ public void Cancel()
+ {
+ if (_recorder.State == RecorderState.Idle || _recorder.State == RecorderState.Ready)
+ {
+ Console.WriteLine("AudioRecordService.Cancel : No need to cancel in State (" + _recorder.State + ") ");
+ return;
+ }
+
+ try
+ {
+ _recorder.Cancel();
+ if (_recorder.State != RecorderState.Ready)
+ {
+ Console.WriteLine(" AudioRecordService.Cancel state error");
+ }
+ }
+ catch (Exception e)
+ {
+ HandleError("Cancel", e);
+ }
+ }
+ /// <summary>
+ /// Stop recording and save the voice recording file
+ /// </summary>
+ /// <returns>file path to save audio file</returns>
+ public string Save()
+ {
+ if ((_recorder.State == RecorderState.Idle) || (_recorder.State == RecorderState.Ready))
+ {
+ Console.WriteLine("AudioRecordService.Save : No need to cancel in State (" + _recorder.State + ") ");
+ return null;
+ }
+
+ try
+ {
+ // Stop recording and save the result in file
+ _recorder.Commit();
+ if (_recorder.State != RecorderState.Ready)
+ {
+ Console.WriteLine(" AudioRecordService.Save state error");
+ // TODO : Handle error case
+ Toast.DisplayText("Fail to save audio file because of recording fw issue.");
+ }
+
+ string name = filepath.Substring(filepath.IndexOf(' '));
+ string final = name.Substring(0, name.IndexOf("_T_"));
+ //string final = name.Substring(name.IndexOf("_T_"), name.Length - name.IndexOf("_T_"));
+ numbering = Convert.ToInt32(final);
+ Console.WriteLine(" ###### Voice File name - " + name + " --> final : " + final + " , numbering : " + numbering);
+ }
+ catch (Exception e)
+ {
+ HandleError("Commit", e);
+ }
+
+ return filepath;
+ }
+
+ public void Destroy()
+ {
+ try
+ {
+ // release resources
+ _recorder.StateChanged -= AudioRecorder_StateChanged;
+ _recorder.RecordingStatusChanged -= AudioRecorder_RecordingStatusChanged;
+ _recorder.RecordingLimitReached -= AudioRecorder_RecordingLimitReached;
+ _recorder.ErrorOccurred -= AudioRecorder_ErrorOccurred;
+ AudioRecorder.DeviceStateChanged -= AudioRecorder_DeviceStateChanged;
+ _recorder.Interrupting -= AudioRecorder_Interrupting;
+ _recorder.Unprepare();
+ if (_recorder.State != RecorderState.Idle)
+ {
+ Console.WriteLine(" ########################################");
+ Console.WriteLine(" AudioRecordService.Destroy state error");
+ Console.WriteLine(" ########################################");
+ }
+
+ Console.WriteLine(" AudioRecordService.Destroy() " + _recorder.State);
+ _recorder.Dispose();
+ }
+ catch (Exception e)
+ {
+ HandleError("Destroy", e);
+ }
+ }
+ /// <summary>
+ /// Get voice recording level
+ /// </summary>
+ /// <returns>volume level</returns>
+ public double GetRecordingLevel()
+ {
+ return _recorder.GetPeakAudioLevel();
+ }
+
+ void HandleError(string method, Exception e)
+ {
+ switch (_recorder.State)
+ {
+ case RecorderState.Idle:
+ Console.WriteLine("[RecordingService.HandleError in " + method + "()] State:Idle " + e.Message);
+ break;
+ case RecorderState.Paused:
+ Console.WriteLine("[RecordingService.HandleError in " + method + "()] State:Paused " + e.Message);
+ Cancel();
+ break;
+ case RecorderState.Ready:
+ Console.WriteLine("[RecordingService.HandleError in " + method + "()] State:Ready " + e.Message);
+ break;
+ case RecorderState.Recording:
+ Console.WriteLine("[RecordingService.HandleError in " + method + "()] State:Recording " + e.Message);
+ Cancel();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void AudioRecorder_Interrupting(object sender, RecorderInterruptingEventArgs e)
+ {
+ Console.WriteLine("");
+ Console.WriteLine("[AudioRecorder_Interrupting] : " + e.Reason);
+ }
+
+ private void AudioRecorder_ErrorOccurred(object sender, RecordingErrorOccurredEventArgs e)
+ {
+ Console.WriteLine("");
+ Console.WriteLine("[AudioRecorder_ErrorOccurred] : " + e.Error + ", Type: " + e.GetType() + ", " + e.ToString());
+ // TODO: Phase 2
+ // Toast Popup Message should be shown.
+ // Message : Recording failed.
+ }
+
+ private void AudioRecorder_RecordingLimitReached(object sender, RecordingLimitReachedEventArgs e)
+ {
+ Console.WriteLine("[AudioRecorder_RecordingLimitReached] type: " + e.Type);
+ // Request stop to save voice recording
+ ViewModel.RequestCommand.Execute(RecordingCommandType.Stop);
+ }
+
+ private void AudioRecorder_RecordingStatusChanged(object sender, RecordingStatusChangedEventArgs e)
+ {
+ //Console.WriteLine("[AudioRecorder_RecordingStatusChanged] ElapsedTime:" + e.ElapsedTime + ", FileSize:" + e.FileSize);
+ }
+
+ private void AudioRecorder_DeviceStateChanged(object sender, RecorderDeviceStateChangedEventArgs e)
+ {
+ Console.WriteLine("");
+ Console.WriteLine("[AudioRecorder_DeviceStateChanged] deviceState : " + e.DeviceState);
+ Console.WriteLine("");
+ }
+
+ private void AudioRecorder_StateChanged(object sender, RecorderStateChangedEventArgs e)
+ {
+ Console.WriteLine("[AudioRecorder_StateChanged] " + e.PreviousState + " --> " + e.CurrentState);
+ switch (e.CurrentState)
+ {
+ case RecorderState.Idle:
+ _state = AudioRecordState.Idle;
+ break;
+ case RecorderState.Paused:
+ _state = AudioRecordState.Paused;
+ break;
+ case RecorderState.Ready:
+ _state = AudioRecordState.Ready;
+ break;
+ case RecorderState.Recording:
+ _state = AudioRecordState.Recording;
+ break;
+ }
+
+ stateCallbacks(sender, (AudioRecordState)e.PreviousState, (AudioRecordState)e.CurrentState);
+ }
+
+ public void RegisterStateCallbacks(Action<Object, AudioRecordState, AudioRecordState> callback)
+ {
+ stateCallbacks = callback;
+ }
+ }
+
+ /// <summary>
+ /// Audio recording state
+ /// </summary>
+ public enum AudioRecordState
+ {
+ Init,
+ Idle,
+ Ready,
+ Recording,
+ Paused,
+ }
+}
--- /dev/null
+/*
+ * 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 System.Linq;
+using Tizen.Applications;
+using Tizen.System;
+
+using SQLite;
+using SQLitePCL;
+
+namespace VoiceMemo.Services
+{
+ public static class Features
+ {
+ // Required features
+ // check microphone feature to use a mic for voice recording
+ public const string Microphone = "http://tizen.org/feature/microphone";
+ // check speech.recognition feature to use stt service
+ public const string STT = "http://tizen.org/feature/speech.recognition";
+ }
+ /// <summary>
+ /// Class DeviceInformation
+ /// It provides following functions
+ /// - device capabilities
+ /// - file path in internal storage
+ /// - application's state(e.g. Foreground, Background)
+ /// </summary>
+ public class DeviceInformationService
+ {
+ private static readonly Lazy<DeviceInformationService> instance = new Lazy<DeviceInformationService>(() => new DeviceInformationService());
+ public static DeviceInformationService Instance
+ {
+ get => instance.Value;
+ }
+
+ const int OneKiloBytes = 1024;
+ // indicate that speech-to-text feature is supported or not
+ internal static bool isSpeechRecognitionSupported = false;
+ // indicate that mic is supported or not
+ internal static bool isMicrophoneSupported = false;
+ internal static bool isFeatureSupported = false;
+
+ public const string LastUsedID = "LAST_SAVED_ID_NUMBER";
+ int _numbering;
+
+ public int LastStoredFileIndex
+ {
+ get
+ {
+ return _numbering;
+ }
+ }
+
+ private DeviceInformationService()
+ {
+ // Check whether STT feature is supported or not
+ Information.TryGetValue(Features.STT, out isSpeechRecognitionSupported);
+ // Check whether mic is available or not
+ Information.TryGetValue(Features.Microphone, out isMicrophoneSupported);
+ isFeatureSupported = isSpeechRecognitionSupported && isMicrophoneSupported;
+
+ bool existing = Preference.Contains(LastUsedID);
+ if (existing)
+ {
+ _numbering = Preference.Get<int>(LastUsedID);
+ }
+ else
+ {
+ _numbering = 0;
+ }
+ }
+
+ /// <summary>
+ /// Indicate speech recognition feature is supported or not
+ /// </summary>
+ public bool SpeechRecognitionAvailable
+ {
+ get
+ {
+ return isFeatureSupported;
+ }
+ }
+
+ ApplicationRunningContext _context;
+ /// <summary>
+ /// Application's execution state
+ /// </summary>
+ public AppState AppState
+ {
+ get
+ {
+
+ if (_context == null)
+ {
+ var appInfo = Application.Current.ApplicationInfo;
+ _context = new ApplicationRunningContext(appInfo.ApplicationId);
+ }
+
+ if (_context.State == ApplicationRunningContext.AppState.Background)
+ {
+ return AppState.Background;
+ }
+ else if (_context.State == ApplicationRunningContext.AppState.Foreground)
+ {
+ return AppState.Foreground;
+ }
+ else if (_context.State == ApplicationRunningContext.AppState.Terminated)
+ {
+ return AppState.Terminated;
+ }
+ else
+ {
+ Console.WriteLine("App State : " + _context.State);
+ return AppState.Undefined;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Indicate that there is enough storage available to save audio file
+ /// </summary>
+ public bool StorageAvailable
+ {
+ get
+ {
+ // Check if internal storage has sufficient storage available or not
+ Storage internalStorage = StorageManager.Storages.Where(s => s.StorageType == StorageArea.Internal).FirstOrDefault();
+ // 'AvaliableSpace' is deprecated in Tizen 5.0
+ // In Tizen 5.0, 'AvaliableSpace' is recommended instead of 'AvaliableSpace'.
+ return ((internalStorage.AvaliableSpace / OneKiloBytes) > 105) ? true : false;
+ }
+ }
+
+ /// <summary>
+ /// Get the path of database file for this app
+ /// </summary>
+ /// <param name="filename">Database file name</param>
+ /// <returns>The full path of Database file</returns>
+ public string GetLocalDBFilePath(string filename)
+ {
+ raw.SetProvider(new SQLite3Provider_sqlite3());
+ raw.FreezeProvider(true);
+ string path = Application.Current.DirectoryInfo.Data;
+ return Path.Combine(path, filename);
+ }
+ }
+
+ public enum AppState
+ {
+ Background,
+ Foreground,
+ Terminated,
+ Undefined,
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Tizen.Wearable.Services;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Services
+{
+ class LocaleService
+ {
+ private static readonly Lazy<LocaleService> instance = new Lazy<LocaleService>(() => new LocaleService());
+
+ public static LocaleService Instance { get => instance.Value; }
+ public CultureInfo CurrentCultureInfo;
+
+ private LocaleService()
+ {
+ CurrentCultureInfo = GetCurrentCultureInfo();
+ // To get notified when system locale settings has been changed1
+ //Trace.StartFrom("LocaleService()-LocaleLanguageChanged");
+ //SystemSettings.LocaleLanguageChanged += SystemSettings_LocaleLanguageChanged;
+ //Trace.End();
+ }
+
+ ///// <summary>
+ ///// Get the current CultureInfo
+ ///// </summary>
+ //public CultureInfo CurrentCultureInfo
+ //{
+ // get
+ // {
+ // return _currentCultureInfo;
+ // }
+ //}
+
+ /// <summary>
+ /// Get CultureInfo from the locale information
+ /// </summary>
+ /// <returns>CultureInfo</returns>
+ public CultureInfo GetCurrentCultureInfo()
+ {
+ var netLanguage = "en";
+ var TizenLocale = SystemSettings.LocaleLanguage;
+ //Console.WriteLine("[GetCurrentCultureInfo] TizenLocale (" + TizenLocale + ")");
+ netLanguage = TizenToDotnetLanguage(TizenLocale.ToString().Replace("_", "-"));
+ //Console.WriteLine("[GetCurrentCultureInfo] netLanguage (" + netLanguage + ")");
+ CultureInfo info = null;
+ try
+ {
+ info = new CultureInfo(netLanguage);
+ }
+ catch (CultureNotFoundException e1)
+ {
+ Console.WriteLine("[GetCurrentCultureInfo] 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 SystemSettings_LocaleLanguageChanged(object sender, LocaleLanguageChangedEventArgs e)
+ {
+ CultureInfo info = GetCurrentCultureInfo();
+ CurrentCultureInfo = info;
+ // Notify the change of locale information
+ MessagingCenter.Send<LocaleService, CultureInfo>(this, MessageKeys.LanguageChanged, info);
+ }
+
+ string TizenToDotnetLanguage(string androidLanguage)
+ {
+ var netLanguage = androidLanguage;
+ //certain languages need to be converted to CultureInfo equivalent
+ switch (androidLanguage)
+ {
+ 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;
+ //case "ar-ae": // Arabic
+ // netLanguage = "ar-sa"; // closest supported for .Net : Arabic (Saudi Arabia)
+ // break;
+ // add more application-specific cases here (if required)
+ // ONLY use cultures that have been tested and known to work
+ }
+
+ //Console.WriteLine(".NET Language/Locale:" + netLanguage);
+ return netLanguage;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 Tizen.Content.MediaContent;
+using VoiceMemo.Models;
+using VoiceMemo.Services;
+//using VoiceMemo.Tizen.Wearable.Services;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Services
+{
+ class MediaContentService
+ {
+ private static readonly Lazy<MediaContentService> instance = new Lazy<MediaContentService>(() => new MediaContentService());
+ public static MediaContentService Instance { get => instance.Value; }
+ MediaDatabase mediaDB;
+ MediaInfoCommand mediaInfoCmd;
+
+ private MediaContentService()
+ {
+ try
+ {
+ mediaDB = new MediaDatabase();
+#if media_svc_get_storage_id_failed_return_minus_2
+ //mediaDB.Connect();
+ //MediaDatabase.FolderUpdated += MediaDatabase_FolderUpdated;
+ //MediaDatabase.MediaInfoUpdated += MediaDatabase_MediaInfoUpdated;
+ //mediaInfoCmd = new MediaInfoCommand(mediaDB);
+#endif
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(" FAILED MediaContentService() : " + e.Message);
+ MessagingCenter.Send(this, MessageKeys.ErrorOccur, e);
+ }
+ }
+
+ /// <summary>
+ /// Destroy MediaContentService
+ /// </summary>
+ public void Destroy()
+ {
+ if (mediaDB != null)
+ {
+ Console.WriteLine("MediaContentService.Destroy() ....");
+#if media_svc_get_storage_id_failed_return_minus_2
+ mediaDB.Disconnect();
+#endif
+ mediaDB.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Get Record from media file
+ /// </summary>
+ /// <param name="path">recorded audio file</param>
+ /// <param name="SttOn">stt on/off mode</param>
+ /// <returns>Record</returns>
+ public Record GetMediaInfo(string path, bool SttOn)
+ {
+ Record record = null;
+ try
+ {
+ // TODO : scan
+#if media_svc_get_storage_id_failed_return_minus_2
+#else
+ mediaDB.Connect();
+#endif
+ mediaDB.ScanFile(path);
+#if media_svc_get_storage_id_failed_return_minus_2
+#else
+ mediaInfoCmd = new MediaInfoCommand(mediaDB);
+#endif
+ AudioInfo info = mediaInfoCmd.Add(path) as AudioInfo;
+#if media_svc_get_storage_id_failed_return_minus_2
+#else
+ mediaDB.Disconnect();
+#endif
+
+ //int minutes = info.Duration / 60000;
+ //int seconds = (info.Duration - minutes * 60000) / 1000;
+ //string duration = String.Format("{0:00}:{1:00}", minutes, seconds);
+ record = new Record
+ {
+ _id = AudioRecordService.numbering,
+ Title = info.Title.Substring(0, info.Title.IndexOf("_T_")),
+ Date = info.DateModified.ToLocalTime().ToString("t"),
+ Duration = info.Duration,
+ Path = path,
+ SttEnabled = SttOn,
+ };
+ Console.WriteLine(" ** Record :" + record);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("###########################################");
+ Console.WriteLine(" FAILED GetMediaInfo : " + e.Message + ", " + e.Source);
+ Console.WriteLine("###########################################");
+ MessagingCenter.Send(this, MessageKeys.ErrorOccur, e);
+ }
+
+ return record;
+ }
+
+ /// <summary>
+ /// Delete audio file from internal storage
+ /// </summary>
+ /// <param name="Path">file path to delete</param>
+ /// <returns>true if the file is successfully deleted.</returns>
+ public bool RemoveMediaFile(string Path)
+ {
+ try
+ {
+ File.Delete(Path);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("[MediaContentService.RemoveMediaFile] Path(" + Path + ") error :" + e.Message);
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.Threading.Tasks;
+using Tizen.Uix.Stt;
+using VoiceMemo.Services;
+//using VoiceMemo.Tizen.Wearable.Services;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+
+
+namespace VoiceMemo.Services
+{
+ /// <summary>
+ /// SpeechToTextService
+ /// The main role is speech recognition.
+ /// Use Tizen.Uix.Stt
+ /// </summary>
+ public class SpeechToTextService
+ {
+ private static readonly Lazy<SpeechToTextService> instance = new Lazy<SpeechToTextService>(() => new SpeechToTextService());
+ public static SpeechToTextService Instance { get => instance.Value; }
+
+ SttClient _Stt;
+
+ private Action<Object, SttState, SttState> stateCallback;
+ private SpeechToTextService()
+ {
+ try
+ {
+ if (_Stt == null)
+ {
+ _Stt = new SttClient();
+ _Stt.StateChanged += _Stt_StateChanged;
+ _Stt.DefaultLanguageChanged += _Stt_DefaultLanguageChanged;
+ _Stt.EngineChanged += _Stt_EngineChanged;
+ _Stt.ErrorOccurred += _Stt_ErrorOccurred;
+ _Stt.RecognitionResult += _Stt_RecognitionResult;
+ // Expected State : Created
+ CurrentSttLanguage = _Stt.DefaultLanguage;
+ _Stt.Prepare();
+ Console.WriteLine("DefaultLanguage ..." + _Stt.DefaultLanguage + ", Engine : " + _Stt.Engine);
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("SttClient Exception: " + e.Message);
+ if (e is NotSupportedException)
+ {
+ Console.WriteLine("SttClient is not supported...");
+ }
+ else if (e is UnauthorizedAccessException)
+ {
+ Console.WriteLine("UnauthorizedAccessException...");
+ }
+ }
+ }
+ /// <summary>
+ /// Start Speech-to-Text service
+ /// It means that it starts speech recognition, converting audio to text
+ /// </summary>
+ public void StartStt()
+ {
+ _Stt.SetSilenceDetection(SilenceDetection.False);
+ _Stt.Start(CurrentSttLanguage, RecognitionType.Partial);
+ }
+ /// <summary>
+ /// Cancel speech recognition
+ /// </summary>
+ public void Cancel()
+ {
+ Console.WriteLine("\n\n[Cancel] Before SttClient.Cancel(), state: " + _Stt.CurrentState);
+ //To cancel the recording without getting the result, use the stt_cancel() function.
+ if (_Stt.CurrentState == State.Recording || _Stt.CurrentState == State.Processing)
+ {
+ _Stt.Cancel();
+ }
+
+ Console.WriteLine("[Cancel] After SttClient.Cancel(), state: " + _Stt.CurrentState);
+ }
+
+ TaskCompletionSource<string> taskCompletionSource;
+ /// <summary>
+ /// Stop speech recognition and get the converted text
+ /// </summary>
+ /// <returns>text</returns>
+ public Task<string> StopAndGetText()
+ {
+ Console.WriteLine("\n\nBefore SttClient.StopAndGetText(), state: " + _Stt.CurrentState);
+ if (_Stt.CurrentState == State.Ready)
+ {
+ return null;
+ }
+
+ // Return Task object
+ taskCompletionSource = new TaskCompletionSource<string>();
+ //To stop the recording and get the recognition result
+ // TODO: Find out a better solution.
+ switch (_Stt.CurrentState)
+ {
+ case State.Processing:
+ //requestToStop = true;
+ MessagingCenter.Subscribe<SpeechToTextService>(this, MessageKeys.CanStopSTT, (obj) =>
+ {
+ _Stt.Stop();
+ MessagingCenter.Unsubscribe<SpeechToTextService>(this, MessageKeys.CanStopSTT);
+ });
+ break;
+ case State.Recording:
+ try
+ {
+ _Stt.Stop();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("[StopAndGetText] _Stt.Stop() --> Error e : " + e.Message + ", " + e.Source + ", " + e.StackTrace + ", " + e.GetType());
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ return taskCompletionSource.Task;
+ }
+ /// <summary>
+ /// Dispose Speech-to-Text service
+ /// </summary>
+ public void Dispose()
+ {
+ Console.WriteLine("SpeechToTextService.Dispose()");
+ try
+ {
+ if (_Stt == null)
+ {
+ return;
+ }
+
+ _Stt.DefaultLanguageChanged -= _Stt_DefaultLanguageChanged;
+ _Stt.EngineChanged -= _Stt_EngineChanged;
+ _Stt.ErrorOccurred -= _Stt_ErrorOccurred;
+ _Stt.RecognitionResult -= _Stt_RecognitionResult;
+ _Stt.StateChanged -= _Stt_StateChanged;
+ _Stt.Unprepare();
+ _Stt.Dispose();
+ _Stt = null;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("SpeechToTextService.Dispose() : Error - " + e.Message); // Invalid Parameters Provided
+ }
+ }
+
+ /// <summary>
+ /// Get installed languages.
+ /// </summary>
+ /// <returns>
+ /// The installed language names.
+ /// </returns>
+ public IEnumerable<string> GetInstalledLanguages()
+ {
+ return _Stt.GetSupportedLanguages();
+ }
+
+ string _currentSttLanguage;
+
+ /// <summary>
+ /// The chosen language for Speech-to-Text service
+ /// </summary>
+ public string CurrentSttLanguage
+ {
+ get
+ {
+ return _currentSttLanguage;
+ }
+
+ set
+ {
+ if (_currentSttLanguage != value)
+ {
+ _currentSttLanguage = value;
+ }
+ }
+ }
+ /// <summary>
+ /// The state of Speech-to-Text service
+ /// </summary>
+ public SttState SttState
+ {
+ get
+ {
+ return (SttState)_Stt.CurrentState;
+ }
+ }
+
+ private void _Stt_RecognitionResult(object sender, RecognitionResultEventArgs arg)
+ {
+ Console.WriteLine("_Stt_RecognitionResult Message: " + arg.Message);
+ Console.WriteLine("_Stt_RecognitionResult Result: " + arg.Result);
+ Console.WriteLine("_Stt_RecognitionResult DataCount : " + arg.DataCount);
+ Console.WriteLine("_Stt_RecognitionResult Data : " + arg.Data);
+ Console.WriteLine(" **** _Stt_RecognitionResult : " + arg.DataCount + ", " + arg.Data + ", " + arg.Message + ", " + arg.Result);
+ Console.WriteLine(" **** _Stt_RecognitionResult --- _Stt.CurrentState : " + _Stt.CurrentState);
+
+ string resultText = "";
+ if (arg.Result == ResultEvent.FinalResult && taskCompletionSource != null)
+ {
+ if (arg.Data != null)
+ {
+ foreach (var part in arg.Data)
+ {
+ Console.WriteLine("text : (" + part + ")");
+ resultText += part;
+ Console.WriteLine("resultText : (" + resultText + ")");
+ }
+
+ if (string.IsNullOrEmpty(resultText))
+ {
+ Console.WriteLine("!!!!!!!!!!!!!!!!!!! resultText is string.IsNullOrEmpty");
+ }
+
+ //if (resultText.Equals("") || resultText.Equals(" ") || resultText.Equals(" "))
+ //{
+ // Console.WriteLine("result data is empty. so assign Recognition failed");
+ // resultText = AppResources.RecognitionFailed;
+ //}
+
+ taskCompletionSource.SetResult(resultText);
+ //System.InvalidOperationException: An attempt was made to transition a task to a final state when it had already completed.
+ // at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
+ // at System.Threading.Tasks.TaskCompletionSource`1.SetResult(TResult result)
+ taskCompletionSource = null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Invoked when an error occurs
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">ErrorOccurredEventArgs</param>
+ private void _Stt_ErrorOccurred(object sender, ErrorOccurredEventArgs e)
+ {
+ Console.WriteLine(" **** _Stt_ErrorOccurred : (" + e.ErrorMessage + "), " + e.ErrorValue);
+ if (taskCompletionSource != null)
+ {
+ taskCompletionSource.SetResult("Stt Error:" + e.ErrorValue);
+ }
+ }
+
+ /// <summary>
+ /// Invoked when Speech-to-Text engine has been changed
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">EngineChangedEventArgs</param>
+ private void _Stt_EngineChanged(object sender, EngineChangedEventArgs e)
+ {
+ Console.WriteLine(" ----_Stt_EngineChanged : " + e.EngineId + ", " + e.Language + ", " + e.NeedCredential + ", " + e.SupportSilence);
+ }
+
+ /// <summary>
+ /// Invoked when the default language for Speech-to-Text service has been changed
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">DefaultLanguageChangedEventArgs</param>
+ private void _Stt_DefaultLanguageChanged(object sender, DefaultLanguageChangedEventArgs e)
+ {
+ Console.WriteLine(" ---- _Stt_DefaultLanguageChanged : " + e.PreviousLanguage + " ---> " + e.CurrentLanguage);
+ }
+
+ /// <summary>
+ /// Invoked when the state of Speech-To-Text service has been changed
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="e">StateChangedEventArgs</param>
+ private void _Stt_StateChanged(object sender, StateChangedEventArgs e)
+ {
+ Console.WriteLine(" ---- _Stt_StateChanged : " + e.Previous + " ---> " + e.Current);
+ stateCallback?.Invoke(sender, (SttState)e.Previous, (SttState)e.Current);
+ }
+
+ public void RegisterStateCallbacks(Action<object, SttState, SttState> callback)
+ {
+ stateCallback = callback;
+ }
+ }
+
+ public enum SttState
+ {
+ //
+ // Summary:
+ // Created state.
+ Created = 0,
+ //
+ // Summary:
+ // Ready state.
+ Ready = 1,
+ //
+ // Summary:
+ // Recording state.
+ Recording = 2,
+ //
+ // Summary:
+ // Processing state.
+ Processing = 3,
+ //
+ // Summary:
+ // Unavailable.
+ Unavailable = 4
+ }
+
+}
--- /dev/null
+/*
+ * 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.Threading.Tasks;
+using Tizen.Security;
+using VoiceMemo.Services;
+using Xamarin.Forms;
+
+
+namespace VoiceMemo.Services
+{
+ class UserPermission
+ {
+ public UserPermission()
+ {
+ }
+
+ TaskCompletionSource<bool> tcs;
+ int GrantedPermission;
+
+ /// <summary>
+ /// Request user permission for privacy privileges
+ /// </summary>
+ /// <param name="service">privacy privilege</param>
+ /// <returns>true if user consent is gained</returns>
+ public async Task<bool> GetPermission(string service)
+ {
+ try
+ {
+ GrantedPermission = -1;
+ // Gets the status of a privacy privilege permission.
+ CheckResult result = PrivacyPrivilegeManager.CheckPermission(service);
+ switch (result)
+ {
+ case CheckResult.Allow:
+ // user consent for privacy privilege is already gained
+ return true;
+ case CheckResult.Deny:
+ case CheckResult.Ask:
+ // User permission request should be required
+ tcs = new TaskCompletionSource<bool>();
+ // Gets the response context for a given privilege.
+ var reponseContext = PrivacyPrivilegeManager.GetResponseContext(service);
+ PrivacyPrivilegeManager.ResponseContext context = null;
+ if (reponseContext.TryGetTarget(out context))
+ {
+ if (context != null)
+ {
+ context.ResponseFetched += Context_ResponseFetched;
+ }
+ }
+ // Try to get the permission for service from a user.
+ PrivacyPrivilegeManager.RequestPermission(service);
+
+ // Check if permission is granted or not every second
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ Device.StartTimer(new TimeSpan(0, 0, 0, 1, 0), CheckPermission);
+ });
+
+ return await tcs.Task;
+ default:
+ return false;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("[UserPermission] (" + service + ") Error :" + e.Message);
+ return false;
+ }
+ }
+
+ bool CheckPermission()
+ {
+ // In case that an app user doesn't give permission yet.
+ if (GrantedPermission == -1)
+ {
+ return true;
+ }
+
+ // In case that an app user gives permission.
+ // Denied
+ if (GrantedPermission == 0)
+ {
+ tcs.SetResult(false);
+ }
+ // Allowed
+ else
+ {
+ tcs.SetResult(true);
+ }
+
+ return false;
+ }
+
+ // Invoked when an app user responses for the permission request
+ private void Context_ResponseFetched(object sender, RequestResponseEventArgs e)
+ {
+ if (e.result == RequestResult.AllowForever)
+ {
+ // User allows an app to grant privacy privilege
+ GrantedPermission = 1;
+ }
+ else
+ {
+ // User doesn't allow an app to grant privacy privilege
+ GrantedPermission = 0;
+ }
+ }
+ }
+}
--- /dev/null
+<StyleCopSettings Version="105">
+ <GlobalSettings>
+ <StringProperty Name="MergeSettingsFiles">NoMerge</StringProperty>
+ </GlobalSettings>
+ <Parsers>
+ <Parser ParserId="StyleCop.CSharp.CsParser">
+ <ParserSettings>
+ <BooleanProperty Name="AnalyzeGeneratedFiles">True</BooleanProperty>
+ </ParserSettings>
+ </Parser>
+ </Parsers>
+ <Analyzers>
+ <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
+ <Rules>
+ <Rule Name="ElementsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EnumerationItemsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustContainValidXml">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PartialElementDocumentationMustHaveSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustHaveSummaryText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotHaveDefaultSummary">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VoidReturnValueMustNotBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParametersMustBeDocumentedPartialClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="GenericTypeParameterDocumentationMustHaveText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustMatchAccessors">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustNotBeCopiedAndPasted">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustNotBeEmpty">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationTextMustContainWhitespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationMustMeetCharacterPercentage">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationHeadersMustNotContainBlankLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludedDocumentationXPathDoesNotExist">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncludeNodeDoesNotContainValidFileAndPath">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InheritDocMustBeUsedWithInheritingClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationMustBeSpelledCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMustHaveHeader">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustShowCopyright">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveCopyrightText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustContainFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchFileName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderMustHaveValidCompanyText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.LayoutRules">
+ <Rules>
+ <Rule Name="AllAccessorsMustBeMultiLineOrSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeadersMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleBlankLinesInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ChainedStatementBlocksMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="WhileDoFooterMustNotBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustNotBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementDocumentationHeaderMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeSeparatedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtStartOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainBlankLinesAtEndOfFile">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.MaintainabilityRules">
+ <Rules>
+ <Rule Name="AccessModifierMustBeDeclared">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldsMustBePrivate">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeAnalysisSuppressionMustHaveJustification">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugAssertMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DebugFailMustProvideMessageText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleClass">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FileMayOnlyContainASingleNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StatementMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ArithmeticExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConditionalExpressionsMustDeclarePrecedence">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveDelegateParenthesisWhenPossible">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AttributeConstructorMustNotUseUnnecessaryParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="RemoveUnnecessaryCode">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
+ <Rules>
+ <Rule Name="ElementMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstFieldNamesMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotUseHungarianNotation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustBeginWithLowerCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="AccessibleFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="VariableNamesMustNotBePrefixed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotBeginWithUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="FieldNamesMustNotContainUnderscore">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyFieldsMustBeginWithUpperCaseLetter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
+ <Rules>
+ <Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustAppearInTheCorrectOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ElementsMustBeOrderedByAccess">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ConstantsMustAppearBeforeFields">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticElementsMustAppearBeforeInstanceElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DeclarationKeywordsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ProtectedMustComeBeforeInternal">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PropertyAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="EventAccessorsMustFollowOrder">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="StaticReadonlyElementsMustAppearBeforeStaticNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="InstanceReadonlyElementsMustAppearBeforeInstanceNonReadonlyElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SystemUsingDirectivesMustBePlacedBeforeOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBePlacedAfterOtherUsingDirectives">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingDirectivesMustBeOrderedAlphabeticallyByNamespace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingAliasDirectivesMustBeOrderedAlphabeticallyByAliasName">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UsingStaticDirectivesMustBePlacedAtTheCorrectLocation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
+ <Rules>
+ <Rule Name="CommentsMustContainText">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPrefixCallsWithBaseUnlessLocalImplementationExists">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixLocalCallsWithThis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PrefixCallsCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningParenthesisMustBeOnDeclarationLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfLastParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingParenthesisMustBeOnLineOfOpeningParenthesis">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CommaMustBeOnSameLineAsPreviousParameter">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterListMustFollowDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustFollowComma">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ParameterMustNotSpanMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustFollowPreviousClause">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesMustBeOnSeparateLinesOrAllOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="QueryClausesSpanningMultipleLinesMustBeginOnOwnLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotPlaceRegionsWithinElements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainEmptyStatements">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedComments">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="BlockStatementsMustNotContainEmbeddedRegions">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseReadableConditions">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseStringEmptyForEmptyStrings">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseBuiltInTypeAlias">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="UseShorthandForNullableTypes">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="StyleCop.CSharp.SpacingRules">
+ <Rules>
+ <Rule Name="CommasMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SemicolonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DocumentationLinesMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="SingleLineCommentsMustBeginWithSingleSpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PreprocessorKeywordsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OperatorKeywordMustBeFollowedBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingGenericBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="OpeningAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingAttributeBracketsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NullableTypeSymbolsMustNotBePrecededBySpace">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="MemberAccessSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="IncrementDecrementSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="NegativeSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="PositiveSignsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DereferenceAndAccessOfSymbolsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ColonsMustBeSpacedCorrectly">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleWhitespaceInARow">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="TabsMustNotBeUsed">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="DoNotSplitNullConditionalOperators">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ </Analyzers>
+</StyleCopSettings>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Reflection;
+
+namespace VoiceMemo
+{
+ public class Utility
+ {
+ public Utility()
+ {
+
+ }
+
+ public static void PrintProperties(object obj)
+ {
+ try
+ {
+ var type = obj.GetType();
+
+ foreach (PropertyInfo p in type.GetProperties())
+ {
+ object propertyValue = p.GetValue(obj, null);
+ Console.WriteLine(p.Name + ":- " + propertyValue);
+ //Console.WriteLine(p.Name + ":- " + p.GetValue(obj, null));
+
+ //if (p.PropertyType.GetProperties().Count() > 0)
+ //{
+ // // what to pass in to recursive method
+ // PrintProperties(propertyValue);
+ //}
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(" FAILED PrintProperties : " + e.Message);
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Utility
+{
+ internal struct RGB : IEquatable<RGB>
+ {
+ internal RGB(byte r, byte g, byte b)
+ {
+ R = r;
+ G = g;
+ B = b;
+ }
+
+ internal byte R { get; set; }
+
+ internal byte G { get; set; }
+
+ internal byte B { get; set; }
+
+ bool IEquatable<RGB>.Equals(RGB rgb)
+ {
+ return (R == rgb.R) && (G == rgb.G) && (B == rgb.B);
+ }
+ }
+
+ internal struct HSB
+ {
+ internal HSB(double h, double s, double b)
+ {
+ H = h;
+ S = s;
+ B = b;
+ A = null;
+ }
+
+ internal HSB(double h, double s, double b, double a)
+ {
+ H = h;
+ S = s;
+ B = b;
+ A = a;
+ }
+
+ internal double H { get; set; }
+
+ internal double S { get; set; }
+
+ internal double B { get; set; }
+
+ internal double? A { get; set; }
+
+ internal bool Equals(HSB hsb)
+ {
+ return (this.H == hsb.H) && (this.S == hsb.S) && (this.B == hsb.B) && (this.A == hsb.A);
+ }
+ }
+
+ internal static class OneUITheme
+ {
+ internal static HSB BlackTheme = new HSB(0, 0, 0);
+
+ internal static HSB WhiteTheme = new HSB(0, 0, 98);
+
+ internal static HSB[] DefaultTheme =
+ {
+ // 1st
+ new HSB(207,75,16),
+ // 2nd
+ new HSB(203, 98, 37),
+ // 3rd
+ new HSB(207, 91, 37),
+ // 4th
+ new HSB(208, 70, 100)
+ };
+ }
+
+ internal static class ColorConverter
+ {
+
+ internal static Xamarin.Forms.Color OneUIColorConverter(HSB hsb, HSB? input)
+ {
+ // Get component's color
+ float h = (input == null) ? (float)hsb.H : (float)hsb.H + (float)input?.H;
+ float s = (input == null) ? (float)hsb.S : (float)hsb.S + (float)input?.S;
+ float b = (input == null) ? (float)hsb.B : (float)hsb.B + (float)input?.B;
+
+ // Check the range
+ h = (h > 360.0f) ? (h - 360.0f) : ((h < 0.0f) ? (h + 360.0f) : h);
+ s = (s > 100.0f) ? (100.0f) : ((s < 0.0f) ? (0.0f) : s);
+ b = (b > 100.0f) ? (100.0f) : ((b < 0.0f) ? (0.0f) : b);
+
+ s /= 100.0f;
+ b /= 100.0f;
+
+ RGB rgb = HsbToRgb(
+ new HSB {
+ H = h,
+ S = s,
+ B = b
+ });
+
+ float a;
+ a = (int)(255 * hsb.A / 100);
+ a = (a > 255) ? (255) : ((a < 0) ? 0 : a);
+ return Color.FromRgba((int)rgb.R, (int)rgb.G, (int)rgb.B, (int)a);
+ }
+
+ internal static RGB HsbToRgb(HSB hsb)
+ {
+ double r = 0, g = 0, b = 0;
+
+ if (hsb.S == 0)
+ {
+ r = hsb.B;
+ g = hsb.B;
+ b = hsb.B;
+ }
+ else
+ {
+ int i;
+ double f, p, q, t;
+
+ if (hsb.H == 360)
+ hsb.H = 0;
+ else
+ hsb.H /= 60;
+
+ i = (int)Math.Truncate(hsb.H);
+ f = hsb.H - i;
+
+ p = hsb.B * (1.0 - hsb.S);
+ q = hsb.B * (1.0 - (hsb.S * f));
+ t = hsb.B * (1.0 - (hsb.S * (1.0 - f)));
+
+ switch (i)
+ {
+ case 0:
+ r = hsb.B;
+ g = t;
+ b = p;
+ break;
+
+ case 1:
+ r = q;
+ g = hsb.B;
+ b = p;
+ break;
+
+ case 2:
+ r = p;
+ g = hsb.B;
+ b = t;
+ break;
+
+ case 3:
+ r = p;
+ g = q;
+ b = hsb.B;
+ break;
+
+ case 4:
+ r = t;
+ g = p;
+ b = hsb.B;
+ break;
+
+ default:
+ r = hsb.B;
+ g = p;
+ b = q;
+ break;
+ }
+ }
+ return new RGB((byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace VoiceMemo.Utility
+{
+ internal static class LOG
+ {
+ internal const string TAG = "Voicememo-2019";
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace VoiceMemo.Utility
+{
+ internal static class PrivilegeInformation
+ {
+ internal const string RecorderPrivilege = "http://tizen.org/privilege/recorder";
+ internal const string MediaStoragePrivilege = "http://tizen.org/privilege/mediastorage";
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.ViewModels
+{
+ // Base page model class
+ public class BasePageModel : INotifyPropertyChanged
+ {
+ public virtual void UpdateText()
+ {
+ }
+
+ 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
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.Models;
+
+namespace VoiceMemo.ViewModels
+{
+ // The model class for DetailPage
+ public class DetailsPageModel : BasePageModel
+ {
+ // Record that is shown in DetailsPage
+ Record _record;
+ public bool IsNew
+ {
+ get;
+ set;
+ }
+ /// <summary>
+ /// Record to display in Details Page
+ /// </summary>
+ public Record Record
+ {
+ get
+ {
+ return _record;
+ }
+
+ set
+ {
+ SetProperty(ref _record, value, "Record");
+ }
+ }
+
+ public DetailsPageModel(Record record = null)
+ {
+ Init(record);
+ }
+ /// <summary>
+ /// Initialize Detail Page with Record
+ /// </summary>
+ /// <param name="record">Record to be shown</param>
+ public void Init(Record record)
+ {
+ if (record is LatestRecord)
+ {
+ IsNew = true;
+ Record = ((LatestRecord)record).Record;
+ }
+ else
+ {
+ IsNew = false;
+ Record = record;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.Globalization;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using VoiceMemo.Models;
+using VoiceMemo.Resx;
+using VoiceMemo.Services;
+using Xamarin.Forms;
+
+namespace VoiceMemo.ViewModels
+{
+ // The model class for MainPage
+ public class MainPageModel : BasePageModel
+ {
+ // Collection of Records
+ ObservableCollection<Record> _Records;
+ // Language for STT recognition
+ string _CurrentLanguage;
+ // Speech-To-Text Service
+ SpeechToTextService _SttService;
+ // Media Content Service to get the path of audio record file
+ MediaContentService _ContentService;
+ // App data service to store / restore app data
+ AppDataService _AppDataService;
+ // indicate whether it is possible to record voice
+ public bool availableToRecord;
+
+ public MainPageModel()
+ {
+ if (Records == null)
+ {
+ Records = new ObservableCollection<Record>();
+ }
+
+ Init();
+
+ RegisterForImportantEvents();
+ }
+
+ // Initialize
+ void Init()
+ {
+ IsCheckable = false;
+ _AppDataService = AppDataService.Instance;
+ if (_AppDataService.Contain(STT_ON_OFF))
+ {
+ SttEnabled = _AppDataService.GetValue<bool>(STT_ON_OFF);
+ //Console.WriteLine(" STT_ON_OFF ??? : " + SttEnabled);
+ }
+ else
+ {
+ SttEnabled = false;
+ //Console.WriteLine(" There's no default value for SttEnabled so make it false. : " + SttEnabled);
+ }
+
+ if (SttEnabled)
+ {
+ MainLabelText = AppResources.StandByTitleMemo;
+ }
+ else
+ {
+ MainLabelText = AppResources.StandByTitleRecorder;
+ }
+
+ availableToRecord = true;
+ }
+
+ // Subscribe to get notified when some events occur.
+ // 1. When a new voice memo is saved in an internal storage
+ // 2. When a voice memo is deleted from an internal storage
+ // 3. When it's checked whether user consent is obtained
+ /*async*/ void RegisterForImportantEvents()
+ {
+ try
+ {
+ //Console.WriteLine("[RegisterForImportantEvents] 1");
+ // You can get notified whenever a new voice memo record is created.
+ // At this point of time, information(record) about that voice memo is needed to store
+ MessagingCenter.Subscribe<RecordingPageModel, Record>(this, MessageKeys.SaveVoiceMemo, (obj, item) =>
+ {
+ // Add it to the collection of records
+ var record = ((LatestRecord)item).Record;
+ //if (Records == null)
+ //{
+ // Records = new ObservableCollection<Record>();
+ //}
+ Records.Add(record);
+ });
+ //Console.WriteLine("[RegisterForImportantEvents] 2");
+ // You can get notified whenever translated text via speech-to-text service is ready
+ // Now, we can update Record to database
+ MessagingCenter.Subscribe<RecordingPageModel, Record>(this, MessageKeys.SaveVoiceMemoInDB, async (obj, record) =>
+ {
+ // add it to database
+ await App.Database.SaveItemAsync(record);
+ });
+ //Console.WriteLine("[RegisterForImportantEvents] 3");
+ // You can get notified whenever a voice memo record is deleted from a storage
+ MessagingCenter.Subscribe<Page, Record>(this, MessageKeys.DeleteVoiceMemo, async (obj, item) =>
+ {
+ var record = item as Record;
+ for (int i = Records.Count - 1; i >= 0; i--)
+ {
+ if (Records[i]._id == record._id)
+ {
+ // Delete record from database
+ await App.Database.DeleteItemAsync(Records[i]);
+ // Delete audio file from internal storage
+ _ContentService.RemoveMediaFile(Records[i].Path);
+ // Delete it from collection of records
+ Records.RemoveAt(i);
+ }
+ }
+ });
+
+ // You can get notified when an app user allows this app to use recorder and internal storage.
+ //MessagingCenter.Subscribe<App, bool>(this, MessageKeys.UserPermission, (obj, item) =>
+ //{
+ // var NonUItask = Task.Run(async () =>
+ // {
+ // //await Task.Delay(3000);
+ // // Restore recorded voice memos
+ // List<Record> tmp = await App.Database.GetItemsAsync();
+ // for (int i = 0; i < tmp.Count; i++)
+ // {
+ // Records.Add(tmp[i]);
+ // }
+ // // Speech-To-Text Service
+ // GetSttService();
+
+ // // Media Content Service
+ // if (_ContentService == null)
+ // {
+ // _ContentService = MediaContentService.Instance;
+ // }
+ // });
+ //});
+ //Console.WriteLine("[RegisterForImportantEvents] 4");
+ //List<Record> tmp = await App.Database.GetItemsAsync();
+ //for (int i = 0; i < tmp.Count; i++)
+ //{
+ // Records.Add(tmp[i]);
+ //}
+ // Speech-To-Text Service
+ //GetSttService();
+ //Console.WriteLine("[RegisterForImportantEvents] 5");
+ // Media Content Service
+ if (_ContentService == null)
+ {
+ _ContentService = MediaContentService.Instance;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("[RegisterForImportantEvents] " + e.Message +", " + e.StackTrace);
+ }
+ }
+
+ /// <summary>
+ /// Unsubscribe from notifications
+ /// </summary>
+ void UnregisterForImportantEvents()
+ {
+ MessagingCenter.Unsubscribe<RecordingPageModel, Record>(this, MessageKeys.SaveVoiceMemo);
+ MessagingCenter.Unsubscribe<Page, Record>(this, MessageKeys.DeleteVoiceMemo);
+ MessagingCenter.Unsubscribe<App, bool>(this, MessageKeys.UserPermission);
+ }
+
+ public void Dispose()
+ {
+ Console.WriteLine("### MainPageModel.Dispose() -----_SttService?.Dispose()----");
+ UnregisterForImportantEvents();
+ _AppDataService.SetValue(STT_ON_OFF, SttEnabled);
+ if (SelectedItemIndex != null)
+ {
+ Console.WriteLine("### MainPageModel.Dispose() SAVE Language : " + SelectedItemIndex.Lang);
+ _AppDataService.SetValue(LANGUAGE_FOR_STT, SelectedItemIndex.Lang);
+ }
+
+ _SttService?.Dispose();
+ _ContentService?.Destroy();
+ }
+
+ /// <summary>
+ /// Collection of Records
+ /// </summary>
+ public ObservableCollection<Record> Records
+ {
+ get
+ {
+ return _Records;
+ }
+
+ set
+ {
+ bool changed = SetProperty(ref _Records, value, "Records");
+
+ Console.WriteLine(" ########## Records changed? " + changed);
+ if (changed && Records.Count == 0)
+ {
+ Console.WriteLine(" ########## Records.Count is 0!!");
+ }
+ }
+ }
+
+ // Language for STT recognition
+ public string CurrentLanguage
+ {
+ get
+ {
+ return _CurrentLanguage;
+ }
+
+ set
+ {
+ if (SetProperty(ref _CurrentLanguage, value, "CurrentLanguage"))
+ {
+ Console.WriteLine("Language for STT service has been changed. So update _SttService's CurrentSttLanguage. : " + CurrentLanguage);
+ if (_SttService != null)
+ {
+ _SttService.CurrentSttLanguage = CurrentLanguage;
+ }
+ }
+ }
+ }
+
+ SttLanguage _SelectedItemIndex;
+ public SttLanguage SelectedItemIndex
+ {
+ get
+ {
+ return _SelectedItemIndex;
+ }
+
+ set
+ {
+ SetProperty(ref _SelectedItemIndex, value, "SelectedItemIndex");
+ }
+ }
+
+ public ObservableCollection<SttLanguage> Languages { get; set; }
+
+ //public ICommand DoInitCommand => new Command(Initialize);
+ //async void Initialize()
+ //{
+ // //List<Record> tmp = await App.Database.GetItemsAsync();
+ // //for (int i = 0; i < tmp.Count; i++)
+ // //{
+ // // Records.Add(tmp[i]);
+ // //}
+ // // Speech-To-Text Service
+ // await GetSttService();
+
+ // // Media Content Service
+ // //if (_contentservice == null)
+ // //{
+ // // _contentservice = mediacontentservice.instance;
+ // //}
+ //}
+
+ //public static readonly BindableProperty GetSttServiceCommandProperty =
+ //BindableProperty.Create("GetSttServiceCommand", typeof(Command), typeof(MainPageModel), default(Command));
+ public ICommand GetSttServiceCommand => new Command(async () => await GetSttService());
+
+ //public static readonly BindableProperty SttOnOffCommandProperty =
+ // BindableProperty.Create("SttOnOffCommand", typeof(Command), typeof(MainPageModel), default(Command));
+
+ public ICommand SttOnOffCommand => new Command(SttOnOff);
+
+ Task GetSttService()
+ {
+ if (_SttService == null)
+ {
+ Console.WriteLine(" GetSttService() ------------1- CurrentLanguage : " + CurrentLanguage);
+ _SttService = SpeechToTextService.Instance;
+ Languages = new ObservableCollection<SttLanguage>();
+
+ // TODO: how to check if the current language is supported by STT engine.
+ //CultureInfo _cultureInfo = new CultureInfo(CurrentLanguage);
+ //RegionInfo _regionInfo = new RegionInfo(CurrentLanguage.Replace("_", "-"));
+ //Languages.Add(new SttLanguage(CurrentLanguage, "Automatic", Regex.Replace(_cultureInfo.DisplayName, @"\t|\n|\r", "")));
+
+
+ var dafaultLang = "en_US";
+ // Restore the selected language for STT or use the STT service's current language
+ if (_AppDataService.Contain(LANGUAGE_FOR_STT))
+ {
+ dafaultLang = _AppDataService.GetValue(LANGUAGE_FOR_STT);
+ Console.WriteLine(" IAppDataService.GetValue => language for stt : " + dafaultLang);
+ }
+ else
+ {
+ dafaultLang = _SttService.CurrentSttLanguage;
+ Console.WriteLine(" IAppDataService no value => CurrentSttLanguage : " + dafaultLang);
+ }
+ // For updating STT service's CurrentSttLanguage
+ CurrentLanguage = dafaultLang;
+
+ foreach (var lang in _SttService.GetInstalledLanguages())
+ {
+ CultureInfo cultureInfo = new CultureInfo(lang);
+ RegionInfo regionInfo = new RegionInfo(lang.Replace("_", "-"));
+ Console.WriteLine(" [GetSttService] lang : " + lang + ", " + cultureInfo.DisplayName + ", " + regionInfo.EnglishName);
+ var stt = new SttLanguage(lang, cultureInfo.DisplayName, regionInfo.EnglishName);
+ Languages.Add(stt);
+ //Languages.Add(new SttLanguage(lang, cultureInfo.DisplayName, regionInfo.EnglishName));
+ if (lang == dafaultLang)
+ {
+ stt.IsOn = true;
+ SelectedItemIndex = stt;
+ }
+ }
+#if ENABLE_DEBUG_PRINT
+ foreach (var lang in Languages)
+ {
+ Console.WriteLine(" Languages -- " + lang.Country + ", " + lang.Name + ", " + lang.IsOn); /*+ " vs. " + lang.isNotOn*/
+ }
+#endif
+ Console.WriteLine(" GetSttService() ------------2- " + _SttService.GetHashCode());
+ }
+ return Task.CompletedTask;
+ }
+
+ // STT feature usability
+ // in case of true : while recording voice, you can convert your voice to text (Speech to text)
+ // in case of false : just record voice.
+ bool _sttEnabled;
+ public bool SttEnabled
+ {
+ get
+ {
+ return _sttEnabled;
+ }
+
+ set
+ {
+ SetProperty(ref _sttEnabled, value, "SttEnabled");
+ }
+ }
+
+ void SttOnOff(object sender)
+ {
+ Console.WriteLine(" StandByPageModel.SttOnOff() : " + SttEnabled);
+ if (SttEnabled)
+ {
+ SttEnabled = false;
+ MainLabelText = AppResources.StandByTitleRecorder;
+ }
+ else
+ {
+ SttEnabled = true;
+ MainLabelText = AppResources.StandByTitleMemo;
+ GetSttService();
+ }
+
+ MessagingCenter.Send<MainPageModel, bool>(this, MessageKeys.SttSupportedChanged, SttEnabled);
+ }
+
+ // main label text : voice memo or voice recorder
+ string _mainlabeltext;
+ public string MainLabelText
+ {
+ get
+ {
+ return _mainlabeltext;
+ }
+
+ set
+ {
+ SetProperty(ref _mainlabeltext, value, "MainLabelText");
+ }
+ }
+
+ // Update the text of main label, depending on enabling or disabling stt service
+ public override void UpdateText()
+ {
+ if (SttEnabled)
+ {
+ // voice memo
+ MainLabelText = AppResources.StandByTitleMemo;
+ }
+ else
+ {
+ // voice recorder
+ MainLabelText = AppResources.StandByTitleRecorder;
+ }
+ }
+
+ ///////////////////////
+ const string SelectAll = "Select all";
+ const string DeselectAll = "Deselect all";
+
+ bool _isCheckable;
+ public bool IsCheckable
+ {
+ get
+ {
+ return _isCheckable;
+ }
+
+ set
+ {
+ bool result = SetProperty(ref _isCheckable, value, "IsCheckable");
+ if (result)
+ {
+ Console.WriteLine("-----IsCheckable : it's changed ---> " + IsCheckable);
+ if (!IsCheckable)
+ {
+ foreach (var record in Records)
+ {
+ record.Checked = false;
+ }
+ }
+ }
+ }
+ }
+
+ bool _popupVisibility;
+ public bool PopupVisibility
+ {
+ get
+ {
+ return _popupVisibility;
+ }
+
+ set
+ {
+ SetProperty(ref _popupVisibility, value, "PopupVisibility");
+ }
+ }
+
+ int _checkedNamesCount;
+ public int CheckedNamesCount
+ {
+ get
+ {
+ return _checkedNamesCount;
+ }
+
+ set
+ {
+ bool changed = SetProperty(ref _checkedNamesCount, value, "CheckedNamesCount");
+ if (changed)
+ {
+ UpdateSelectOptionMessage();
+ }
+ }
+ }
+
+ string _selectOptionMessage1;
+ public string SelectOptionMessage1
+ {
+ get
+ {
+ return _selectOptionMessage1;
+ }
+
+ set
+ {
+ SetProperty(ref _selectOptionMessage1, value, "SelectOptionMessage1");
+ }
+ }
+
+ string _selectOptionMessage2;
+ public string SelectOptionMessage2
+ {
+ get
+ {
+ return _selectOptionMessage2;
+ }
+
+ set
+ {
+ SetProperty(ref _selectOptionMessage2, value, "SelectOptionMessage2");
+ }
+ }
+
+ void OnLongClick()
+ {
+ Console.WriteLine("OnLongClick() is invoked!!");
+ if (!IsCheckable)
+ {
+ IsCheckable = true;
+ }
+ }
+
+ void OnCheckedCounterClicked()
+ {
+ Console.WriteLine("OnCheckedCounterClicked() is invoked!!");
+ PopupVisibility = true;
+
+ Console.WriteLine("Checked is clicked!!!");
+ }
+
+ void SelectOption1Job()
+ {
+ bool r = CheckedNamesCount < Records.Count;
+ Console.WriteLine("CheckedNamesCount : " + CheckedNamesCount + " vs. CheckableNames.Count: " + Records.Count);
+ foreach (var x in Records)
+ {
+ x.Checked = r;
+ }
+ }
+
+ void SelectOption2Job()
+ {
+ Console.WriteLine("CheckedNamesCount : " + CheckedNamesCount + " vs. CheckableNames.Count: " + Records.Count);
+ if (CheckedNamesCount > 0 && CheckedNamesCount != Records.Count)
+ {
+ foreach (var x in Records)
+ {
+ x.Checked = false;
+ }
+ }
+ }
+
+ void UpdateSelectOptionMessage()
+ {
+ SelectOptionMessage1 = CheckedNamesCount < Records.Count ? SelectAll : DeselectAll;
+ SelectOptionMessage2 = CheckedNamesCount != 0 && CheckedNamesCount != Records.Count ? DeselectAll : "";
+ }
+
+ async void DeleteRecords()
+ {
+ Console.WriteLine(" ######## DeleteRecords ");
+ for (int i = Records.Count - 1; i >= 0; i--)
+ {
+ //if (Records[i].ID == record.ID)
+ if (Records[i].Checked)
+ {
+ await App.Database.DeleteItemAsync(Records[i]);
+ _ContentService.RemoveMediaFile(Records[i].Path);
+ Records.RemoveAt(i);
+ }
+ }
+
+ IsCheckable = false;
+ CheckedNamesCount = 0;
+ }
+
+ void ChangeToDeleteMode()
+ {
+ IsCheckable = true;
+ }
+
+ public ICommand SelectCommand1 => new Command(SelectOption1Job);
+ public ICommand SelectCommand2 => new Command(SelectOption2Job);
+ public ICommand DeleteRecordsCommand => new Command(DeleteRecords);
+ public ICommand ChangeToDeleteModeCommand => new Command(ChangeToDeleteMode);
+
+ public static readonly BindableProperty SelectModeActionButtonPressedCommandProperty =
+ BindableProperty.Create("SelectModeActionButtonPressedCommand", typeof(Command), typeof(MainPageModel), default(Command));
+ public ICommand SelectModeActionButtonPressedCommand => new Command(OnCheckedCounterClicked);
+
+ public static readonly BindableProperty LongClickCommandProperty =
+ BindableProperty.Create("LongClickCommand", typeof(Command), typeof(MainPageModel), default(Command));
+ public ICommand LongClickCommand => new Command(OnLongClick);
+
+ private const string LANGUAGE_FOR_STT = "language_for_stt";
+ private const string STT_ON_OFF = "stt_on_off";
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.ViewModels
+{
+ public class MessageKeys
+ {
+ // Notify that player's state has changed
+ public const string PlayerStateChanged = "PlayerStateChanged";
+
+ // Notify that Audio play has done
+ public const string AudioPlayDone = "AudioPlayDone";
+
+ // Notify that Error occurs
+ public const string ErrorOccur = "ErrorOccur";
+
+ // Notify that a voice memo is saved
+ public const string SaveVoiceMemo = "SaveVoiceMemo";
+
+ // Notify that a voice memo is saved in Database
+ public const string SaveVoiceMemoInDB = "SaveVoiceMemoInDB";
+
+ // Notify that a voice memo is deleted
+ public const string DeleteVoiceMemo = "DeleteVoiceMemo";
+
+ // Notify that language has been changed
+ public const string LanguageChanged = "LanguageChanged";
+
+ // Notify that STT configuration has been changed
+ public const string SttSupportedChanged = "SttSupportedChanged";
+
+ // TODO: It's not used yet.
+ // Notify that Stt text is ready
+ public const string SttText = "SttText";
+
+ // Notify that permission from an user is got or not
+ public const string UserPermission = "UserPermission";
+
+ // Notify that long click event occurs in RecordListPage
+ //public const string RecordListLongPressed = "RecordListLongPressed";
+
+ // TODO : It's a missing feature
+ // Notify that volume level is too high
+ //public const string WarnHearingDamange = "WarnHearingDamange";
+
+ // TODO : Need to consider this case
+ public const string CanStopSTT = "CanStopSTT";
+
+ // Notify that the state of audio recording service get Recording.
+ public const string ReadyToRecord = "ReadyToRecord";
+
+ // TODO : Need to consider this case
+ // forcibly go back to main page when stt state is not ready for recording.
+ // Even though stt is not ready, recording can be started. We can change it.
+ public const string ForcePopRecordingPage = "ForcePopRecordingPage";
+
+ // Notify that the some texts are needed to update for localization when the language has been changed.
+ public const string UpdateByLanguageChange = "UpdateByLanguageChange";
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Models;
+using VoiceMemo.Services;
+using Xamarin.Forms;
+
+namespace VoiceMemo.ViewModels
+{
+ public enum VolumeControlType
+ {
+ Minus,
+ Plus,
+ }
+
+ /// <summary>
+ /// Page model class for PlayBack page
+ /// </summary>
+ public class PlayBackPageModel : BasePageModel
+ {
+ const string VOLUME_ON = "voice_mamo_slider_volume_on.png"; //"button/details_vol_icon_on.png"
+ const string VOLUME_OFF = "voice_mamo_slider_mute.png"; //"button/details_vol_icon_off.png"
+ const string VOLUME_ON_SMALL_IMAGE = "button/details_vol_icon_on.png";
+ const string VOLUME_OFF_SMALL_IMAGE = "button/details_vol_icon_off.png";
+ const string PLAY_ON = "voicerecorder_btn_play.png";
+ const string PLAY_OFF = "voicerecorder_btn_pause.png";
+ double play_progressbar_delta;
+
+ public PlayBackPageModel(Record record = null)
+ {
+ if (PlayWatch == null)
+ {
+ PlayWatch = new Stopwatch();
+ }
+
+ _playService = AudioPlayService.Instance;
+ _playService.AudioPlayFinished += _playService_AudioPlayFinished;
+ MaxVolumeLevel = _playService.GetMaxVolume();
+ // You can get notified whenever the state of audio player has been changed
+ MessagingCenter.Subscribe<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged, (obj, state) =>
+ {
+ if (state == AudioPlayState.Playing)
+ {
+ PlayControlImage = PLAY_OFF;
+ // start to update the remaining time
+ StartTimerForRemainingTime();
+ }
+ else
+ {
+ PlayControlImage = PLAY_ON;
+ // Stop updating the remaining time
+ StopTimerForRemainingTime();
+ }
+ });
+ Init(record);
+ }
+
+ async public void Init(Record record)
+ {
+ PlayControlImage = PLAY_ON;
+ _record = record;
+ VolumeViewVisible = false;
+ CurrentVolume = _playService.Volume;
+ if (CurrentVolume == 0)
+ {
+ Mute = true;
+ MuteOnOffImage = VOLUME_OFF;
+ MuteOnOffSmallImage = VOLUME_OFF_SMALL_IMAGE;
+ }
+ else
+ {
+ Mute = false;
+ MuteOnOffImage = VOLUME_ON;
+ MuteOnOffSmallImage = VOLUME_ON_SMALL_IMAGE;
+ }
+
+ RemainingTime = record.Duration;
+ Console.WriteLine(" [PlayBackPageModel] RemainingTime : " + RemainingTime);
+ int minutes = RemainingTime / 60000;
+ int seconds = (RemainingTime - minutes * 60000) / 1000;
+ int total = minutes * 60 + seconds;
+ Console.WriteLine(" [PlayBackPageModel] minutes : " + minutes + ", seconds:" + seconds + ", total:" + total);
+ PlayingProcess = 0.0;
+ VolumeLevelProcess = CurrentVolume / MaxVolumeLevel;
+ Console.WriteLine(" [PlayBackPageModel] VolumeLevelProcess : " + VolumeLevelProcess);
+ // calculate value (unit: 100ms)
+ play_progressbar_delta = (1.0 / total) / 10;
+ Console.WriteLine(" [PlayBackPageModel] play_progressbar_delta : " + play_progressbar_delta);
+ _Touched = false;
+
+ PlayWatch.Reset();
+
+ _playService.VolumeChanged += _playService_VolumeChanged;
+ _playService.RegisterVolumeChangedCallback();
+ await _playService.Init(record.Path);
+ }
+
+ // It's called when PlayBackPage is hidden.
+ public void Stop()
+ {
+ Console.WriteLine("[PlayBackPageModel.Stop()] current playService's state :" + _playService.State);
+ // stop playing audio file
+ _playService.Stop();
+ // unregister event callbacks
+ _playService.VolumeChanged -= _playService_VolumeChanged;
+ _playService.UnregisterVolumeChangedCallback();
+ }
+
+ /// <summary>
+ /// Start to update the remaining time
+ /// </summary>
+ void StartTimerForRemainingTime()
+ {
+ PlayWatch.Start();
+ // Any background code that needs to update the user interface
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ // interact with UI elements
+ Device.StartTimer(new TimeSpan(0, 0, 0, 1, 0), UpdateRemainingPlayTime);
+ Device.StartTimer(new TimeSpan(0, 0, 0, 0, 100), UpdateProgressbar);
+ });
+ }
+
+ void StopTimerForRemainingTime()
+ {
+ PlayWatch.Stop();
+ }
+
+ /// <summary>
+ /// Update the value of progressbar at every 100 ms
+ /// </summary>
+ /// <returns>bool</returns>
+ bool UpdateProgressbar()
+ {
+ if (_playService.State == AudioPlayState.Playing)
+ {
+ PlayingProcess += play_progressbar_delta;
+ //Console.WriteLine("[UpdateRemainingPlayTime] PlayingProcess " + PlayingProcess);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Update the remaining time at every second
+ /// </summary>
+ /// <returns>bool</returns>
+ bool UpdateRemainingPlayTime()
+ {
+ //return true to keep the timer running or false to stop it after the current invocation.
+ if (_playService.State == AudioPlayState.Playing)
+ {
+ RemainingTime = RemainingTime - 1000;
+ return true;
+ }
+ else if (_playService.State == AudioPlayState.Idle)
+ {
+ RemainingTime = 0;
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ Stopwatch PlayWatch;
+ AudioPlayService _playService;
+ Record _record;
+ bool _Touched;
+
+ /// <summary>
+ /// Record to play
+ /// </summary>
+ public Record Record
+ {
+ get
+ {
+ return _record;
+ }
+ }
+
+ int _remainTime;
+ /// <summary>
+ /// The remaining time (ms)
+ /// </summary>
+ public int RemainingTime
+ {
+ get
+ {
+ return _remainTime;
+ }
+
+ set
+ {
+ SetProperty(ref _remainTime, value, "RemainingTime");
+ }
+ }
+
+ double _PlayingProcess;
+ public double PlayingProcess
+ {
+ get
+ {
+ return _PlayingProcess;
+ }
+
+ set
+ {
+ SetProperty(ref _PlayingProcess, value, "PlayingProcess");
+ }
+ }
+
+ double MaxVolumeLevel;
+ double _VolumeLevelProcess;
+ public double VolumeLevelProcess
+ {
+ get
+ {
+ return _VolumeLevelProcess;
+ }
+
+ set
+ {
+ SetProperty(ref _VolumeLevelProcess, value, "VolumeLevelProcess");
+ }
+ }
+
+ int _currentVolume;
+ /// <summary>
+ /// Volume level
+ /// </summary>
+ public int CurrentVolume
+ {
+ get
+ {
+ return _currentVolume;
+ }
+
+ set
+ {
+ bool changed = SetProperty(ref _currentVolume, value, "CurrentVolume");
+ if (changed)
+ {
+ // Decide mute mode according to volume level
+ if (CurrentVolume == 0)
+ {
+ Mute = true;
+ }
+ else
+ {
+ Mute = false;
+ }
+ }
+
+ }
+ }
+
+ bool _volumeViewVisible;
+ /// <summary>
+ /// Indicate that volume control view is visible or not
+ /// </summary>
+ public bool VolumeViewVisible
+ {
+ get
+ {
+ return _volumeViewVisible;
+ }
+
+ set
+ {
+ SetProperty(ref _volumeViewVisible, value, "VolumeViewVisible");
+ }
+ }
+
+ bool _mute;
+ /// <summary>
+ /// indicate that silent mode is on or off
+ /// </summary>
+ public bool Mute
+ {
+ get
+ {
+ return _mute;
+ }
+
+ set
+ {
+ if (SetProperty(ref _mute, value, "Mute"))
+ {
+ if (Mute)
+ {
+ // In case that Mute is on
+ _playService.Muted = true;
+ MuteOnOffImage = VOLUME_OFF;
+ MuteOnOffSmallImage = VOLUME_OFF_SMALL_IMAGE;
+ CurrentVolume = 0;
+ }
+ else
+ {
+ // In case that Mute is off
+ _playService.Muted = false;
+ MuteOnOffImage = VOLUME_ON;
+ MuteOnOffSmallImage = VOLUME_ON_SMALL_IMAGE;
+ CurrentVolume = _playService.Volume;
+ }
+ }
+ }
+ }
+
+ string _playcontrolImage;
+ public string PlayControlImage
+ {
+ get
+ {
+ return _playcontrolImage;
+ }
+
+ set
+ {
+ SetProperty(ref _playcontrolImage, value, "PlayControlImage");
+ }
+ }
+
+ string _muteOnOffImage;
+ /// <summary>
+ /// image which show that silent mode is on or not
+ /// </summary>
+ public string MuteOnOffImage
+ {
+ get
+ {
+ return _muteOnOffImage;
+ }
+
+ set
+ {
+ SetProperty(ref _muteOnOffImage, value, "MuteOnOffImage");
+ }
+ }
+
+ string _muteOnOffSmallImage;
+ public string MuteOnOffSmallImage
+ {
+ get
+ {
+ return _muteOnOffSmallImage;
+ }
+
+ set
+ {
+ SetProperty(ref _muteOnOffSmallImage, value, "MuteOnOffSmallImage");
+ }
+ }
+
+ // For changing the visibility of a View which provides a way to control volume level
+ public ICommand VolumeViewVisibilityCommand => new Command<bool>(ChangeVolumeViewVisibility);/* { private set; get; }*/
+ // To show up VolumeView which provides a way to make the volume up and down
+ public ICommand VolumeViewCommand => new Command(ControlVolumeView);
+ // For Volume Up/Down
+ public ICommand VolumeControlCommand => new Command<VolumeControlType>(ControlVolume);
+ // For toggling play / pause functions
+ public ICommand PlayControlCommand => new Command(ControlPlay);
+ // For Mute On/Off
+ public ICommand MuteControlCommand => new Command(ControlMute);
+ // Update delay time
+ public ICommand DelayTimeCommand => new Command(DelayTime);
+
+ /// <summary>
+ /// Make volume level up/down
+ /// </summary>
+ /// <param name="type">VolumeControlType</param>
+ void ControlVolume(VolumeControlType type)
+ {
+ switch (type)
+ {
+ case VolumeControlType.Minus:
+ VolumeLevelProcess -= 1 / MaxVolumeLevel;
+ _playService.DecreaseVolume();
+ break;
+ case VolumeControlType.Plus:
+ //if (CurrentVolume == 9)
+ //{
+ // NotifyHearingDamage();
+ //}
+
+ VolumeLevelProcess += 1 / MaxVolumeLevel;
+ _playService.IncreaseVolume();
+ break;
+ }
+ }
+
+ void NotifyHearingDamage()
+ {
+ // TODO
+ //MessagingCenter.Send<PlayBackPageModel>(this, MessageKeys.WarnHearingDamange);
+ }
+
+ // Toggle Mute On/Off
+ void ControlMute()
+ {
+ Mute = !Mute;
+ }
+
+ void ChangeVolumeViewVisibility(bool visible = true)
+ {
+ VolumeViewVisible = visible;
+ }
+
+ void ControlVolumeView()
+ {
+ Console.WriteLine("[ControlVolumeView] need to show VolumeView.");
+ VolumeViewVisible = true;
+
+ // Any background code that needs to update the user interface
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ // interact with UI elements
+ Device.StartTimer(new TimeSpan(0, 0, 0, 3, 0), HideVolumeView);
+ });
+ }
+
+ bool HideVolumeView()
+ {
+ // In case that screen is touched, VolumeView is still visible.
+ if (_Touched)
+ {
+ Console.WriteLine("In case that screen is touched, VolumeView is still visible.");
+ _Touched = false;
+ return true;
+ }
+ else
+ {
+ // Screen is not touched for last 3 seconds. So VolumeView will be hidden.
+ Console.WriteLine("Screen is not touched for last 3 seconds. So VolumeView will be hidden.");
+ VolumeViewVisible = false;
+ return false;
+ }
+ }
+
+ void DelayTime()
+ {
+ Console.WriteLine(" $$$$$$$$$$$$ ");
+ Console.WriteLine(" $$$$$$$$$$$$ ");
+ Console.WriteLine(" Screen is touched. So delaytime will be updated.");
+ Console.WriteLine(" $$$$$$$$$$$$ ");
+ Console.WriteLine(" $$$$$$$$$$$$ ");
+ _Touched = true;
+ }
+
+ void ControlPlay()
+ {
+ Console.WriteLine(" [ControlPlay] STATE: " + _playService.State);
+ if (_playService.State == AudioPlayState.Playing)
+ {
+ Console.WriteLine(" [ControlPlay] Pause");
+ _playService.Pause();
+ //StopTimerForRemainingTime();
+ }
+ else if (_playService.State == AudioPlayState.Paused)
+ {
+ Console.WriteLine(" [ControlPlay] Start");
+ _playService.Start();
+ //StartTimerForRemainingTime();
+ }
+ else if (_playService.State == AudioPlayState.Idle)
+ {
+ Init(_record);
+ _playService.Start();
+ }
+ }
+
+ private void _playService_VolumeChanged(object sender, AudioVolumeChangedEventArgs e)
+ {
+ Console.WriteLine("[_playService_VolumeChanged] e.Level : " + e.Level);
+ CurrentVolume = e.Level;
+ }
+
+ private void _playService_AudioPlayFinished(object sender, System.EventArgs e)
+ {
+ Console.WriteLine("_playService_AudioPlayFinished() ");
+ MessagingCenter.Send<PlayBackPageModel, bool>(this, MessageKeys.AudioPlayDone, true);
+ }
+
+ public void Dispose()
+ {
+ Console.WriteLine("PlayBackPageModel.Dispose() _playService.State: " + _playService.State);
+ MessagingCenter.Unsubscribe<AudioPlayService, AudioPlayState>(this, MessageKeys.PlayerStateChanged);
+ if (_playService.State == AudioPlayState.Playing)
+ {
+ _playService.Stop();
+ }
+
+ _playService.AudioPlayFinished -= _playService_AudioPlayFinished;
+ _playService.VolumeChanged -= _playService_VolumeChanged;
+ _playService.Destroy();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Models;
+using VoiceMemo.Resx;
+using VoiceMemo.Services;
+using Xamarin.Forms;
+using Native = Tizen.Applications;
+
+namespace VoiceMemo.ViewModels
+{
+ public enum RecordingViewModelState
+ {
+ Ready,
+ Recording,
+ Paused,
+ PausedForCancel,
+ Cancelled,
+ Stopped,
+ }
+
+ public enum RecordingCommandType
+ {
+ Record,
+ Pause,
+ PauseForCancelRequest,
+ Resume,
+ Cancel,
+ Stop
+ }
+
+ // The model class for RecordingPage
+ public class RecordingPageModel : BasePageModel
+ {
+ /// <summary>
+ /// It works while recording.
+ /// </summary>
+ private static Stopwatch RecordingWatch;
+
+ // For recording voice
+ AudioRecordService _audioRecordingService;
+
+ public AudioRecordService AudioRecordingService
+ {
+ get => _audioRecordingService;
+ private set
+ {
+ SetProperty(ref _audioRecordingService, value, "AudioRecordingService");
+ }
+ }
+
+ RecordingViewModelState _recordingViewModelState;
+ public RecordingViewModelState RecordingViewModelState
+ {
+ get => _recordingViewModelState;
+ private set
+ {
+ SetProperty(ref _recordingViewModelState, value, "RecordingViewModelState");
+ }
+ }
+
+ bool _SttOn;
+ public bool SttOn
+ {
+ get
+ {
+ return _SttOn;
+ }
+
+ set
+ {
+ SetProperty(ref _SttOn, value, "SttOn");
+ }
+ }
+
+ string _recordTitle;
+ public string RecordTitle
+ {
+ get => _recordTitle;
+ set
+ {
+ SetProperty(ref _recordTitle, value, "RecordTitle");
+ }
+ }
+
+ string _recordingTime;
+ public string RecordingTime
+ {
+ get => _recordingTime;
+ set
+ {
+ SetProperty(ref _recordingTime, value, "RecordingTime");
+ }
+ }
+
+ string _toggleImage;
+ public string PauseRecordToggleImage
+ {
+ get => _toggleImage;
+ set
+ {
+ SetProperty(ref _toggleImage, value, "PauseRecordToggleImage");
+ }
+ }
+
+ double _RecordingProcess;
+ public double RecordingProcess
+ {
+ get
+ {
+ return _RecordingProcess;
+ }
+
+ set
+ {
+ SetProperty(ref _RecordingProcess, value, "RecordingProcess");
+ }
+ }
+
+ public ICommand RequestCommand { private set; get; }
+
+ // For restoring information of the created voice media file
+ MediaContentService _ContentService;
+ // For
+ DeviceInformationService _DeviceInfoService;
+ SpeechToTextService _SttService;
+ public int Index;
+
+ double RECORDING_PROGRESSBAR_DELTA;
+
+ bool _isRecordingEffectOn;
+
+ public bool RecordingEffectOn
+ {
+ get
+ {
+ return _isRecordingEffectOn;
+ }
+
+ set
+ {
+ SetProperty(ref _isRecordingEffectOn, value, "RecordingEffectOn");
+ }
+ }
+
+ bool _isTimeFlickeringOn;
+ private bool _requestSttStart;
+ private bool _requestSttStop;
+
+ public bool TimeFlickeringOn
+ {
+ get
+ {
+ return _isTimeFlickeringOn;
+ }
+
+ set
+ {
+ SetProperty(ref _isTimeFlickeringOn, value, "TimeFlickeringOn");
+ }
+ }
+
+ bool disposing;
+
+ private void RecordingStateCallback(Object o, AudioRecordState prev, AudioRecordState current)
+ {
+ switch (current)
+ {
+ case AudioRecordState.Idle:
+ HandleRecorderIdleState(o, prev, current);
+ break;
+ case AudioRecordState.Init:
+ HandleRecorderInitState(o, prev, current);
+ break;
+ case AudioRecordState.Ready:
+ HandleRecorderReadyState(o, prev, current);
+ break;
+ case AudioRecordState.Recording:
+ HandleRecorderRecordingState(o, prev, current);
+ break;
+ case AudioRecordState.Paused:
+ HandleRecorderPauseState(o, prev, current);
+ break;
+ }
+ }
+
+ public void SttStateCallback(Object o, SttState prev, SttState current)
+ {
+ switch (current)
+ {
+ case SttState.Created:
+ HandleSttCreatedState(o, prev, current);
+ break;
+ case SttState.Ready:
+ HandleSttReadyState(o, prev, current);
+ ((App)App.Current).mainPageModel.availableToRecord = true;
+ break;
+ case SttState.Recording:
+ HandleSttRecordingState(o, prev, current);
+ ((App)App.Current).mainPageModel.availableToRecord = false;
+ break;
+ case SttState.Processing:
+ HandleSttProcessingState(o, prev, current);
+ ((App)App.Current).mainPageModel.availableToRecord = false;
+ break;
+ case SttState.Unavailable:
+ HandleSttUnavailableState(o, prev, current);
+ break;
+ }
+ }
+
+ private void HandleSttProcessingState(object o, SttState prev, SttState current)
+ {
+ if (_requestSttStop)
+ {
+ _requestSttStop = false;
+ _SttService.Cancel();
+ }
+ }
+
+ private void HandleSttUnavailableState(object o, SttState prev, SttState current)
+ {
+ }
+
+ private void HandleSttRecordingState(object o, SttState prev, SttState current)
+ {
+ if (_requestSttStop)
+ {
+ _requestSttStop = false;
+ _SttService.Cancel();
+ }
+ }
+
+ private void HandleSttReadyState(object o, SttState prev, SttState current)
+ {
+ if (_requestSttStart && !_requestSttStop)
+ {
+ _requestSttStart = false;
+ _SttService.StartStt();
+ }
+ }
+
+ private void HandleSttCreatedState(object o, SttState prev, SttState current)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void HandleRecorderPauseState(object o, AudioRecordState prev, AudioRecordState current)
+ {
+ StopTimer();
+ StopRecordingEffect();
+ if (RecordingViewModelState != RecordingViewModelState.PausedForCancel)
+ {
+ StartTimeFlickering();
+ RecordingViewModelState = RecordingViewModelState.Paused;
+ PauseRecordToggleImage = "record_stop_icon.png";
+ }
+ }
+
+ private void HandleRecorderRecordingState(object o, AudioRecordState prev, AudioRecordState current)
+ {
+ if (prev == AudioRecordState.Paused) // from pause -> recording
+ {
+ StopTimeFlickering();
+ }
+
+ // Start recording UI on
+ StartTimer();
+ StartRecordingEffect();
+ PauseRecordToggleImage = "recording_icon_pause.png";
+ RecordingViewModelState = RecordingViewModelState.Recording;
+ }
+
+ private void HandleRecorderReadyState(object o, AudioRecordState prev, AudioRecordState current)
+ {
+
+ }
+
+ private void HandleRecorderInitState(object o, AudioRecordState prev, AudioRecordState current)
+ {
+
+ }
+
+ private void HandleRecorderIdleState(object o, AudioRecordState prev, AudioRecordState current)
+ {
+ throw new NotImplementedException();
+ }
+
+ public RecordingPageModel(bool sttOn)
+ {
+ RequestCommand = new Command<RecordingCommandType>(Request);
+
+ if (RecordingWatch == null)
+ {
+ RecordingWatch = new Stopwatch();
+ }
+
+ _audioRecordingService = AudioRecordService.Instance;
+ _audioRecordingService.ViewModel = this;
+ _audioRecordingService.RegisterStateCallbacks(new Action<Object, AudioRecordState, AudioRecordState>(RecordingStateCallback));
+ AudioRecordingService = _audioRecordingService;
+ _DeviceInfoService = DeviceInformationService.Instance;
+ _SttService = SpeechToTextService.Instance;
+ _SttService.RegisterStateCallbacks(new Action<object, SttState, SttState>(SttStateCallback));
+ _ContentService = MediaContentService.Instance;
+ string.Format("{0:00}", _DeviceInfoService.LastStoredFileIndex);
+ Index = _DeviceInfoService.LastStoredFileIndex + 1;
+ SttOn = sttOn;
+ MessagingCenter.Subscribe<MainPageModel, bool>(this, MessageKeys.SttSupportedChanged, (obj, sttIsOn) =>
+ {
+ Console.WriteLine("### MessagingCenter ## [RecordingPageModel] just received << MessageKeys.SttSupportedChanged >> On? :" + sttIsOn);
+ SttOn = sttIsOn;
+ });
+ disposing = false;
+ }
+
+ public void Init()
+ {
+ // This is an entry point to any service
+ // Recorder: ready
+ // Stt: ready if present
+ Console.WriteLine("[RecordingPageModel.Init] SttOn:" + SttOn + ", stt state:" + _SttService.SttState);
+ if (!_DeviceInfoService.StorageAvailable)
+ {
+ // TODO: ContextPopup
+ return;
+ }
+
+ // initialize page
+ RecordingViewModelState = RecordingViewModelState.Ready;
+ RecordingTime = "00:00";
+ RecordingProcess = 0;
+ RecordingWatch.Reset();
+ RecordingEffectOn = false;
+ TimeFlickeringOn = false;
+ RecordTitle = "Memo " + Index.ToString();
+ RECORDING_PROGRESSBAR_DELTA = 0.003333;
+ _requestSttStart = false;
+ _requestSttStop = false;
+
+ if (SttOn)
+ {
+ switch (_SttService.SttState)
+ {
+ case SttState.Created:
+ _requestSttStart = true; // this should be false in Ready callback
+ break;
+ case SttState.Ready:
+ _SttService.StartStt();
+ break;
+ case SttState.Recording:
+ case SttState.Processing:
+ try
+ {
+ Console.WriteLine("[RecordingPageModel.Init] STT state: " + _SttService.SttState + " --> Cancel it");
+ _SttService.Cancel();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Catch possible error while cancel " + e.Message + ", " + e.Source);
+ }
+
+ MessagingCenter.Send<RecordingPageModel>(this, MessageKeys.ForcePopRecordingPage);
+ return;
+ case SttState.Unavailable:
+ break;
+ }
+ }
+ else
+ {
+ RecordTitle = "Voice " + Index.ToString();
+ RECORDING_PROGRESSBAR_DELTA = 0.000555556;
+ }
+
+ _audioRecordingService.Start(RecordTitle, SttOn);
+ }
+
+ void StartTimer()
+ {
+ // TODO: Need to check recorded time and display time
+ RecordingWatch.Start();
+ // Any background code that needs to update the user interface
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ // interact with UI elements
+ Device.StartTimer(new TimeSpan(0, 0, 0, 1, 0), UpdateRecordingTimeAndProgressBar);
+ });
+ }
+
+ void StopTimer()
+ {
+ RecordingWatch.Stop();
+ }
+
+ bool UpdateRecordingTimeAndProgressBar()
+ {
+ //return true to keep the timer running or false to stop it after the current invocation.
+ if (_audioRecordingService.State == AudioRecordState.Recording)
+ {
+ RecordingProcess += RECORDING_PROGRESSBAR_DELTA;
+
+ RecordingTime = string.Format("{0:00}:{1:00}", RecordingWatch.Elapsed.Minutes, RecordingWatch.Elapsed.Seconds);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ void Request(RecordingCommandType request)
+ {
+ switch (request)
+ {
+ case RecordingCommandType.Record:
+ RecordingCommandRequested();
+ break;
+ case RecordingCommandType.Pause:
+ PauseCommandRequested();
+ break;
+ case RecordingCommandType.PauseForCancelRequest:
+ PauseForCancelCommandRequested();
+ break;
+ case RecordingCommandType.Resume:
+ ResumeCommandRequested();
+ break;
+ case RecordingCommandType.Cancel:
+ CancelCommandRequested();
+ break;
+ case RecordingCommandType.Stop:
+ StopCommandRequested();
+ break;
+ }
+ }
+
+ private void RecordingCommandRequested()
+ {
+ // 20180220-vincent: At this point audioRecordingService is either Recording or Paused
+ if (_audioRecordingService.State == AudioRecordState.Paused)
+ {
+ _audioRecordingService.Resume();
+ }
+ }
+
+ private async void StopCommandRequested()
+ {
+ if (RecordingViewModelState == RecordingViewModelState.Stopped)
+ {
+ return; // if already set to stopped, then do not repeat
+ }
+
+ RecordingViewModelState = RecordingViewModelState.Stopped;
+
+ if (!disposing)
+ {
+ StopTimer();
+ StopRecordingEffect();
+ StopTimeFlickering();
+ }
+
+ Record record = SaveRecording();
+ bool ExistText = SttOn && _SttService.SttState == SttState.Recording;
+ if (ExistText)
+ {
+ string SttText = await _SttService.StopAndGetText();
+ record.Text = SttText;
+ Console.WriteLine("[StopCommandRequested] record.Text : (" + record.Text + ")");
+ }
+ else if (SttOn)
+ {
+ // TODO: remove it.
+ record.Text = "STT Error? " + _SttService.SttState;
+ }
+
+ // Notify that it's time to save Record in database
+ MessagingCenter.Send<RecordingPageModel, Record>(this, MessageKeys.SaveVoiceMemoInDB, record);
+
+ if (ExistText &&
+ (string.IsNullOrEmpty(record.Text) || record.Text.Equals("") || record.Text.Equals(" ") || record.Text.Equals(" ")))
+ {
+ Console.WriteLine("[StopCommandRequested] !!!!!!!!! resultText is string.IsNullOrEmpty");
+ record.Text = AppResources.RecognitionFailed;
+ }
+
+ if (disposing)
+ {
+ DisposeServices();
+ }
+ }
+
+ Record SaveRecording()
+ {
+ var filePath = _audioRecordingService.Save();
+ // TODO: Handle audio recording failure case
+ Index++;
+ Record record = _ContentService.GetMediaInfo(filePath, SttOn);
+ MessagingCenter.Send<RecordingPageModel, Record>(this, MessageKeys.SaveVoiceMemo, new LatestRecord(record));
+ return record;
+ }
+
+ private void CancelCommandRequested()
+ {
+ Console.WriteLine("\n\n CANCEL --- AUDIO RECORDING & STT");
+ _requestSttStart = false; // initialize stt start request
+ if (RecordingViewModelState == RecordingViewModelState.Paused)
+ {
+ StopTimeFlickering();
+ }
+
+ if (SttOn)
+ {
+ _requestSttStop = true;
+ if (_SttService.SttState == SttState.Recording || _SttService.SttState == SttState.Processing)
+ {
+ _SttService.Cancel();
+ }
+ else // created, ready
+ {
+ // do not need to cancel but should not start
+ if (_requestSttStart)
+ {
+ _requestSttStart = false; // prevent create->ready (should not start)
+ }
+ }
+ }
+
+ _audioRecordingService.Cancel();
+ RecordingViewModelState = RecordingViewModelState.Cancelled;
+ }
+
+ private void ResumeCommandRequested()
+ {
+ if (RecordingViewModelState == RecordingViewModelState.PausedForCancel)
+ {
+ Request(RecordingCommandType.Record);
+ }
+ }
+
+ private void PauseCommandRequested()
+ {
+ if (_audioRecordingService.State == AudioRecordState.Recording)
+ {
+ _audioRecordingService.Pause();
+ // Any background code that needs to update the user interface
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ // interact with UI elements
+ Device.StartTimer(new TimeSpan(0, 0, 0, 60, 0), DoAutoSaveAndTerminate);
+ });
+ }
+ }
+
+ bool DoAutoSaveAndTerminate()
+ {
+
+ //if (_audioRecordingService.State == AudioRecordState.Paused)
+ //{
+ // RequestCommand.Execute(RecordingCommandType.Stop);
+
+ //}
+ Console.WriteLine(" Now, save recording and terminate this application. ");
+ // Terminate this application
+ Native.Application.Current.Exit();
+ return false;
+ }
+
+ private void PauseForCancelCommandRequested()
+ {
+ if (_audioRecordingService.State == AudioRecordState.Recording)
+ {
+ RecordingViewModelState = RecordingViewModelState.PausedForCancel;
+ _audioRecordingService.Pause();
+ }
+ }
+
+ private void StartRecordingEffect()
+ {
+ RecordingEffectOn = true;
+ }
+
+ private void StopRecordingEffect()
+ {
+ RecordingEffectOn = false;
+ }
+
+ private void StartTimeFlickering()
+ {
+ TimeFlickeringOn = true;
+ }
+
+ private void StopTimeFlickering()
+ {
+ TimeFlickeringOn = false;
+ }
+
+ public void Dispose()
+ {
+ Console.WriteLine("RecordingPageModel.Dispose() START");
+
+ MessagingCenter.Unsubscribe<MainPageModel, bool>(this, MessageKeys.SttSupportedChanged);
+ if (_audioRecordingService.State == AudioRecordState.Recording || _audioRecordingService.State == AudioRecordState.Paused)
+ {
+ disposing = true;
+ RequestCommand.Execute(RecordingCommandType.Stop);
+ }
+ else
+ {
+ DisposeServices();
+ }
+ //////#if STT_ON
+ ////// if (SttOn)
+ ////// {
+ ////// Console.WriteLine("RecordingPageModel.Dispose() _SttService.Dispose");
+ ////// _SttService.Dispose();
+ ////// }
+ //////#endif
+ ////// _ContentService.Destroy();
+ ////// _audioRecordingService.Destroy();
+ Console.WriteLine("RecordingPageModel.Dispose() DONE");
+ }
+
+ void DisposeServices()
+ {
+ Console.WriteLine("RecordingPageModel.DisposeServices() START");
+ _audioRecordingService.Destroy();
+ _SttService.Dispose();
+ _ContentService.Destroy();
+ Console.WriteLine("RecordingPageModel.DisposeServices() DONE");
+ }
+ }
+}
--- /dev/null
+using Xamarin.Forms;
+using Tizen.Wearable.CircularUI.Forms;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// class custom circle toolbar item
+ /// Its visibility can be changeable.
+ /// </summary>
+ public class BindableToolbarItem : CircleToolbarItem
+ {
+ public static readonly BindableProperty IsVisibleProperty =
+ BindableProperty.Create("BindableToolbarItem", typeof(bool), typeof(ToolbarItem),
+ true, BindingMode.TwoWay, propertyChanged: OnIsVisibleChanged);
+
+ public BindableToolbarItem()
+ {
+ InitVisibility();
+ }
+
+ /// <summary>
+ /// ToolbarItem's visibility
+ /// </summary>
+ public bool IsVisible
+ {
+ get { return (bool)GetValue(IsVisibleProperty); }
+ set { SetValue(IsVisibleProperty, value); }
+ }
+
+ private void InitVisibility()
+ {
+ OnIsVisibleChanged(this, false, IsVisible);
+ }
+
+ private static void OnIsVisibleChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ var item = bindable as BindableToolbarItem;
+
+ if (item != null && item.Parent == null)
+ {
+ return;
+ }
+
+ if (item != null)
+ {
+ var items = ((ContentPage)item.Parent).ToolbarItems;
+
+ // case : add to toolbar items
+ if ((bool)newvalue && !items.Contains(item))
+ {
+ Device.BeginInvokeOnMainThread(() => { items.Add(item); });
+ }
+ // case : remove from toolbar items
+ else if (!(bool)newvalue && items.Contains(item))
+ {
+ Device.BeginInvokeOnMainThread(() => { items.Remove(item); });
+ }
+ }
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<w:TwoButtonPage
+ x:Class="VoiceMemo.Views.CancelPage"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:resx="clr-namespace:VoiceMemo.Resx;"
+ xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms">
+ <w:TwoButtonPage.FirstButton>
+ <MenuItem Clicked="OnAbortCancelClicked" Icon="tw_ic_popup_btn_delete.png" />
+ </w:TwoButtonPage.FirstButton>
+ <w:TwoButtonPage.SecondButton>
+ <MenuItem Clicked="OnCancelClicked" Icon="tw_ic_popup_btn_check.png" />
+ </w:TwoButtonPage.SecondButton>
+
+ <w:TwoButtonPage.Content>
+ <StackLayout BackgroundColor="Black">
+ <Label
+ x:Name="CancelLabel"
+ FontSize="Medium"
+ HorizontalOptions="Center"
+ HorizontalTextAlignment="Center"
+ Text="{x:Static resx:AppResources.CancelRecording}"
+ TextColor="White"
+ VerticalOptions="CenterAndExpand"
+ VerticalTextAlignment="Center"
+ />
+ </StackLayout>
+ </w:TwoButtonPage.Content>
+</w:TwoButtonPage>
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.ViewModels;
+using VoiceMemo.Resx;
+using Xamarin.Forms;
+using Tizen.Wearable.CircularUI.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// CancelPage class
+ /// It provides a way to cancel voice recording
+ /// </summary>
+ public partial class CancelPage : TwoButtonPage
+ {
+ RecordingPageModel ViewModel;
+
+ public CancelPage(RecordingPageModel vm)
+ {
+ // Hide navigation bar
+ NavigationPage.SetHasNavigationBar(this, false);
+ // Subscribe notification of locale changes to update text based on locale
+ MessagingCenter.Subscribe<App>(this, MessageKeys.UpdateByLanguageChange, (obj) =>
+ {
+ // Update text that has been translated into the current language.
+ CancelLabel.Text = AppResources.CancelRecording;
+ });
+ InitializeComponent();
+ Init(vm);
+ }
+
+ void Init(RecordingPageModel vm)
+ {
+ BindingContext = ViewModel = vm;
+ }
+
+ // Keep recording
+ // CancelPage --> RecordingPage
+ async void OnAbortCancelClicked(object sender, EventArgs args)
+ {
+ // Request to Resume
+ ViewModel.RequestCommand.Execute(RecordingCommandType.Resume);
+ await Navigation.PopAsync();
+ }
+
+ // Cancel recording voice
+ // CancelPage --> StandByPage(First main page)
+ async void OnCancelClicked(object sender, EventArgs args)
+ {
+ // Request to Cancel
+ ViewModel.RequestCommand.Execute(RecordingCommandType.Cancel);
+ await Navigation.PopToRootAsync(); // @20180218-vincent: This should never be used without clearing
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<w:CirclePage 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"
+ x:Class="VoiceMemo.Views.CirclePageEx">
+</w:CirclePage>
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.Views
+{
+ /// <summary>
+ /// Base Page class
+ /// </summary>
+ public partial class CirclePageEx : CirclePage
+ {
+ public CirclePageEx()
+ {
+ // Hide navigation bar
+ NavigationPage.SetHasNavigationBar(this, false);
+
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<view:CirclePageEx
+ x:Class="VoiceMemo.Views.DetailsPage"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:converters="clr-namespace:VoiceMemo.Converters;"
+ xmlns:view="clr-namespace:VoiceMemo.Views;"
+ xmlns:vm="clr-namespace:VoiceMemo.ViewModels;"
+ xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms"
+ RotaryFocusObject="{x:Reference ScrollTextView}"
+ x:Name="Details">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+ <Color x:Key="AT0116">#FF4DCFFF</Color>
+ <Style x:Key="LabelStyle-AT0116" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="TextColor" Value="{StaticResource AT0116}" />
+ </Style>
+ <Color x:Key="AT0114">#FFB3B3B3</Color>
+ <Style x:Key="LabelStyle-AT0114" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="8" />
+ <Setter Property="TextColor" Value="{StaticResource AT0114}" />
+ </Style>
+ <converters:DurationToRemainingTimeConverter x:Key="TimeConverter" />
+ <converters:RemainingTimeType x:Key="TimeType" />
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <w:CircleScrollView
+ x:Name="ScrollTextView"
+ Orientation="Vertical">
+ <RelativeLayout x:Name="mainLayout">
+ <Label
+ x:Name="TitleLabel"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=64}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0, Constant=51}"
+ WidthRequest="232"
+ HeightRequest="39"
+ Text="{Binding Record.Title}"
+ Style="{StaticResource LabelStyle-AT0116}" />
+ <Label
+ x:Name="DateInfoLabel"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=33}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0, Constant=115}"
+ WidthRequest="294"
+ HeightRequest="32"
+ Text="{Binding Record.Date}"
+ Style="{StaticResource LabelStyle-AT0114}" />
+ <Label
+ x:Name="TimeInfoLabel"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=33}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0, Constant=147}"
+ WidthRequest="294"
+ HeightRequest="32"
+ Text="{Binding Record.Duration, Converter={StaticResource TimeConverter}, ConverterParameter={x:Static converters:RemainingTimeType.TimeText}}"
+ Style="{StaticResource LabelStyle-AT0114}"/>
+ <Label
+ x:Name="ContentLabel"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=33}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0, Constant=179}"
+ WidthRequest="294"
+ Text="{Binding Record.Text}"
+ TextColor="White"
+ HorizontalTextAlignment="Center"
+ VerticalTextAlignment="Center" />
+ <!-- default font size of label : 9.2936170212766-->
+ </RelativeLayout>
+ </w:CircleScrollView>
+ </ContentPage.Content>
+ <w:CirclePage.ActionButton>
+ <w:ActionButtonItem Clicked="OnPlayBackClicked" Icon="details_playback_icon.png" />
+ </w:CirclePage.ActionButton>
+ <w:CirclePage.ToolbarItems>
+ <w:CircleToolbarItem
+ Clicked="OnCircleToolbarItemClicked_DeleteRecord"
+ Icon="more_option_icon_delete.png"
+ Text="Delete" />
+ </w:CirclePage.ToolbarItems>
+</view:CirclePageEx>
\ No newline at end of file
--- /dev/null
+using VoiceMemo.Resx;
+/*
+ * 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 VoiceMemo.Models;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// DetailsPage class
+ /// It shows the details of voice memo
+ /// file name, recorded time, recording time
+ /// </summary>
+ public partial class DetailsPage : CirclePageEx
+ {
+ DetailsPageModel viewModel;
+
+ public DetailsPage(DetailsPageModel _viewModel)
+ {
+ InitializeComponent();
+ BindingContext = viewModel = _viewModel;
+
+ // Spare as high as an action button when a text is long
+ mainLayout.Children.Add(new BoxView(), Constraint.RelativeToView(ContentLabel, (Parent, sibling) =>
+ {
+ return sibling.X;
+ }), Constraint.RelativeToView(ContentLabel, (parent, sibling) =>
+ {
+ return sibling.Y + sibling.Height;
+ }), Constraint.RelativeToParent((parent) =>
+ {
+ return 294;
+ }), Constraint.RelativeToParent((parent) =>
+ {
+ return 78 + 7;
+ }));
+ }
+
+ /// <summary>
+ /// Request to play voice memo/record
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="args">EventArgs</param>
+ async void OnPlayBackClicked(object sender, EventArgs args)
+ {
+ // Move to the recording page to keep recording
+ Console.WriteLine("[DetailsPage.OnPlayBackClicked] ");
+ await Navigation.PushAsync(PageFactory.GetInstance(Pages.PlayBack, viewModel.Record/*new PlayBackPageModel(viewModel.Record)*/));
+ }
+
+ /// <summary>
+ /// Request to delete the record
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="args">EventArgs</param>
+ /*async*/ void OnCircleToolbarItemClicked_DeleteRecord(object sender, EventArgs args)
+ {
+ Console.WriteLine("[OnCircleToolbarItemClicked_DeleteRecord] sender : " + sender + ", record: " + ((DetailsPageModel)viewModel).Record);
+
+ //await DisplayAlert("Delete", "Delete this recording", "OK");
+ GraphicPopUp popup = new GraphicPopUp
+ {
+ Text = AppResources.Deleted,
+ };
+ popup.TimedOut += Popup_TimedOut;
+ popup.Show();
+
+ // Publish "DeleteVoiceMemo" message for a listener to remove this record from database.
+ MessagingCenter.Send<Page, Record>(this, MessageKeys.DeleteVoiceMemo, ((DetailsPageModel)viewModel).Record);
+ }
+
+ private async void Popup_TimedOut(object sender, EventArgs e)
+ {
+ Console.WriteLine("[DetailsPage] Popup_TimedOut() ...");
+ await Navigation.PopToRootAsync();
+ }
+
+ /// <summary>
+ /// Invoked immediately prior to the Page becoming visible.
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ Console.WriteLine("[DetailsPage] OnAppearing() ...");
+ }
+
+ /// <summary>
+ /// Invoked when this page disappears.
+ /// </summary>
+ protected override void OnDisappearing()
+ {
+ Console.WriteLine("[DetailsPage] OnDisappearing");
+ }
+
+ /// <summary>
+ /// Invoked when backbutton is pressed
+ /// VoiceMemoStandByPage will be shown.
+ /// </summary>
+ /// <returns>bool value</returns>
+ protected override bool OnBackButtonPressed()
+ {
+ if (((DetailsPageModel)BindingContext).IsNew)
+ {
+ Console.WriteLine("[DetailsPage-OnBackButtonPressed] PopToRootAsync");
+ Navigation.PopToRootAsync();
+ }
+ else
+ {
+ Console.WriteLine("[DetailsPage-OnBackButtonPressed] PopAsync");
+ Navigation.PopAsync();
+ }
+
+ return true;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 System;
+using VoiceMemo.Tizen.Wearable.Renderers;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// Class GraphicPopUp
+ /// </summary>
+ public class GraphicPopUp : BindableObject
+ {
+ public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(GraphicPopUp), null);
+
+ public static readonly BindableProperty DurationProperty = BindableProperty.Create(nameof(Duration), typeof(double), typeof(GraphicPopUp), 1.5);
+
+ IGraphicPopup _popUp = null;
+
+ public event EventHandler BackButtonPressed;
+ public event EventHandler TimedOut;
+
+ // Constructor
+ public GraphicPopUp()
+ {
+ _popUp = new GraphicPopUpRenderer();
+ if (_popUp == null)
+ {
+ throw new Exception("Object reference not set to an instance of a Popup.");
+ }
+
+ _popUp.BackButtonPressed += (s, e) =>
+ {
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ };
+
+ _popUp.TimedOut += (s, e) =>
+ {
+ TimedOut?.Invoke(this, EventArgs.Empty);
+ };
+
+ SetBinding(TextProperty, new Binding(nameof(Text), mode: BindingMode.OneWayToSource, source: _popUp));
+ SetBinding(DurationProperty, new Binding(nameof(Duration), mode: BindingMode.OneWayToSource, source: _popUp));
+ }
+
+ /// <summary>
+ /// Gets or sets text of the Popup.
+ /// </summary>
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets duration of the Popup.
+ /// </summary>
+ public double Duration
+ {
+ get { return (double)GetValue(DurationProperty); }
+ set { SetValue(DurationProperty, value); }
+ }
+
+ /// <summary>
+ /// Show Toast Popup
+ /// </summary>
+ public void Show()
+ {
+ _popUp.Show();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// Interface IPopup for Toast Popup
+ /// </summary>
+ public interface IGraphicPopup
+ {
+ /// <summary>
+ /// Text for Toast Popup
+ /// </summary>
+ string Text { get; set; }
+
+ /// <summary>
+ /// Duration for Toast Popup
+ /// How long to display the text message
+ /// </summary>
+ double Duration { get; set; }
+
+ /// <summary>
+ /// Make Toast Popup show
+ /// </summary>
+ void Show();
+
+ /// <summary>
+ /// Raised when back button is pressed
+ /// </summary>
+ event EventHandler BackButtonPressed;
+
+ /// <summary>
+ /// Raised after the text is shown for the specified duration
+ /// </summary>
+ event EventHandler TimedOut;
+ }
+}
--- /dev/null
+/*
+ * 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 VoiceMemo.Views
+{
+ /// <summary>
+ /// IProgressbarPopup interface
+ /// </summary>
+ public interface IProgressbarPopup : IGraphicPopup
+ {
+ /// <summary>
+ /// Text for Progressbar
+ /// </summary>
+ string ProgressbarText { get; set; }
+
+ /// <summary>
+ /// Duration for Progressbar
+ /// How long to display the text message with progressbar
+ /// </summary>
+ double ProgressbarDuration { get; set; }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<view:CirclePageEx
+ x:Class="VoiceMemo.Views.LanguageSelectionPage"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:converters="clr-namespace:VoiceMemo.Converters;"
+ xmlns:vms="clr-namespace:VoiceMemo.ViewModels;"
+ xmlns:view="clr-namespace:VoiceMemo.Views;"
+ xmlns:resx="clr-namespace:VoiceMemo.Resx;"
+ xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms"
+ RotaryFocusObject="{x:Reference LangListView}">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+ <Style x:Key="BaseLabelStyle" TargetType="Label">
+ <Setter Property="HorizontalOptions" Value="Center" />
+ <Setter Property="VerticalOptions" Value="Center" />
+ <Setter Property="VerticalTextAlignment" Value="Center" />
+ <Setter Property="HorizontalTextAlignment" Value="Center" />
+ <Setter Property="TextColor" Value="White" />
+ </Style>
+ <converters:CountryCodeToNameConverter x:Key="LangConverter" />
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <view:CirclePageEx.Content>
+ <w:CircleListView
+ x:Name="LangListView"
+ HasUnevenRows="True"
+ HorizontalOptions="FillAndExpand"
+ ItemTapped="OnItemTapped"
+ ItemsSource="{Binding Languages}"
+ VerticalOptions="FillAndExpand">
+ <ListView.Footer>
+ <StackLayout HeightRequest="115">
+ <BoxView />
+ </StackLayout>
+ </ListView.Footer>
+ <ListView.Header>
+ <StackLayout HeightRequest="115">
+ <!--original #4DCFFF-->
+ <Label
+ x:Name="headerLabel"
+ TextColor="#5aFFFF"
+ Style="{StaticResource BaseLabelStyle}"
+ Text="{x:Static resx:AppResources.Languages}"
+ FontSize="16"
+ FontAttributes="Bold"
+ VerticalOptions="FillAndExpand" />
+ </StackLayout>
+ </ListView.Header>
+ <ListView.ItemTemplate>
+ <DataTemplate>
+ <view:SttLanguageViewCell />
+ </DataTemplate>
+ </ListView.ItemTemplate>
+ </w:CircleListView>
+ </view:CirclePageEx.Content>
+</view:CirclePageEx>
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.Models;
+using VoiceMemo.Resx;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// LanguageSelectionPage class
+ /// </summary>
+ public partial class LanguageSelectionPage : CirclePageEx
+ {
+ MainPageModel ViewModel;
+ public bool ignoreRadioSelection = true;
+
+ public LanguageSelectionPage(MainPageModel viewModel)
+ {
+ BindingContext = ViewModel = viewModel;
+ // Subscribe notification of locale changes to update text based on locale
+ MessagingCenter.Subscribe<App>(this, MessageKeys.UpdateByLanguageChange, (obj) =>
+ {
+ // Update text that has been translated into the current language.
+ headerLabel.Text = AppResources.Languages;
+ });
+ InitializeComponent();
+ Init();
+ }
+
+ public void Init()
+ {
+ Console.WriteLine("[LanguageSelectionPage.Init()] LangListView.SelectedItem :" + LangListView.SelectedItem
+ + " VS. ViewModel.SelectedItemIndex :" + ViewModel.SelectedItemIndex);
+ ignoreRadioSelection = true;
+
+ // Scroll to the previously selected language if it exists.
+ if (ViewModel.SelectedItemIndex != null)
+ {
+ LangListView.ScrollTo(ViewModel.SelectedItemIndex, ScrollToPosition.Center, false);
+ }
+ }
+
+ /// <summary>
+ /// Invoked when an item is tapped
+ /// </summary>
+ /// <param name="sender">object</param>
+ /// <param name="args">ItemTappedEventArgs</param>
+ void OnItemTapped(object sender, ItemTappedEventArgs args)
+ {
+ ignoreRadioSelection = false;
+
+ ViewModel.CurrentLanguage = ((SttLanguage)args.Item).Lang;
+ SttLanguage item = args.Item as SttLanguage;
+ for (int i = 0; i < ((MainPageModel)BindingContext).Languages.Count; i++)
+ {
+ if (((MainPageModel)BindingContext).Languages[i].Combo == item.Combo)
+ {
+ Console.WriteLine("OnItemTapped : *" + i + " ) FOUND!! IsOn will be true! ");
+ // The following code makes the Radio button of SttLanguageViewCell is selected.
+ ((MainPageModel)BindingContext).Languages[i].IsOn = true;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Invoked immediately prior to the Page becoming visible.
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ Console.WriteLine("[LanguageSelectionPage] OnAppearing() ...");
+ }
+
+ protected override void OnDisappearing()
+ {
+ Console.WriteLine("[LanguageSelectionPage] OnDisappearing");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<views:CirclePageEx x:Class="VoiceMemo.Views.MainPage"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:converters="clr-namespace:VoiceMemo.Converters;"
+ xmlns:views="clr-namespace:VoiceMemo.Views;"
+ xmlns:vm="clr-namespace:VoiceMemo.ViewModels;"
+ xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms"
+ xmlns:resx="clr-namespace:VoiceMemo.Resx;"
+ x:Name="StandBy"
+ Title="MainPage">
+ <views:CirclePageEx.Resources>
+ <ResourceDictionary>
+ <Color x:Key="AT011">#FFFAFAFA</Color>
+ <Style x:Key="LabelStyle-AT011" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="20" />
+ <Setter Property="TextColor" Value="{StaticResource AT011}" />
+ </Style>
+ <Color x:Key="AT0116">#FF4DCFFF</Color>
+ <Style x:Key="LabelStyle-AT0116" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="TextColor" Value="{StaticResource AT0116}" />
+ </Style>
+ </ResourceDictionary>
+ </views:CirclePageEx.Resources>
+ <views:CirclePageEx.BindingContext>
+ <vm:MainPageModel x:Name="ViewModel" />
+ </views:CirclePageEx.BindingContext>
+ <views:CirclePageEx.Content>
+ <AbsoluteLayout x:Name="MainView" BackgroundColor="Black">
+ <!-- Stand by -->
+ <Image AbsoluteLayout.LayoutBounds=".5, .5, 96, 96"
+ AbsoluteLayout.LayoutFlags="PositionProportional"
+ Source="2019_custom_button/voicerecorder_btn_bg.png"
+ x:Name="VoiceRecorderBgImage" />
+ <Image AbsoluteLayout.LayoutBounds=".5, .5, 96, 96"
+ AbsoluteLayout.LayoutFlags="PositionProportional"
+ Source="2019_custom_button/voicerecorder_stop_icon.png"
+ x:Name="VoiceRecorderIconImage" />
+ <Label x:Name="TitleLabel"
+ AbsoluteLayout.LayoutBounds="64,51,232,39"
+ AbsoluteLayout.LayoutFlags="None"
+ Text="{Binding MainLabelText}"
+ Style="{StaticResource LabelStyle-AT0116}" />
+ <Label
+ x:Name="MinTimeLabel"
+ AbsoluteLayout.LayoutBounds="102,249,156,79"
+ AbsoluteLayout.LayoutFlags="None"
+ Text="00:00"
+ Style="{StaticResource LabelStyle-AT011}" />
+ </AbsoluteLayout>
+ </views:CirclePageEx.Content>
+ <w:CirclePage.ToolbarItems>
+ <w:CircleToolbarItem
+ x:Name="recordsMenu"
+ Icon="2019_images_icons/more_option_icon_stt_recording.png"
+ Clicked="OnCircleToolbarItemClicked_DisplayRecordings"
+ Text="{x:Static resx:AppResources.Recordings}" />
+ <w:CircleToolbarItem
+ x:Name="sttOnOfMenu"
+ Icon="2019_images_icons/more_option_icon_stt_on.png"
+ Clicked="OnCircleToolbarItemClicked_OnOffStt"
+ Text="{x:Static resx:AppResources.SpeechToText}" />
+ <w:CircleToolbarItem
+ x:Name="languageList"
+ Icon="2019_images_icons/more_option_icon_language.png"
+ Clicked="OnCircleToolbarItemClicked_SelectLanguage"
+ Text="{x:Static resx:AppResources.Languages}" />
+ </w:CirclePage.ToolbarItems>
+</views:CirclePageEx>
--- /dev/null
+/*
+ * 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 Xamarin.Forms.Xaml;
+using VoiceMemo.ViewModels;
+using VoiceMemo.Resx;
+using Xamarin.Forms;
+using Tizen.Wearable.CircularUI.Forms;
+using VoiceMemo.Effects;
+using VoiceMemo.Utility;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// The MainPage of VoiceMemo application
+ /// </summary>
+ public partial class MainPage : CirclePageEx
+ {
+ /// <summary>
+ /// Constructor of StandByPage()
+ /// </summary>
+ public MainPage()
+ {
+ InitializeComponent();
+ Color stopIconColor = ColorConverter.OneUIColorConverter(new HSB(0, 98, 88, 100), null);
+ ImageAttributes.SetBlendColor(VoiceRecorderIconImage, stopIconColor);
+ var tapGestureRecognizer = new TapGestureRecognizer();
+ tapGestureRecognizer.Tapped += OnImageButtonClicked;
+ VoiceRecorderIconImage.GestureRecognizers.Add(tapGestureRecognizer);
+ }
+
+ /// <summary>
+ /// Invoked when recording(red) button is tapped.
+ /// RecordingPage will be shown.
+ /// </summary>
+ /// <param name="sender"> Sender object</param>
+ /// <param name="e">EventArgs</param>
+ async void OnImageButtonClicked(object sender, EventArgs e)
+ {
+ // While STT is in progress, a new recording cannot be started.
+ if (((App)App.Current).mainPageModel.availableToRecord)
+ {
+ // can record
+ await Navigation.PushAsync(PageFactory.GetInstance(Pages.Recording, ((MainPageModel)ViewModel).SttEnabled));
+ }
+ else
+ {
+ // cannot record
+ await DisplayAlert("Recording", "Stt service is in progress. Please wait a moment.", "OK");
+ }
+ }
+
+ /// <summary>
+ /// Standby > More options > Recordings
+ /// A list of Recordings will be shown if there are recordings.
+ /// </summary>
+ /// <param name="sender"> Sender object</param>
+ /// <param name="e">EventArgs</param>
+ async void OnCircleToolbarItemClicked_DisplayRecordings(object sender, EventArgs e)
+ {
+ Console.WriteLine(" MainPage.OnCircleToolbarItemClicked_DisplayRecordings()");
+ await Navigation.PushAsync(PageFactory.GetInstance(Pages.RecordList, ViewModel));
+ }
+
+ /// <summary>
+ /// Standby > More options > Speech to text
+ /// Invoked when "Speech to text" more option is selected to turn Speech to text feature on/off
+ /// </summary>
+ /// <param name="sender">sender object </param>
+ /// <param name="e">EventArgs</param>
+ void OnCircleToolbarItemClicked_OnOffStt(object sender, EventArgs e)
+ {
+ Console.WriteLine(" MainPage.OnCircleToolbarItemClicked_OnOffStt()");
+ }
+
+ /// <summary>
+ /// Called when Language is selected in More option
+ /// Standby > More options > Language
+ /// </summary>
+ /// <param name="sender">sender object </param>
+ /// <param name="e">EventArgs</param>
+ async void OnCircleToolbarItemClicked_SelectLanguage(object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(PageFactory.GetInstance(Pages.Languages, ViewModel));
+ }
+
+
+ /// <summary>
+ /// Invoked immediately prior to the Page becoming visible.
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ }
+
+ /// <summary>
+ /// Invoked when this page disappears.
+ /// </summary>
+ protected override void OnDisappearing()
+ {
+ }
+
+ public void Dispose()
+ {
+ ((MainPageModel)BindingContext).Dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.Models;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Views
+{
+ public enum Pages
+ {
+ StandBy,
+ StandByCS,
+ Recording,
+ TryCancel,
+ Details,
+ RecordList,
+ PlayBack,
+ Languages,
+ }
+
+ public static class PageFactory
+ {
+ static MainPage main;
+ //static MainPageCS main2;
+ static RecordingPage record;
+ static CancelPage cancel;
+ static RecordListPage list;
+ static DetailsPage detail;
+ static PlayBackPage playback;
+ static LanguageSelectionPage lang;
+
+ public static Page GetInstance(Pages page, Object o = null)
+ {
+ switch (page)
+ {
+ //case Pages.StandByCS:
+ // return PageFactory.main2 ?? (main2 = new MainPageCS());
+ case Pages.StandBy:
+ return PageFactory.main ?? (main = new MainPage());
+ case Pages.Recording:
+ var sttOn = System.Convert.ToBoolean(o);
+ record?.Init(/*sttOn*/);
+ return PageFactory.record ?? (record = new RecordingPage(new RecordingPageModel(sttOn)));
+ case Pages.TryCancel:
+ //VoiceRecordingPageModel vm = o as VoiceRecordingPageModel;
+ return PageFactory.cancel ?? (cancel = new CancelPage(o as RecordingPageModel));
+ case Pages.Details:
+ if (detail != null)
+ {
+ //Console.WriteLine("<PageFactory> DetailsPageModel : " + ((DetailsPageModel)detail.BindingContext));
+ ((DetailsPageModel)detail.BindingContext).Init(o as Record);
+ }
+ //Console.WriteLine("<PageFactory> Return detail : " + detail);
+ return PageFactory.detail ?? (detail = new DetailsPage(new DetailsPageModel(o as Record)));
+ case Pages.RecordList:
+ return PageFactory.list ?? (list = new RecordListPage((MainPageModel)o));
+
+ case Pages.PlayBack:
+ if (playback != null)
+ {
+ ((PlayBackPageModel)playback.BindingContext).Init(o as Record);
+ }
+
+ return PageFactory.playback ?? (playback = new PlayBackPage(new PlayBackPageModel(o as Record)));
+ case Pages.Languages:
+ Console.WriteLine("<PageFactory> ++++++++++++ Languages");
+ lang?.Init();
+ return PageFactory.lang ?? (lang = new LanguageSelectionPage((MainPageModel)o));
+ default:
+ return null;
+ }
+ }
+
+ public static void DestoryPage()
+ {
+ //cancel?.Dispose();
+ ////list?.Dispose();
+ ////detail?.Dispose();
+ ////lang?.Dispose();
+ playback?.Dispose();
+ record?.Dispose();
+ //main2?.Dispose();
+ main?.Dispose();
+ }
+ }
+}
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<views:CirclePageEx
+ x:Class="VoiceMemo.Views.PlayBackPage"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:converters="clr-namespace:VoiceMemo.Converters;"
+ xmlns:views="clr-namespace:VoiceMemo.Views;"
+ xmlns:vm="clr-namespace:VoiceMemo.ViewModels;"
+ xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms" >
+ <!--TODO: it is possible to cast CircleSlider to RotaryFocusable object. RotaryFocusTargetName="PlayProgress">-->
+ <w:CirclePage.CircleSurfaceItems>
+ <w:CircleProgressBarSurfaceItem x:Name="PlayProgress" Value="{Binding PlayingProcess}" />
+ <!--// BG Dim Code : AO0241 - #FF404040
+ // BG Normal Code : AO0231 - #FF004373
+ // Progress BG : Normal : AO0232 - #FF467ea6
+ // Progress BG : DIM : AO0242 - #994673A6
+ // Progress BG : MAX VOLUME : AO025 - #ffe84c0e-->
+
+ <!--<w:CircleSliderSurfaceItem
+ x:Name="VolumeProgress"
+ BackgroundColor="DarkBlue"
+ BarColor="Cyan"
+ IsVisible="{Binding VolumeViewVisible}"
+ Value="{Binding VolumeLevelProcess}" />-->
+ <!--<w:CircleProgressBarSurfaceItem
+ x:Name="VolumeProgress"
+
+ BackgroundColor="DarkBlue"
+ BarColor="Cyan"
+ IsVisible="{Binding VolumeViewVisible}" />-->
+ <!--Value="{Binding VolumeLevelProcess}" />-->
+ </w:CirclePage.CircleSurfaceItems>
+ <ContentPage.Resources>
+ <ResourceDictionary>
+ <Color x:Key="AT011">#FFFAFAFA</Color>
+ <Style x:Key="LabelStyle-AT011" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="20" />
+ <Setter Property="TextColor" Value="{StaticResource AT011}" />
+ </Style>
+ <Color x:Key="AT0211">#FFFAFAFA</Color>
+ <Style x:Key="LabelStyle-AT0211" TargetType="Label">
+ <Setter Property="FontAttributes" Value="Bold" />
+ <!--<Setter Property="HorizontalOptions" Value="Center" />
+ <Setter Property="VerticalOptions" Value="Center" />-->
+ <Setter Property="HorizontalTextAlignment" Value="Center" />
+ <Setter Property="VerticalTextAlignment" Value="Center" />
+ <Setter Property="FontSize" Value="13" />
+ <Setter Property="TextColor" Value="{StaticResource AT0211}" />
+ </Style>
+ <converters:DurationToRemainingTimeConverter x:Key="TimeConverter" />
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <AbsoluteLayout x:Name="PlaybackLayout">
+ <Image x:Name="VolumeImage"
+ AbsoluteLayout.LayoutBounds="145,32,70,70"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="{Binding MuteOnOffSmallImage}">
+ <Image.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding VolumeViewCommand}" />
+ </Image.GestureRecognizers>
+ </Image>
+ <Image x:Name="PlaybackImage"
+ AbsoluteLayout.LayoutBounds="117,116,126,126"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="{Binding PlayControlImage}">
+ <Image.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding PlayControlCommand}" />
+ </Image.GestureRecognizers>
+ </Image>
+ <Label x:Name="RemainingTimeLabel"
+ AbsoluteLayout.LayoutBounds="102,242,156,85"
+ AbsoluteLayout.LayoutFlags="None"
+ Text="{Binding RemainingTime, Converter={StaticResource TimeConverter}, ConverterParameter={x:Static converters:RemainingTimeType.TimeText}}}"
+ Style="{StaticResource LabelStyle-AT011}" />
+ <!--Volume control View-->
+ <ContentView x:Name="VolumeView" IsVisible="{Binding VolumeViewVisible}">
+ <ContentView.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding DelayTimeCommand}" />
+ </ContentView.GestureRecognizers>
+ <AbsoluteLayout>
+ <AbsoluteLayout.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding DelayTimeCommand}" />
+ </AbsoluteLayout.GestureRecognizers>
+ <BoxView AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All"
+ BackgroundColor="#80000000" />
+ <Grid
+ AbsoluteLayout.LayoutBounds="83,39,194,64"
+ AbsoluteLayout.LayoutFlags="None"
+ VerticalOptions="FillAndExpand">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="*" />
+ </Grid.ColumnDefinitions>
+ <Button
+ x:Name="MinusButton"
+ Grid.Row="0"
+ Grid.Column="0"
+ BackgroundColor="Transparent"
+ Command="{Binding VolumeControlCommand}"
+ CommandParameter="{x:Static vm:VolumeControlType.Minus}"
+ Image="icons/b_slider_icon_minus.png">
+ <Button.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding DelayTimeCommand}" />
+ </Button.GestureRecognizers>
+ <Button.Triggers>
+ <Trigger TargetType="Button" Property="IsEnabled" Value="False">
+ <Setter Property="BackgroundColor" Value="Black" />
+ </Trigger>
+ <Trigger TargetType="Button" Property="IsEnabled" Value="True">
+ <Setter Property="BackgroundColor" Value="Transparent" />
+ </Trigger>
+ <DataTrigger
+ Binding="{Binding CurrentVolume}"
+ TargetType="Button"
+ Value="0">
+ <Setter Property="IsEnabled" Value="False" />
+ </DataTrigger>
+ </Button.Triggers>
+ </Button>
+
+ <!--This Label shows the current volume level.-->
+ <Label x:Name="VolumeLevelLabel"
+ Grid.Row="0"
+ Grid.Column="1"
+ Style="{StaticResource LabelStyle-AT0211}"
+ Text="{Binding CurrentVolume}" />
+
+ <!-- By clicking/tapping this button, volume level will increase -->
+ <Button
+ Grid.Row="0"
+ Grid.Column="2"
+ BackgroundColor="Transparent"
+ Command="{Binding VolumeControlCommand}"
+ CommandParameter="{x:Static vm:VolumeControlType.Plus}"
+ Image="icons/b_slider_icon_plus.png">
+ <Button.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding DelayTimeCommand}" />
+ </Button.GestureRecognizers>
+ <Button.Triggers>
+ <Trigger TargetType="Button" Property="IsEnabled" Value="False">
+ <Setter Property="BackgroundColor" Value="Black" />
+ </Trigger>
+ <Trigger TargetType="Button" Property="IsEnabled" Value="True">
+ <Setter Property="BackgroundColor" Value="Transparent" />
+ </Trigger>
+ <DataTrigger
+ Binding="{Binding CurrentVolume}"
+ TargetType="Button"
+ Value="15">
+ <Setter Property="IsEnabled" Value="False" />
+ </DataTrigger>
+ </Button.Triggers>
+ </Button>
+ </Grid>
+ <!--Volume Image-->
+ <Image AbsoluteLayout.LayoutBounds="105,105,150,150"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="voice_mamo_slider_btn_bg.png" />
+ <Image AbsoluteLayout.LayoutBounds="105,105,150,150"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="{Binding MuteOnOffImage}" />
+ <BoxView AbsoluteLayout.LayoutBounds="105,105,150,150"
+ AbsoluteLayout.LayoutFlags="None">
+ <BoxView.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding DelayTimeCommand}" />
+ <TapGestureRecognizer Command="{Binding MuteControlCommand}" />
+ </BoxView.GestureRecognizers>
+ </BoxView>
+ </AbsoluteLayout>
+ </ContentView>
+ </AbsoluteLayout>
+ </ContentPage.Content>
+</views:CirclePageEx>
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.ViewModels;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// PlayBackPage class
+ /// In this page, recorded content will play.
+ /// </summary>
+ public partial class PlayBackPage : CirclePageEx
+ {
+ PlayBackPageModel viewModel;
+ bool hidePageRequested;
+ public PlayBackPage(PlayBackPageModel _viewModel)
+ {
+ BindingContext = this.viewModel = _viewModel;
+ InitializeComponent();
+ RegisterSomeNotifications();
+ }
+
+ void RegisterSomeNotifications()
+ {
+ // You can get notified whenever playing audio is done.
+ MessagingCenter.Subscribe<PlayBackPageModel, bool>(this, MessageKeys.AudioPlayDone, async (obj, finished) =>
+ {
+ // If playing audio is done, this page will be hidden. (MainPage will be shown.)
+ if (finished)
+ {
+ await Navigation.PopAsync();
+ }
+ });
+ }
+
+ protected override bool OnBackButtonPressed()
+ {
+ if (hidePageRequested)
+ {
+ return true;
+ }
+ else
+ {
+ hidePageRequested = true;
+ }
+
+ if (VolumeView.IsVisible)
+ {
+ Console.WriteLine("[PlaybackPage.OnBackButtonPressed] VolumeView is shown.");
+ viewModel.VolumeViewVisibilityCommand?.Execute(false);
+ return false;
+ }
+
+ Console.WriteLine("[PlaybackPage.OnBackButtonPressed] PlayBackPage will be hidden.");
+ viewModel.Stop();
+ return base.OnBackButtonPressed();
+ }
+
+ /// <summary>
+ /// Invoked immediately prior to the Page becoming visible.
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ Console.WriteLine("[PlaybackPage] OnAppearing() ...");
+ hidePageRequested = false;
+ }
+
+ /// <summary>
+ /// Invoked when this page disappears.
+ /// </summary>
+ protected override void OnDisappearing()
+ {
+ Console.WriteLine("[PlaybackPage] OnDisappearing");
+ viewModel.Stop();
+ }
+
+ public void Dispose()
+ {
+ Console.WriteLine("[PlayBackPageModel] Dispose");
+ MessagingCenter.Unsubscribe<PlayBackPageModel, bool>(this, MessageKeys.AudioPlayDone);
+ ((PlayBackPageModel)BindingContext).Dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.Tizen.Wearable.Renderers;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Views
+{
+ public class ProgressbarPopup : BindableObject
+ {
+ public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(ProgressbarPopup), null);
+
+ public static readonly BindableProperty DurationProperty = BindableProperty.Create(nameof(Duration), typeof(double), typeof(ProgressbarPopup), 1.5);
+
+ public static readonly BindableProperty ProgressbarTextProperty = BindableProperty.Create(nameof(ProgressbarText), typeof(string), typeof(ProgressbarPopup), null);
+
+ public static readonly BindableProperty ProgressbarDurationProperty = BindableProperty.Create(nameof(ProgressbarDuration), typeof(double), typeof(ProgressbarPopup), 0.8);
+
+ IProgressbarPopup _popUp = null;
+ public event EventHandler BackButtonPressed;
+ public event EventHandler TimedOut;
+
+ public ProgressbarPopup()
+ {
+ _popUp = new ProgressbarPopupRenderer();
+ if (_popUp == null)
+ {
+ throw new Exception("Object reference not set to an instance of a Popup.");
+ }
+
+ _popUp.BackButtonPressed += (s, e) =>
+ {
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ };
+
+ _popUp.TimedOut += (s, e) =>
+ {
+ TimedOut?.Invoke(this, EventArgs.Empty);
+ };
+
+ SetBinding(TextProperty, new Binding(nameof(Text), mode: BindingMode.OneWayToSource, source: _popUp));
+ SetBinding(DurationProperty, new Binding(nameof(Duration), mode: BindingMode.OneWayToSource, source: _popUp));
+ SetBinding(ProgressbarTextProperty, new Binding(nameof(ProgressbarText), mode: BindingMode.OneWayToSource, source: _popUp));
+ SetBinding(ProgressbarDurationProperty, new Binding(nameof(ProgressbarDuration), mode: BindingMode.OneWayToSource, source: _popUp));
+ }
+
+ /// <summary>
+ /// Gets or sets text of the Popup.
+ /// </summary>
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets duration of the Popup.
+ /// </summary>
+ public double Duration
+ {
+ get { return (double)GetValue(DurationProperty); }
+ set { SetValue(DurationProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets text of the Popup.
+ /// </summary>
+ public string ProgressbarText
+ {
+ get { return (string)GetValue(ProgressbarTextProperty); }
+ set { SetValue(ProgressbarTextProperty, value); }
+ }
+
+ /// <summary>
+ /// Duration for Progressbar
+ /// How long to display the text message with progressbar
+ /// </summary>
+ double ProgressbarDuration
+ {
+ get { return (double)GetValue(ProgressbarDurationProperty); }
+ set { SetValue(ProgressbarDurationProperty, value); }
+ }
+
+ /// <summary>
+ /// Show Toast Popup
+ /// </summary>
+ public void Show()
+ {
+ _popUp.Show();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<views:CirclePageEx
+ x:Class="VoiceMemo.Views.RecordListPage"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:converters="clr-namespace:VoiceMemo.Converters;"
+ xmlns:views="clr-namespace:VoiceMemo.Views;"
+ xmlns:effect="clr-namespace:VoiceMemo.Effects;"
+ xmlns:resx="clr-namespace:VoiceMemo.Resx;"
+ xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms"
+ x:Name="myRecordListPage"
+ RotaryFocusObject="{x:Reference DataListView}">
+ <views:CirclePageEx.Resources>
+ <ResourceDictionary>
+ <!-- First line size 13 -->
+ <Color x:Key="T0211">#FFFAFAFA</Color>
+ <!-- Second line size 8 -->
+ <Color x:Key="T022">#FFB3B3B3</Color>
+
+ <Color x:Key="AT011">#FFFAFAFA</Color>
+ <Style x:Key="LabelStyle-AT011" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="20" />
+ <Setter Property="TextColor" Value="{StaticResource AT011}" />
+ </Style>
+ <Color x:Key="AT0116">#FF4DCFFF</Color>
+ <Style x:Key="LabelStyle-AT0116" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="TextColor" Value="{StaticResource AT0116}" />
+ </Style>
+ <converters:RecordsCountToViewVisibilityConverter x:Key="ViewVisibilityConverter" />
+ </ResourceDictionary>
+ </views:CirclePageEx.Resources>
+
+ <views:CirclePageEx.Content>
+ <AbsoluteLayout x:Name="RecordListView" BackgroundColor="Black">
+ <Image
+ x:Name="NoImage"
+ AbsoluteLayout.LayoutBounds="0,0,1,1"
+ AbsoluteLayout.LayoutFlags="All"
+ IsVisible="{Binding Records.Count, Converter={StaticResource ViewVisibilityConverter}, ConverterParameter={x:Static converters:ViewType.NoRecordView}}"
+ Source="tw_no_item_bg.png"/>
+ <Label
+ x:Name="recordingsLabel"
+ AbsoluteLayout.LayoutBounds="64,51,232,39"
+ AbsoluteLayout.LayoutFlags="None"
+ IsVisible="{Binding Records.Count, Converter={StaticResource ViewVisibilityConverter}, ConverterParameter={x:Static converters:ViewType.NoRecordView}}"
+ Text="{x:Static resx:AppResources.Recordings}"
+ HorizontalTextAlignment="Center" VerticalTextAlignment="Center"
+ FontSize="10"
+ TextColor="#FF4DCFFF" />
+ <Label
+ x:Name="noRecordingsLabel"
+ AbsoluteLayout.LayoutBounds=".5,.8,360,79"
+ AbsoluteLayout.LayoutFlags="PositionProportional"
+ IsVisible="{Binding Records.Count, Converter={StaticResource ViewVisibilityConverter}, ConverterParameter={x:Static converters:ViewType.NoRecordView}}"
+ HorizontalTextAlignment="Center" VerticalTextAlignment="Center"
+ TextColor="#FFFAFAFA"
+ FontSize="10"
+ Text="{x:Static resx:AppResources.NoRecordings}" />
+ <Image
+ AbsoluteLayout.LayoutBounds=".5, .5, 98, 98"
+ AbsoluteLayout.LayoutFlags="PositionProportional"
+ IsVisible="{Binding Records.Count, Converter={StaticResource ViewVisibilityConverter}, ConverterParameter={x:Static converters:ViewType.NoRecordView}}"
+ Source="b_icon_voicememo.png"/>
+ <w:CircleListView
+ x:Name="DataListView"
+ AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
+ AbsoluteLayout.LayoutFlags="All"
+ IsVisible="{Binding Records.Count, Converter={StaticResource ViewVisibilityConverter}, ConverterParameter={x:Static converters:ViewType.RecordListView}}"
+ Header="Recordings"
+ ItemSelected="OnItemSelected"
+ ItemTapped="OnItemTapped"
+ effect:ItemLongPressEffect.Command="{Binding LongClickCommand}"
+ ItemsSource="{Binding Records}">
+ <w:CircleListView.Effects>
+ <effect:ItemLongPressEffect />
+ </w:CircleListView.Effects>
+ <ListView.Footer>
+ <StackLayout HeightRequest="115">
+ <BoxView />
+ </StackLayout>
+ </ListView.Footer>
+ <ListView.Header>
+ <AbsoluteLayout HeightRequest="115" BackgroundColor="Black">
+ <Label
+ x:Name="headerLabel"
+ AbsoluteLayout.LayoutBounds="0.5, 0.5, 1, 0.5"
+ AbsoluteLayout.LayoutFlags="All"
+ Text="{x:Static resx:AppResources.Recordings}"
+ Style="{StaticResource LabelStyle-AT0116}"
+ TextColor="#FF00FFF0"
+ FontSize="14" />
+ </AbsoluteLayout>
+ </ListView.Header>
+ <ListView.ItemTemplate>
+ <DataTemplate>
+ <ViewCell>
+ <RelativeLayout HeightRequest="130" WidthRequest="360">
+ <RelativeLayout.Resources>
+ <ResourceDictionary>
+ <converters:DurationToRemainingTimeConverter x:Key="TimeConverter" />
+ <converters:SttToPropertyConverter x:Key="SttConverter" />
+ <Style TargetType="Label">
+ <Setter Property="HorizontalTextAlignment" Value="Center" />
+ <Setter Property="VerticalTextAlignment" Value="Center" />
+ <Setter Property="WidthRequest" Value="200" />
+ <Setter Property="TextColor" Value="White" />
+ <!--<Setter Property="BackgroundColor" Value="Pink" />-->
+ <Setter Property="RelativeLayout.XConstraint"
+ Value="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=30}" />
+ </Style>
+ </ResourceDictionary>
+ </RelativeLayout.Resources>
+ <Label
+ Text="{Binding Title}"
+ HeightRequest="47"
+ WidthRequest="230"
+ TextColor="{StaticResource T0211}"
+ FontSize="13"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0,
+ Constant=9}" />
+ <Label
+ Text="{Binding Date}"
+ HeightRequest="32"
+ WidthRequest="230"
+ TextColor="{StaticResource T022}"
+ FontSize="8"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0,
+ Constant=56}" />
+ <Label
+ Text="{Binding Duration, Converter={StaticResource TimeConverter}, ConverterParameter={x:Static converters:RemainingTimeType.TimeText}}}"
+ HeightRequest="32"
+ WidthRequest="230"
+ TextColor="{StaticResource T022}"
+ FontSize="8"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0,
+ Constant=90}" />
+ <Image Source="{Binding SttEnabled, Converter={StaticResource SttConverter}, ConverterParameter={x:Static converters:SttPropertyType.RecordImageSource}}"
+ WidthRequest="76"
+ HeightRequest="76"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Width,
+ Factor=0,
+ Constant=268}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0,
+ Constant=27}" />
+ <w:Check DisplayStyle="Default"
+ IsToggled="{Binding Checked, Mode=TwoWay}"
+ IsEnabled="{Binding BindingContext.IsCheckable, Source={x:Reference myRecordListPage}}"
+ WidthRequest="76"
+ HeightRequest="76"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Width,
+ Factor=0,
+ Constant=150}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0,
+ Constant=30}"
+ effect:TizenStyleEffect.Style="genlist/select_mode"
+ effect:TizenEventPropagationEffect.EnablePropagation="True">
+ <w:Check.Effects>
+ <effect:TizenStyleEffect/>
+ <effect:TizenEventPropagationEffect/>
+ </w:Check.Effects>
+ </w:Check>
+ </RelativeLayout>
+ </ViewCell>
+ </DataTemplate>
+ </ListView.ItemTemplate>
+ </w:CircleListView>
+ <Button x:Name="CheckedCounter"
+ IsVisible="{Binding IsCheckable}"
+ Text="{Binding CheckedNamesCount}"
+ AbsoluteLayout.LayoutBounds="0.5, 10, 100, 80"
+ AbsoluteLayout.LayoutFlags="XProportional"
+ Command="{Binding SelectModeActionButtonPressedCommand}">
+ <Button.Behaviors>
+ <w:ContextPopupEffectBehavior x:Name="CheckedCounterBehavior"
+ PositionOption="BottomOfView" />
+ </Button.Behaviors>
+ </Button>
+ </AbsoluteLayout>
+ </views:CirclePageEx.Content>
+ <w:CirclePage.ActionButton>
+ <w:ActionButtonItem
+ x:Name="deleteActionButton"
+ IsVisible="{Binding IsCheckable}"
+ IsEnable="{Binding CheckedNamesCount, Converter={StaticResource ViewVisibilityConverter}, ConverterParameter={x:Static converters:ViewType.RecordCheckView}}"
+ Clicked="DeleteActionButtonClicked"
+ Command="{Binding DeleteRecordsCommand}"
+ Text="{x:Static resx:AppResources.DELETE}" />
+ </w:CirclePage.ActionButton>
+ <w:CirclePage.ToolbarItems>
+ <views:BindableToolbarItem
+ x:Name="toolbarItem"
+ IsVisible="{Binding Records.Count, Converter={StaticResource ViewVisibilityConverter}, ConverterParameter={x:Static converters:ViewType.RecordListView}}"
+ Command="{Binding ChangeToDeleteModeCommand}"
+ Icon="more_option_icon_delete.png"
+ Text="{x:Static resx:AppResources.DELETE}" />
+ </w:CirclePage.ToolbarItems>
+</views:CirclePageEx>
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.Effects;
+using VoiceMemo.Models;
+using VoiceMemo.Resx;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+using Tizen.Wearable.CircularUI.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// RecordListPage class
+ /// It shows the list of recordings
+ /// </summary>
+ public partial class RecordListPage : CirclePageEx
+ {
+ MainPageModel ViewModel;
+ public RecordListPage(MainPageModel viewModel)
+ {
+ BindingContext = ViewModel = viewModel;
+ // Subscribe notification of locale changes to update text based on locale
+ MessagingCenter.Subscribe<App>(this, MessageKeys.UpdateByLanguageChange, (obj) =>
+ {
+ UpdateUI();
+ });
+ InitializeComponent();
+ ImageAttributes.SetBlendColor(NoImage, Color.FromRgba(94, 94, 94, 77));
+ // Binding for ContextPopupEffectBehavior's properties
+ // BindingContext is not inherited by Behavior and Effect.
+ // Source of Binding should be specified explicitly.
+ CheckedCounterBehavior.SetBinding(ContextPopupEffectBehavior.AcceptTextProperty, new Binding("SelectOptionMessage1", BindingMode.OneWay, source: BindingContext));
+ CheckedCounterBehavior.SetBinding(ContextPopupEffectBehavior.AcceptCommandProperty, new Binding("SelectCommand1", BindingMode.OneWay, source: BindingContext));
+ CheckedCounterBehavior.SetBinding(ContextPopupEffectBehavior.CancelTextProperty, new Binding("SelectOptionMessage2", BindingMode.OneWay, source: BindingContext));
+ CheckedCounterBehavior.SetBinding(ContextPopupEffectBehavior.CancelCommandProperty, new Binding("SelectCommand2", BindingMode.OneWay, source: BindingContext));
+ CheckedCounterBehavior.SetBinding(ContextPopupEffectBehavior.VisibilityProperty, new Binding("PopupVisibility", BindingMode.TwoWay, source: BindingContext));
+ }
+
+ void UpdateUI()
+ {
+ // Update text that has been translated into the current language.
+ noRecordingsLabel.Text = AppResources.NoRecordings;
+ recordingsLabel.Text = AppResources.Recordings;
+ headerLabel.Text = AppResources.Recordings;
+ deleteActionButton.Text = AppResources.DELETE;
+ toolbarItem.Text = AppResources.DELETE;
+ }
+
+ /// <summary>
+ /// Invoked immediately prior to the Page becoming visible.
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ Console.WriteLine("[RecordListPage] OnAppearing() ...");
+ foreach (var record in ViewModel.Records)
+ {
+ record.Checked = false;
+ }
+ }
+
+ public void Rotate(RotaryEventArgs args)
+ {
+ Console.WriteLine("[RecordListPage] Rotate() IsClockwise ? " + args.IsClockwise);
+ //DataListView.ScrollTo();
+ }
+
+ /// <summary>
+ /// Invoked when this page disappears.
+ /// </summary>
+ protected override void OnDisappearing()
+ {
+ Console.WriteLine("[RecordListPage] OnDisappearing");
+ ViewModel.IsCheckable = false;
+ }
+
+ /*async*/
+ void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
+ {
+ var item = args.SelectedItem as Record;
+ if (item == null)
+ {
+ return;
+ }
+ Console.WriteLine(" RecordListPage.OnItemSelected : " + args.SelectedItem/* + " , index: " + args.SelectedItemIndex*/);
+ //await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(item)));
+
+ // Manually deselect item
+ DataListView.SelectedItem = null;
+ }
+
+ async void OnItemTapped(object sender, ItemTappedEventArgs args)
+ {
+ if (((MainPageModel)BindingContext).IsCheckable)
+ {
+ Console.WriteLine(" RecordListPage.OnItemTapped : Checkable mode so ignore tapped event");
+ return;
+ }
+
+ Console.WriteLine(" RecordListPage.OnItemTapped : " + args.Item);
+ await Navigation.PushAsync(PageFactory.GetInstance(Pages.Details, args.Item));
+ }
+
+ void OnCircleToolbarItemClicked_DeleteRecord(object sender, EventArgs args)
+ {
+ Console.WriteLine("[RecordListPage.OnCircleToolbarItemClicked_DeleteRecord] sender : " + sender);
+
+ //MessagingCenter.Send<Page, Record>(this, MessageKeys.DeleteVoiceMemo, ((DetailsPageModel)viewModel).Record);
+ //await Navigation.PopToRootAsync();
+
+ //MessagingCenter.Send<Page, Record>(this, MessageKeys.DeleteVoiceMemo, ((DetailsPageModel)viewModel).Record);
+ //await Navigation.PushAsync(PageFactory.GetInstance(Pages.PlayBack, viewModel.Record/*new PlayBackPageModel(viewModel.Record)*/));
+ }
+/*
+ async void DeleteActionButtonClicked(object sender, EventArgs args)
+ {
+ Console.WriteLine("[RecordListPage.DeleteActionButtonClicked] sender : " + sender + " CheckedNamesCount : " + ViewModel.CheckedNamesCount);
+ //if (ViewModel.CheckedNamesCount == 1)
+ //{
+ // await DisplayAlert("Delete", "Deleted..", "OK");
+ // //GraphicPopUp popup = new GraphicPopUp
+ // //{
+ // // Text = AppResources.Deleted,
+ // //};
+ // //popup.TimedOut += Popup_TimedOut;
+ // //popup.Show();
+ //}
+ //else
+ //{
+ // ProgressbarPopup popup = new ProgressbarPopup
+ // {
+ // Text = AppResources.Deleted,
+ // ProgressbarText = AppResources.Deleting,
+ // };
+ // popup.TimedOut += Popup_TimedOut;
+ // popup.Show();
+ //}
+ await DisplayAlert("Delete", "Deleted..", "OK");
+ }
+*/
+
+ void DeleteActionButtonClicked(object sender, EventArgs args)
+ {
+ Console.WriteLine("[RecordListPage.DeleteActionButtonClicked] sender : " + sender + " CheckedNamesCount : " + ViewModel.CheckedNamesCount);
+ if (ViewModel.CheckedNamesCount == 1)
+ {
+ GraphicPopUp popup = new GraphicPopUp
+ {
+ Text = AppResources.Deleted,
+ };
+ popup.TimedOut += Popup_TimedOut;
+ popup.Show();
+ }
+ else
+ {
+ ProgressbarPopup popup = new ProgressbarPopup
+ {
+ Text = AppResources.Deleted,
+ ProgressbarText = AppResources.Deleting,
+ };
+ popup.TimedOut += Popup_TimedOut;
+ popup.Show();
+ }
+ }
+
+ private void Popup_TimedOut(object sender, EventArgs e)
+ {
+ Console.WriteLine("[RecordListPage.Popup_TimedOut] sender : " + sender);
+ //await Navigation.PopToRootAsync();
+ }
+
+ protected override bool OnBackButtonPressed()
+ {
+ if (ViewModel.IsCheckable)
+ {
+ Console.WriteLine("[RecordListPage.OnBackButtonPressed] IsCheckable = true");
+ ViewModel.IsCheckable = false;
+ return true;
+ }
+ else
+ {
+ Console.WriteLine("[RecordListPage.OnBackButtonPressed] IsCheckable = false");
+ return false;
+ }
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<views:CirclePageEx
+ x:Class="VoiceMemo.Views.RecordingPage"
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:views="clr-namespace:VoiceMemo.Views;"
+ xmlns:vm="clr-namespace:VoiceMemo.ViewModels;"
+ xmlns:converter="clr-namespace:VoiceMemo.Converters"
+ xmlns:w="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=XSF.CircularUI.Forms"
+ Title="{Binding Title}">
+ <views:CirclePageEx.Resources>
+ <ResourceDictionary>
+ <!-- record_controller_bg.png -->
+ <Color x:Key="AO012L1">#FFFAFAFA</Color>
+ <Color x:Key="AO012L1P">#66FAFAFA</Color>
+ <Color x:Key="AO012L1D">#66FAFAFA</Color>
+ <!-- recording_icon_cancel(_pause).png -->
+ <Color x:Key="AO012L3">#FF4F4F4F</Color>
+ <Color x:Key="AO012L3P">#64F4F4F</Color>
+ <Color x:Key="AO012L3D">#664F4F4F</Color>
+ <!-- record_stop_icon.png -->
+ <Color x:Key="AO016L1">#FFE00404</Color>
+ <Color x:Key="AO016L1P">#66E00404</Color>
+ <Color x:Key="AO016L1D">#66E00404</Color>
+ <!-- recording_stt_icon.png -->
+ <Color x:Key="AO0210">#FFFAFAFA</Color>
+ <Color x:Key="AT011">#FFFAFAFA</Color>
+ <Color x:Key="AO028">#8C11B4FF</Color>
+ <!-- voicerecorder_btn_stop(_pause, _play).png -->
+ <Color x:Key="AO014L3">#FFFAFAFA</Color>
+ <Color x:Key="AO014L3P">#66FAFAFA</Color>
+ <Color x:Key="AO014L3D">#66FAFAFA</Color>
+ <!-- voicerecorder_btn_bg.png -->
+ <Color x:Key="AO014L1">#FF12B4FF</Color>
+ <!-- FF467EA6 -->
+ <Color x:Key="AO014L1P">#6612B4FF</Color>
+ <Color x:Key="AO014L1D">#6612B4FF</Color>
+
+ <Style x:Key="LabelStyle-AT011" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="20" />
+ <Setter Property="TextColor" Value="{StaticResource AT011}" />
+ </Style>
+ <Color x:Key="AT0116">#FF4DCFFF</Color>
+ <Style x:Key="LabelStyle-AT0116" TargetType="Label" BasedOn="{StaticResource LabelStyle-Base}">
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="TextColor" Value="{StaticResource AT0116}" />
+ </Style>
+ <converter:RecordImageSourceColorConverter x:Key="RecordImageSourceColorConverter" />
+ </ResourceDictionary>
+ </views:CirclePageEx.Resources>
+ <w:CirclePage.CircleSurfaceItems>
+ <w:CircleProgressBarSurfaceItem
+ x:Name="RecordingProgress"
+ Value="{Binding RecordingProcess}" />
+ </w:CirclePage.CircleSurfaceItems>
+ <ContentPage.Content>
+ <AbsoluteLayout BackgroundColor="Black">
+ <!-- Recorder -->
+ <Image
+ x:Name="VoiceRecorderBtnEffectImage"
+ AbsoluteLayout.LayoutBounds="117,117,126,126"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="voicememo_effect.png" />
+ <Image
+ x:Name="VoiceRecorderBtnEffectImage2"
+ AbsoluteLayout.LayoutBounds="117,117,126,126"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="voicememo_effect.png" />
+ <Image
+ x:Name="VoiceRecorderBtnBgImage"
+ AbsoluteLayout.LayoutBounds="117,117,126,126"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="voicerecorder_btn_bg.png">
+ </Image>
+ <Image
+ x:Name="VoiceRecorderBtnStopImage"
+ AbsoluteLayout.LayoutBounds="117,117,126,126"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="voicerecorder_btn_stop.png">
+ </Image>
+ <BoxView
+ AbsoluteLayout.LayoutBounds="117,117,126,126"
+ AbsoluteLayout.LayoutFlags="None"
+ x:Name="TapLayer">
+ <BoxView.GestureRecognizers>
+ <TapGestureRecognizer Tapped="CompleteRecording_Tapped" />
+ </BoxView.GestureRecognizers>
+ </BoxView>
+
+ <Image
+ x:Name="RecordingSttImage"
+ AbsoluteLayout.LayoutBounds="162,18,36,36"
+ AbsoluteLayout.LayoutFlags="None"
+ IsVisible="{Binding SttOn}"
+ Source="recording_stt_icon.png" />
+
+ <!-- Record Controller in Left -->
+ <Image
+ x:Name="RecordControllerCancelBgImage"
+ AbsoluteLayout.LayoutBounds="30,139,82,82"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="record_controller_bg.png" />
+ <Image
+ x:Name="RecordingIconCancelImage"
+ AbsoluteLayout.LayoutBounds="30,139,82,82"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="recording_icon_cancel.png">
+ <Image.GestureRecognizers>
+ <TapGestureRecognizer Tapped="TryToCancel_Tapped" />
+ <!--<TapGestureRecognizer Command="{Binding TryToCancelCommand}"/>-->
+ </Image.GestureRecognizers>
+ </Image>
+
+ <!-- Record Controller in Right -->
+ <Image
+ x:Name="RecordControllerPauseBgImage"
+ AbsoluteLayout.LayoutBounds="248,139,82,82"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="record_controller_bg.png">
+ <Image.GestureRecognizers>
+ <TapGestureRecognizer Tapped="PauseRecording_Tapped" />
+ </Image.GestureRecognizers>
+ <!--<Image.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding RequestCommand}" CommandParameter="{x:Static vm:RecordingCommandType.PAUSE}"/>
+ </Image.GestureRecognizers>-->
+ </Image>
+ <Image
+ x:Name="RecordingIconPauseImage"
+ AbsoluteLayout.LayoutBounds="248,139,82,82"
+ AbsoluteLayout.LayoutFlags="None"
+ Source="{Binding PauseRecordToggleImage, Converter={StaticResource RecordImageSourceColorConverter}, ConverterParameter={x:Reference RecordingIconPauseImage}}">
+ <Image.GestureRecognizers>
+ <TapGestureRecognizer Tapped="PauseRecording_Tapped" />
+ </Image.GestureRecognizers>
+ <!--<Image.GestureRecognizers>
+ <TapGestureRecognizer Command="{Binding RequestCommand}" CommandParameter="{x:Static vm:RecordingCommandType.PAUSE}"/>
+ </Image.GestureRecognizers>-->
+ </Image>
+ <Label
+ x:Name="TitleLabel"
+ AbsoluteLayout.LayoutBounds="64,51,232,39"
+ AbsoluteLayout.LayoutFlags="None"
+ Text="{Binding RecordTitle}"
+ Style="{StaticResource LabelStyle-AT0116}" />
+ <Label
+ x:Name="RecordingTimeLabel"
+ AbsoluteLayout.LayoutBounds="102,249,156,79"
+ AbsoluteLayout.LayoutFlags="None"
+ Text="{Binding RecordingTime}"
+ Style="{StaticResource LabelStyle-AT011}" />
+ </AbsoluteLayout>
+ </ContentPage.Content>
+</views:CirclePageEx>
\ No newline at end of file
--- /dev/null
+/*
+ * 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.Threading.Tasks;
+using VoiceMemo.Effects;
+using VoiceMemo.Models;
+using VoiceMemo.Services;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// RecordingPage class
+ /// It provides voice recording, pausing, and canceling.
+ /// </summary>
+ public partial class RecordingPage : CirclePageEx
+ {
+ public RecordingPageModel ViewModel;
+ double prevScale;
+ double prevShadowScale;
+ double prevAudioLevel;
+ bool currentOpaque = true;
+ double opacity;
+
+ public static readonly BindableProperty RecordingEffectAnimationProperty = BindableProperty.Create(nameof(RecordingEffectAnimation), typeof(bool), typeof(RecordingPage), false, propertyChanged: OnAnimationPropertyChanged);
+
+ private static void OnAnimationPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if ((bool)oldValue == false && (bool)newValue == true)
+ {
+ ((RecordingPage)(bindable)).StartVolumeEffectAnimation();
+ }
+ }
+
+ public static readonly BindableProperty TimeFlickeringAnimationProperty = BindableProperty.Create(nameof(TimeFlickeringAnimation), typeof(bool), typeof(RecordingPage), false, propertyChanged: OnTimeFlickeringPropertyChanged);
+
+ private static void OnTimeFlickeringPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if ((bool)oldValue == false && (bool)newValue == true)
+ {
+ ((RecordingPage)(bindable)).Blink();
+ }
+ }
+
+ public bool RecordingEffectAnimation
+ {
+ get { return (bool)GetValue(RecordingEffectAnimationProperty); }
+ set
+ {
+ SetValue(RecordingEffectAnimationProperty, value);
+ }
+ }
+
+ public bool TimeFlickeringAnimation
+ {
+ get { return (bool)GetValue(TimeFlickeringAnimationProperty); }
+ set
+ {
+ SetValue(TimeFlickeringAnimationProperty, value);
+ }
+ }
+
+ public RecordingPage(RecordingPageModel viewModel)
+ {
+ BindingContext = ViewModel = viewModel;
+ InitializeComponent();
+ UpdateColor();
+
+ RegisterForEvents();
+
+ RecordingEffectAnimation = false;
+ TimeFlickeringAnimation = false;
+
+ SetBinding(RecordingEffectAnimationProperty, new Binding("RecordingEffectOn", mode: BindingMode.Default));
+ SetBinding(TimeFlickeringAnimationProperty, new Binding("TimeFlickeringOn", mode: BindingMode.Default));
+
+ Init();
+ }
+
+ void RegisterForEvents()
+ {
+ //// Get notified when AudioRecordService's State is Recording
+ //MessagingCenter.Subscribe<IAudioRecordService>(this, MessageKeys.ReadyToRecord, async (obj) =>
+ //{
+ // Console.WriteLine("### MessagingCenter ## [RecordingPage] ReadyToRecord!! ");
+ //});
+
+ MessagingCenter.Subscribe<RecordingPageModel, Record>(this, MessageKeys.SaveVoiceMemo, async (obj, item) =>
+ {
+ var _DeviceInfoService = DeviceInformationService.Instance;
+ if (_DeviceInfoService.AppState == AppState.Terminated)
+ {
+ //Toast.DisplayText("Memo " + item.ID + " saved.", 3000);
+ // TODO: show popup using app control
+ return;
+ }
+
+ await Navigation.PushAsync(PageFactory.GetInstance(Pages.Details, item/*new DetailsPageModel(item)*/));
+
+ if (_DeviceInfoService.AppState == AppState.Background)
+ {
+ Console.WriteLine(" ************ TO DO SOMETHING *************");
+ }
+ });
+
+ // Called when an error occurs while recording
+ MessagingCenter.Subscribe<MediaContentService, Exception>(this, MessageKeys.ErrorOccur, (obj, exception) =>
+ {
+ DisplayAlert("Recording", exception.Message, "OK");
+ });
+
+ MessagingCenter.Subscribe<RecordingPageModel>(this, MessageKeys.ForcePopRecordingPage, async (obj) =>
+ {
+ Console.WriteLine("### MessagingCenter ## [RecordingPage] just received ForcePopRecordingPage");
+ await Navigation.PopAsync();
+ });
+ }
+
+ /// <summary>
+ /// Initialize Recording page
+ /// </summary>
+ public void Init()
+ {
+ RecordingTimeLabel.TextColor = Color.White;
+ ImageAttributes.SetBlendColor(RecordingIconPauseImage, (Color)Resources["AO012L3"]);
+ ((RecordingPageModel)ViewModel).Init();
+ }
+
+ /// <summary>
+ /// Start volume effect animation
+ /// </summary>
+ void StartVolumeEffectAnimation()
+ {
+ // Any background code that needs to update the user interface
+ // interact with UI elements
+ // call CheckVolumeAndResizeAnimationAsync method every 100ms
+ Device.StartTimer(TimeSpan.FromMilliseconds(100), CheckVolumeAndResizeAnimationAsync);
+ }
+
+ // Draw animation effects depending on volume level
+ private bool CheckVolumeAndResizeAnimationAsync()
+ {
+ RecordingPageModel recordingViewModel = (RecordingPageModel)BindingContext;
+ // @20180217-vincent: async chain can be stopped by using Task.Factory.StartNew(...)
+ // Device.InovkeOnMainThread was removed because it is guaranteed main thread
+ Task.Factory.StartNew(async () =>
+ {
+ if (recordingViewModel.AudioRecordingService.State == AudioRecordState.Recording)
+ {
+ double fScaleValue = 0, fShodowScaleValue = 0;
+ double volume = recordingViewModel.AudioRecordingService.GetRecordingLevel(); //-300 ~ 0
+ int uwLevel = 0;
+ int uwShadowLevel = 0;
+ int effectLevel = 0;
+ double db = volume;
+ const int VOLUME_LEVEL_MIN = -55;
+ const int VOLUME_LEVEL_MAX = -10;
+ const double LINEAR_VI_SCALE = 1.0 / 20.0;
+
+ if (db < VOLUME_LEVEL_MIN)
+ {
+ effectLevel = 0;
+ }
+ else if (db > VOLUME_LEVEL_MAX)
+ {
+ effectLevel = VOLUME_LEVEL_MAX - VOLUME_LEVEL_MIN;
+ }
+ else
+ {
+ effectLevel = (int)(db - VOLUME_LEVEL_MIN);
+ }
+
+ uwLevel = effectLevel;
+
+ if (uwLevel > 1)
+ {
+ uwShadowLevel = uwLevel / 4 + (new Random().Next() % (uwLevel / 2));
+ }
+
+ if (uwLevel > prevAudioLevel)
+ {
+ fScaleValue = (30 + 70 * (uwLevel) / (VOLUME_LEVEL_MAX - VOLUME_LEVEL_MIN)) / 100.0;
+ fShodowScaleValue = (30 + 70 * (uwShadowLevel) / (VOLUME_LEVEL_MAX - VOLUME_LEVEL_MIN)) / 100.0;
+
+ if (fScaleValue < prevScale)
+ {
+ fScaleValue = prevScale - LINEAR_VI_SCALE;
+ }
+
+ if (fShodowScaleValue < prevShadowScale)
+ {
+ fShodowScaleValue = prevShadowScale - LINEAR_VI_SCALE;
+ }
+ }
+ else
+ {
+ fScaleValue = prevScale - LINEAR_VI_SCALE;
+ fShodowScaleValue = prevShadowScale - LINEAR_VI_SCALE;
+ }
+
+ if (fScaleValue < 0.3)
+ {
+ fScaleValue = 0.3;
+ }
+ else if (fScaleValue > 1.0)
+ {
+ fScaleValue = 1.0;
+ }
+
+ if (fShodowScaleValue < 0.3)
+ {
+ fShodowScaleValue = 0.3;
+ }
+ else if (fShodowScaleValue > 1.0)
+ {
+ fShodowScaleValue = 1.0;
+ }
+
+ int actualSize = (int)(fScaleValue * 360);
+ int acutalShadowSize = (int)(fShodowScaleValue * 360);
+
+ await DrawVolumeEffectAnimationAsync(actualSize, acutalShadowSize);
+
+ prevAudioLevel = uwLevel;
+ prevScale = fScaleValue;
+ prevShadowScale = fShodowScaleValue;
+ }
+ });
+ return ((RecordingPageModel)BindingContext).RecordingEffectOn;
+ }
+
+ // Draw animation depending on volume level while recording
+ private async Task DrawVolumeEffectAnimationAsync(int imageSize, int shadowImageSize)
+ {
+ double image2Ratio = (double)imageSize / 126;
+ double imageRatio = (double)shadowImageSize / 126;
+ await Task.WhenAll(
+ VoiceRecorderBtnEffectImage2.ScaleTo(image2Ratio,50),
+ VoiceRecorderBtnEffectImage.ScaleTo(imageRatio,50));
+ }
+
+ /// <summary>
+ /// Invoked immediately prior to the Page becoming visible.
+ /// </summary>
+ protected override void OnAppearing()
+ {
+ }
+
+ /// <summary>
+ /// Invoked when this page disappears.
+ /// </summary>
+ protected override void OnDisappearing()
+ {
+ }
+
+ /// <summary>
+ /// Invoked when backbutton is pressed
+ /// As a result, CancelPage will be shown.
+ /// </summary>
+ /// <returns>bool</returns>
+ protected override bool OnBackButtonPressed()
+ {
+ TryToCancel_Tapped(this, new EventArgs());
+ return true;
+ }
+
+ // When it is no need to receive notifications anymore, unsubscribes subscribers from the specified messages
+ void UnregisterForEvents()
+ {
+ //MessagingCenter.Unsubscribe<IAudioRecordService>(this, MessageKeys.ReadyToRecord);
+ MessagingCenter.Unsubscribe<RecordingPageModel, Record>(this, MessageKeys.SaveVoiceMemo);
+ MessagingCenter.Unsubscribe<MediaContentService, Exception>(this, MessageKeys.ErrorOccur);
+ MessagingCenter.Unsubscribe<RecordingPageModel>(this, MessageKeys.ForcePopRecordingPage);
+ }
+
+ /// <summary>
+ /// Dispose this page
+ /// </summary>
+ public void Dispose()
+ {
+ UnregisterForEvents();
+ ((RecordingPageModel)BindingContext).Dispose();
+ }
+
+ /// <summary>
+ /// Invoked when RecordingIconCancelImage is tapped
+ /// Recording is temporarily paused.
+ /// </summary>
+ /// <param name="sender">sender object</param>
+ /// <param name="e">EventArgs</param>
+ async void TryToCancel_Tapped(object sender, EventArgs e)
+ {
+ if (ViewModel.RecordingViewModelState == RecordingViewModelState.Recording)
+ {
+ ViewModel.RequestCommand.Execute(RecordingCommandType.PauseForCancelRequest);
+ }
+
+ await Navigation.PushAsync(PageFactory.GetInstance(Pages.TryCancel, ViewModel));
+ }
+
+ /// <summary>
+ /// Invoked when RecordControllerPauseBgImage or RecordingIconPauseImage is tapped
+ /// Recording is temporarily paused.
+ /// paused and recording states are toggled every time this image button is pressed
+ /// </summary>
+ /// <param name="sender">sender object</param>
+ /// <param name="e">EventArgs</param>
+ void PauseRecording_Tapped(object sender, EventArgs e)
+ {
+ if (((RecordingPageModel)BindingContext).RecordingViewModelState == RecordingViewModelState.Paused)
+ {
+ ViewModel.RequestCommand.Execute(RecordingCommandType.Record);
+ }
+ else if (((RecordingPageModel)BindingContext).RecordingViewModelState == RecordingViewModelState.Recording)
+ {
+ ViewModel.RequestCommand.Execute(RecordingCommandType.Pause);
+ }
+ }
+
+ private void Blink()
+ {
+ currentOpaque = true;
+ Device.StartTimer(TimeSpan.FromMilliseconds(700), BlinkInvoker);
+ }
+
+ private bool BlinkInvoker()
+ {
+ Task.Factory.StartNew(async () =>
+ {
+ opacity = currentOpaque ? 0 : 1;
+ await BlinkAnimation();
+ currentOpaque = !currentOpaque;
+ });
+
+ return ((RecordingPageModel)BindingContext).TimeFlickeringOn;
+ }
+
+ private async Task BlinkAnimation()
+ {
+ if (!((RecordingPageModel)BindingContext).TimeFlickeringOn)
+ {
+ RecordingTimeLabel.Opacity = 1.0;
+ }
+ else
+ {
+ await Task.WhenAny(RecordingTimeLabel.FadeTo(opacity, 500, Easing.Linear));
+ }
+ }
+
+ /// <summary>
+ /// Invoked when VoiceRecorderBtnBgImage and VoiceRecorderBtnStopImage is tapped
+ /// Audio recording will be done
+ /// </summary>
+ /// <param name="sender">sender object</param>
+ /// <param name="args">EventArgs</param>
+ void CompleteRecording_Tapped(object sender, EventArgs args)
+ {
+ Console.WriteLine("RecordingPage ============ CompleteRecording_Tapped");
+ ViewModel.RequestCommand.Execute(RecordingCommandType.Stop);
+ }
+
+ void UpdateColor()
+ {
+ // For case that STT is On, recording_stt_icon.png
+ ImageAttributes.SetBlendColor(RecordingSttImage, (Color)Resources["AO0210"]);
+
+ // Recorder
+ ImageAttributes.SetBlendColor(VoiceRecorderBtnEffectImage, (Color)Resources["AO028"]);
+ ImageAttributes.SetBlendColor(VoiceRecorderBtnEffectImage2, (Color)Resources["AO028"]);
+ ImageAttributes.SetBlendColor(VoiceRecorderBtnBgImage, (Color)Resources["AO014L1"]);
+ ImageAttributes.SetBlendColor(VoiceRecorderBtnStopImage, (Color)Resources["AO014L3"]);
+
+ // Record Controller in Left
+ ImageAttributes.SetBlendColor(RecordControllerCancelBgImage, (Color)Resources["AO012L1"]);
+ ImageAttributes.SetBlendColor(RecordingIconCancelImage, (Color)Resources["AO012L3"]);
+
+ // Record Controller in Right
+ ImageAttributes.SetBlendColor(RecordControllerPauseBgImage, (Color)Resources["AO012L1"]);
+ ImageAttributes.SetBlendColor(RecordingIconPauseImage, (Color)Resources["AO012L3"]);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<ViewCell
+ x:Class="VoiceMemo.Views.SttLanguageViewCell"
+ 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">
+ <ViewCell.View>
+ <RelativeLayout HeightRequest="130" WidthRequest="360">
+ <Label
+ FontAttributes="Bold"
+ HorizontalTextAlignment="Center"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Width,
+ Factor=0,
+ Constant=30}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0,
+ Constant=15}"
+ Style="{StaticResource BaseLabelStyle}"
+ Text="{Binding Name}"
+ FontSize="11"
+ VerticalTextAlignment="Center"
+ WidthRequest="220" />
+ <Label
+ FontAttributes="None"
+ HorizontalTextAlignment="Center"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Width,
+ Factor=0,
+ Constant=30}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0,
+ Constant=80}"
+ Style="{StaticResource BaseLabelStyle}"
+ Text="{Binding Country}"
+ FontSize="7"
+ VerticalTextAlignment="Center"
+ WidthRequest="220" />
+ <w:Radio
+ x:Name="onOffSwitch"
+ GroupName="Language"
+ RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Width,
+ Factor=0.7,
+ Constant=0}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
+ Property=Height,
+ Factor=0.15,
+ Constant=0}"
+ IsSelected="{Binding IsOn, Mode=TwoWay}"
+ Selected="OnSelected"
+ Value="{Binding Combo}"/>
+ </RelativeLayout>
+ </ViewCell.View>
+</ViewCell>
\ No newline at end of file
--- /dev/null
+/*
+ * 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 VoiceMemo.Models;
+using VoiceMemo.ViewModels;
+using Xamarin.Forms;
+using Tizen.Wearable.CircularUI.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace VoiceMemo.Views
+{
+ /// <summary>
+ /// SttLanguageViewCell
+ /// It's a custom view cell for CircleListView in LanguageSelectionPage
+ /// </summary>
+ public partial class SttLanguageViewCell : ViewCell
+ {
+ public SttLanguageViewCell()
+ {
+ InitializeComponent();
+ }
+
+ // Called when radio button is selected
+ public async void OnSelected(object sender, SelectedEventArgs args)
+ {
+ Console.WriteLine($"Radio.OnSoundSelected!! value:{args.Value}");
+ Radio radio = sender as Radio;
+ if (Application.Current.MainPage.Navigation.NavigationStack.Count > 0)
+ {
+ int index = Application.Current.MainPage.Navigation.NavigationStack.Count - 1;
+
+ LanguageSelectionPage currPage = Application.Current.MainPage.Navigation.NavigationStack[index] as LanguageSelectionPage;
+ //Error CS0023 Operator '!' cannot be applied to operand of type 'string'
+ if (args.Value != null)
+ {
+ Console.WriteLine(" < UNSELECTED > XXXXXX sender " + sender + " - " + radio.Value);
+ currPage.ignoreRadioSelection = false;
+ return;
+ }
+ else
+ {
+ if (currPage.ignoreRadioSelection)
+ {
+ Console.WriteLine("\n\nRadio_Selected >>>>>>> SKIP!\n\n");
+ currPage.ignoreRadioSelection = false;
+ return;
+ }
+
+ Console.WriteLine(" < SELECTED > XXXXXX sender " + sender + " - " + radio.Value);
+ SttLanguage item = radio.BindingContext as SttLanguage;
+ ((MainPageModel)currPage.BindingContext).CurrentLanguage = item.Lang;
+
+ ((MainPageModel)currPage.BindingContext).SelectedItemIndex = item;
+ await currPage.Navigation.PopToRootAsync();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace VoiceMemo.Views
+{
+ public static class ViewExtensions
+ {
+ public static void CancelAnimation(this VisualElement self)
+ {
+ self.AbortAnimation("ColorTo");
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Tizen.Applications;
+using Tizen.Security;
+using Tizen.Wearable.CircularUI.Forms;
+using Tizen.Wearable.CircularUI.Forms.Renderer;
+using VoiceMemo.EffectRenderers;
+using VoiceMemo.Services;
+using VoiceMemo.Utility;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+using TLog = Tizen.Log;
+
+namespace VoiceMemo
+{
+ class Program : global::Xamarin.Forms.Platform.Tizen.ApplicationLifecycle
+ {
+ static App app;
+ protected async override void OnCreate()
+ {
+ bool recorderGranted = false;
+ if (PrivacyPrivilegeManager.CheckPermission(PrivilegeInformation.RecorderPrivilege) != CheckResult.Allow)
+ {
+ recorderGranted = await RequestPermission(PrivilegeInformation.RecorderPrivilege);
+ }
+ else
+ {
+ recorderGranted = true;
+ }
+
+ bool mediaStorageGranted = false;
+ if (PrivacyPrivilegeManager.CheckPermission(PrivilegeInformation.MediaStoragePrivilege) != CheckResult.Allow)
+ {
+ mediaStorageGranted = await RequestPermission(PrivilegeInformation.MediaStoragePrivilege);
+ }
+ else
+ {
+ mediaStorageGranted = true;
+ }
+
+ if (!(recorderGranted && mediaStorageGranted))
+ {
+ Log.Error(LOG.TAG, "Failed to obtain user consent. So, app is going to be terminated.");
+ // Terminate this application.
+ FormsApplication.Current.Exit();
+ return;
+ }
+
+ app = new App();
+ FormsApplication.LoadApplication(app);
+ }
+ static Task<bool> RequestPermission(string privilege)
+ {
+ TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+ var response = PrivacyPrivilegeManager.GetResponseContext(privilege);
+ PrivacyPrivilegeManager.ResponseContext target;
+ response.TryGetTarget(out target);
+ target.ResponseFetched += (s, e) =>
+ {
+ if (e.cause == CallCause.Error)
+ {
+ /// Handle errors
+ Log.Error(LOG.TAG, "An error occurred while requesting an user permission");
+ tcs.SetResult(false);
+ return;
+ }
+
+ // Now, we can check if the permission is granted or not
+ switch (e.result)
+ {
+ case RequestResult.AllowForever:
+ // Permission is granted.
+ // We can do this permission-related task we want to do.
+ Log.Info(LOG.TAG, "Response: RequestResult.AllowForever");
+ tcs.SetResult(true);
+ break;
+ case RequestResult.DenyForever:
+ case RequestResult.DenyOnce:
+ // Functionality that depends on this permission will not be available
+ Log.Info(LOG.TAG, "Response: RequestResult." + e.result.ToString());
+ tcs.SetResult(false);
+ break;
+ }
+
+ };
+ PrivacyPrivilegeManager.RequestPermission(privilege);
+ return tcs.Task;
+ }
+
+ /// <summary>
+ /// Called when this application is terminated
+ /// </summary>
+ protected override void OnTerminate()
+ {
+ // Save the index of recording files
+ Preference.Set(DeviceInformationService.LastUsedID, AudioRecordService.numbering);
+ base.OnTerminate();
+ app.Terminate();
+ }
+
+ 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(global:: Tizen.Wearable.CircularUI.Forms.CircleScrollView), ()=> new global::Tizen.Wearable.CircularUI.Forms.Renderer.CircleScrollViewRenderer() },
+ { typeof(global:: Tizen.Wearable.CircularUI.Forms.TwoButtonPage), ()=> new global::Tizen.Wearable.CircularUI.Forms.Renderer.TwoButtonPageRenderer() },
+ };
+
+ var option = new InitializationOptions(app)
+ {
+ UseMessagingCenter = true,
+ UseStyle = true,
+ UseShell = false,
+ UseVisual = false,
+ StaticRegistarStrategy = StaticRegistrarStrategy.StaticRegistrarOnly,
+ CustomHandlers = customRenderers,
+ Flags = InitializationFlags.DisableCss
+ };
+
+ Xamarin.Forms.InitializationOptions.EffectScope myeffect = new Xamarin.Forms.InitializationOptions.EffectScope();
+ myeffect.Name = "SEC";
+ //myeffect.Name = "MyCompany";
+
+ ExportEffectAttribute attr1 = new ExportEffectAttribute(typeof(BlendColorEffect), "BlendColorEffect");
+ myeffect.Effects = new ExportEffectAttribute[] { attr1 };
+
+ Xamarin.Forms.InitializationOptions.EffectScope circleEffect = new Xamarin.Forms.InitializationOptions.EffectScope();
+ circleEffect.Name = "CircleUI";
+ ExportEffectAttribute attr2 = new ExportEffectAttribute(typeof(TizenConfirmPopupEffect), "ContextPopupEffectBehavior");
+
+ ExportEffectAttribute attr3 = new ExportEffectAttribute(typeof(TizenCircleSurfaceEffect), "CircleSurfaceEffect");
+
+ circleEffect.Effects = new ExportEffectAttribute[] { attr2, attr3 };
+
+ option.EffectScopes = new InitializationOptions.EffectScope[] { myeffect, circleEffect };
+
+
+ // NOTE(vincent): Note that this setenv should be called per app.
+ string productTheme = Interop.Interop.SystemCall.GetEnv("ELM_DEVICE_PROD_THEME");
+ if (productTheme == "pulse")
+ {
+ Interop.Interop.SystemCall.SetEnv("ELM_PROD_THEME", "pulse", 1);
+ }
+ Forms.Init(option);
+ // TODO(vincent): Remove this line if OneUI is supported in Xamarin.
+ // Precondition: oneui-theme-1.1.0-1.armv7l.rpm should be installed.
+ //Elementary.AddThemeOverlay("/usr/share/elementary/themes/oneui-theme.edj");
+ global::Tizen.Wearable.CircularUI.Forms.Renderer.FormsCircularUI.Init();
+ app.FormsApplication.Run(args);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<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>
+ <Compile Remove="Utility.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Folder Include="Effects\" />
+ <Folder Include="lib\" />
+ <Folder Include="Resx\" />
+ <Folder Include="res\2019_custom_button\" />
+ <Folder Include="res\2019\" />
+ <Folder Include="res\2019_images_icons\" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="sqlite" Version="3.13.0" />
+ <PackageReference Include="sqlite-net-base" Version="1.5.166-beta" />
+ <PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
+ </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>
+ <Reference Include="ElmSharp">
+ <HintPath>lib\ElmSharp.dll</HintPath>
+ </Reference>
+ <Reference Include="ElmSharp.Wearable">
+ <HintPath>lib\ElmSharp.Wearable.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <EmbeddedResource Update="Views\NoRecordView.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Update="App.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\CancelPage.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\CirclePageEx.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\DetailsPage.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\LanguageSelectionPage.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\MainPage.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\PlayBackPage.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\RecordingPage.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\RecordListPage.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ <None Update="Views\SttLanguageViewCell.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ </None>
+ </ItemGroup>
+
+ <!--<ItemGroup>
+ <EmbeddedResource Update="App.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="MainPage.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ </ItemGroup>-->
+
+</Project>
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<resources>
+ <application name="org.tizen.example.VoiceMemo.Tizen.Wearable" >
+ <serviceProfile
+ id="/dotnetvoicememo/filetransfer"
+ name="FileTransferSender"
+ role="consumer"
+ version="1.0"
+ serviceLimit="ONE_PEERAGENT"
+ serviceTimeout="30">
+ <supportedTransports>
+ <transport type="TRANSPORT_BT" />
+ <transport type="TRANSPORT_WIFI"/>
+ <transport type="TRANSPORT_BLE"/>
+ </supportedTransports>
+ <serviceChannel
+ id="113"
+ dataRate="low"
+ priority="low"
+ reliability= "enable">
+ </serviceChannel>
+ </serviceProfile>
+ </application>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="org.tizen.example.VoiceMemo" version="1.0.0" api-version="4" xmlns="http://tizen.org/ns/packages">
+ <profile name="wearable" />
+ <ui-application appid="org.tizen.example.VoiceMemo" exec="VoiceMemo.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" splash-screen-display="true" process-pool="true" launch_mode="single">
+ <label xml:lang="en-us">Voice Recorder</label>
+ <label xml:lang="hi-in">.NET वॉइस मेमो</label>
+ <label xml:lang="ko-kr">닷넷 음성 메모</label>
+ <icon>VoiceMemo.png</icon>
+ <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+ <background-category value="media" />
+ <splash-screens />
+ </ui-application>
+ <shortcut-list />
+ <privileges>
+ <privilege>http://tizen.org/privilege/mediastorage</privilege>
+ <privilege>http://tizen.org/privilege/externalstorage</privilege>
+ <privilege>http://tizen.org/privilege/externalstorage.appdata</privilege>
+ <privilege>http://tizen.org/privilege/recorder</privilege>
+ <privilege>http://tizen.org/privilege/volume.set</privilege>
+ <privilege>http://tizen.org/privilege/content.write</privilege>
+ <privilege>http://tizen.org/privilege/window.priority.set</privilege>
+ <privilege>http://developer.samsung.com/tizen/privilege/screen.top</privilege>
+ </privileges>
+ <dependencies />
+ <provides-appdefined-privileges />
+ <feature name="http://tizen.org/feature/microphone">true</feature>
+ <feature name="http://tizen.org/feature/speech.recognition">true</feature>
+</manifest>
+
+<!--<splash-screens>
+ <splash-screen orientation="portrait" src="splash.png" type="img" />
+</splash-screens>-->
\ No newline at end of file
namespace Tizen.Wearable.CircularUI.Forms.Renderer
{
- class CircleScrollViewRenderer : ViewRenderer<CircleScrollView, ElmSharp.Wearable.CircleScroller>
+ public class CircleScrollViewRenderer : ViewRenderer<CircleScrollView, ElmSharp.Wearable.CircleScroller>
{
Xamarin.Forms.Platform.Tizen.Native.Box _scrollCanvas;
ElmSharp.SmartEvent _scrollAnimationStart, _scrollAnimationStop;
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Alarm", "Test\Alarm\Alarm\Alarm.csproj", "{4653BFED-31B5-412F-8076-A609A4E5D851}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VoiceMemo", "VoiceMemo", "{E1BF76E2-0454-490C-964A-587E7926CA13}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VoiceMemo", "Test\Voicememo2020\VoiceMemo\VoiceMemo.csproj", "{6DADDF43-87F3-43C6-822D-12DE155CCF86}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = 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
+ {6DADDF43-87F3-43C6-822D-12DE155CCF86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6DADDF43-87F3-43C6-822D-12DE155CCF86}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6DADDF43-87F3-43C6-822D-12DE155CCF86}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6DADDF43-87F3-43C6-822D-12DE155CCF86}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{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}
+ {E1BF76E2-0454-490C-964A-587E7926CA13} = {087187F9-A361-4269-88EB-47A44185FA7F}
+ {6DADDF43-87F3-43C6-822D-12DE155CCF86} = {E1BF76E2-0454-490C-964A-587E7926CA13}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {81158F7F-D003-4C6D-9937-5002C54D57EA}
{
for (var i = 0; i < effectScopes.Length; i++)
{
- var effectScope = effectScopes[0];
+ var effectScope = effectScopes[i];
Registrar.RegisterEffects(effectScope.Name, effectScope.Effects);
}
}