SettingsService now uses Application.Current.Properties dictionary.

Platform specific settings code removed.
This commit is contained in:
David Britch 2018-03-29 13:26:26 +01:00
parent f2a4f3ac2c
commit a627d1f9e2
11 changed files with 811 additions and 1001 deletions

View File

@ -15,7 +15,6 @@ namespace eShopOnContainers.Core.Extensions
} }
return collection; return collection;
} }
} }
} }

View File

@ -1,4 +1,6 @@
namespace eShopOnContainers.Core.Services.Settings using System.Threading.Tasks;
namespace eShopOnContainers.Core.Services.Settings
{ {
public interface ISettingsService public interface ISettingsService
{ {
@ -10,5 +12,10 @@
string Latitude { get; set; } string Latitude { get; set; }
string Longitude { get; set; } string Longitude { get; set; }
bool AllowGpsLocation { get; set; } bool AllowGpsLocation { get; set; }
bool GetValueOrDefault(string key, bool defaultValue);
string GetValueOrDefault(string key, string defaultValue);
Task AddOrUpdateValue(string key, bool value);
Task AddOrUpdateValue(string key, string value);
} }
} }

View File

@ -1,13 +0,0 @@
namespace eShopOnContainers.Core.Services.Settings
{
public interface ISettingsServiceImplementation
{
bool GetValueOrDefault(string key, bool defaultValue);
string GetValueOrDefault(string key, string defaultValue);
bool AddOrUpdateValue(string key, bool value);
bool AddOrUpdateValue(string key, string value);
void Remove(string key);
}
}

View File

@ -1,21 +1,11 @@
using eShopOnContainers.Core.Services.Dependency; using System;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace eShopOnContainers.Core.Services.Settings namespace eShopOnContainers.Core.Services.Settings
{ {
public class SettingsService : ISettingsService public class SettingsService : ISettingsService
{ {
private readonly ISettingsServiceImplementation _settingsService;
ISettingsServiceImplementation AppSettings
{
get { return _settingsService; }
}
public SettingsService(IDependencyService dependencyService)
{
_settingsService = dependencyService.Get<ISettingsServiceImplementation>();
}
#region Setting Constants #region Setting Constants
private const string AccessToken = "access_token"; private const string AccessToken = "access_token";
@ -37,52 +27,113 @@ namespace eShopOnContainers.Core.Services.Settings
#endregion #endregion
#region Settings Properties
public string AuthAccessToken public string AuthAccessToken
{ {
get => AppSettings.GetValueOrDefault(AccessToken, AccessTokenDefault); get => GetValueOrDefault(AccessToken, AccessTokenDefault);
set => AppSettings.AddOrUpdateValue(AccessToken, value); set => AddOrUpdateValue(AccessToken, value);
} }
public string AuthIdToken public string AuthIdToken
{ {
get => AppSettings.GetValueOrDefault(IdToken, IdTokenDefault); get => GetValueOrDefault(IdToken, IdTokenDefault);
set => AppSettings.AddOrUpdateValue(IdToken, value); set => AddOrUpdateValue(IdToken, value);
} }
public bool UseMocks public bool UseMocks
{ {
get => AppSettings.GetValueOrDefault(IdUseMocks, UseMocksDefault); get => GetValueOrDefault(IdUseMocks, UseMocksDefault);
set => AppSettings.AddOrUpdateValue(IdUseMocks, value); set => AddOrUpdateValue(IdUseMocks, value);
} }
public string UrlBase public string UrlBase
{ {
get => AppSettings.GetValueOrDefault(IdUrlBase, UrlBaseDefault); get => GetValueOrDefault(IdUrlBase, UrlBaseDefault);
set => AppSettings.AddOrUpdateValue(IdUrlBase, value); set => AddOrUpdateValue(IdUrlBase, value);
} }
public bool UseFakeLocation public bool UseFakeLocation
{ {
get => AppSettings.GetValueOrDefault(IdUseFakeLocation, UseFakeLocationDefault); get => GetValueOrDefault(IdUseFakeLocation, UseFakeLocationDefault);
set => AppSettings.AddOrUpdateValue(IdUseFakeLocation, value); set => AddOrUpdateValue(IdUseFakeLocation, value);
} }
public string Latitude public string Latitude
{ {
get => AppSettings.GetValueOrDefault(IdLatitude, FakeLatitudeDefault.ToString()); get => GetValueOrDefault(IdLatitude, FakeLatitudeDefault.ToString());
set => AppSettings.AddOrUpdateValue(IdLatitude, value); set => AddOrUpdateValue(IdLatitude, value);
} }
public string Longitude public string Longitude
{ {
get => AppSettings.GetValueOrDefault(IdLongitude, FakeLongitudeDefault.ToString()); get => GetValueOrDefault(IdLongitude, FakeLongitudeDefault.ToString());
set => AppSettings.AddOrUpdateValue(IdLongitude, value); set => AddOrUpdateValue(IdLongitude, value);
} }
public bool AllowGpsLocation public bool AllowGpsLocation
{ {
get => AppSettings.GetValueOrDefault(IdAllowGpsLocation, AllowGpsLocationDefault); get => GetValueOrDefault(IdAllowGpsLocation, AllowGpsLocationDefault);
set => AppSettings.AddOrUpdateValue(IdAllowGpsLocation, value); set => AddOrUpdateValue(IdAllowGpsLocation, value);
}
#endregion
#region Public Methods
public Task AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value);
public Task AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value);
public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
#endregion
#region Internal Implementation
async Task AddOrUpdateValueInternal<T>(string key, T value)
{
if (value == null)
{
await Remove(key);
}
Application.Current.Properties[key] = value;
try
{
await Application.Current.SavePropertiesAsync();
}
catch (Exception ex)
{
Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message);
} }
} }
T GetValueOrDefaultInternal<T>(string key, T defaultValue = default(T))
{
object value = null;
if (Application.Current.Properties.ContainsKey(key))
{
value = Application.Current.Properties[key];
}
return null != value ? (T)value : defaultValue;
}
async Task Remove(string key)
{
try
{
if (Application.Current.Properties[key] != null)
{
Application.Current.Properties.Remove(key);
await Application.Current.SavePropertiesAsync();
}
}
catch (Exception ex)
{
Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message);
}
}
#endregion
}
} }

View File

@ -32,7 +32,7 @@ namespace eShopOnContainers.Droid.Extensions
{ {
return new DateTimeOffset(Epoch.AddMilliseconds(location.Time)); return new DateTimeOffset(Epoch.AddMilliseconds(location.Time));
} }
catch (Exception e) catch (Exception)
{ {
return new DateTimeOffset(Epoch); return new DateTimeOffset(Epoch);
} }

View File

@ -1,149 +0,0 @@
using Android.App;
using Android.Content;
using Android.Preferences;
using eShopOnContainers.Core.Services.Settings;
using eShopOnContainers.Droid.Services;
using System;
[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))]
namespace eShopOnContainers.Droid.Services
{
public class SettingsServiceImplementation : ISettingsServiceImplementation
{
#region Internal Implementation
readonly object _locker = new object();
ISharedPreferences GetSharedPreference()
{
return PreferenceManager.GetDefaultSharedPreferences(Application.Context);
}
bool AddOrUpdateValueInternal<T>(string key, T value)
{
if (Application.Context == null)
return false;
if (value == null)
{
Remove(key);
return true;
}
var type = typeof(T);
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = Nullable.GetUnderlyingType(type);
}
var typeCode = Type.GetTypeCode(type);
lock (_locker)
{
using (var sharedPrefs = GetSharedPreference())
{
using (var editor = sharedPrefs.Edit())
{
switch (typeCode)
{
case TypeCode.Boolean:
editor.PutBoolean(key, Convert.ToBoolean(value));
break;
case TypeCode.String:
editor.PutString(key, Convert.ToString(value));
break;
default:
throw new ArgumentException($"Value of type {typeCode} is not supported.");
}
editor.Commit();
}
}
}
return true;
}
T GetValueOrDefaultInternal<T>(string key, T defaultValue = default(T))
{
if (Application.Context == null)
return defaultValue;
if (!Contains(key))
return defaultValue;
lock (_locker)
{
using (var sharedPrefs = GetSharedPreference())
{
var type = typeof(T);
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = Nullable.GetUnderlyingType(type);
}
object value = null;
var typeCode = Type.GetTypeCode(type);
switch (typeCode)
{
case TypeCode.Boolean:
value = sharedPrefs.GetBoolean(key, Convert.ToBoolean(defaultValue));
break;
case TypeCode.String:
value = sharedPrefs.GetString(key, Convert.ToString(defaultValue));
break;
default:
throw new ArgumentException($"Value of type {typeCode} is not supported.");
}
return null != value ? (T)value : defaultValue;
}
}
}
bool Contains(string key)
{
if (Application.Context == null)
return false;
lock (_locker)
{
using (var sharedPrefs = GetSharedPreference())
{
if (sharedPrefs == null)
return false;
return sharedPrefs.Contains(key);
}
}
}
#endregion
#region ISettingsServiceImplementation
public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value);
public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value);
public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
public void Remove(string key)
{
if (Application.Context == null)
return;
lock (_locker)
{
using (var sharedPrefs = GetSharedPreference())
{
using (var editor = sharedPrefs.Edit())
{
editor.Remove(key);
editor.Commit();
}
}
}
}
#endregion
}
}

View File

@ -19,7 +19,7 @@
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk> <AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion> <TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis> <AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
<AndroidStoreUncompressedFileExtensions /> <AndroidStoreUncompressedFileExtensions />
<MandroidI18n /> <MandroidI18n />
@ -211,7 +211,6 @@
<Compile Include="Effects\CircleEffect.cs" /> <Compile Include="Effects\CircleEffect.cs" />
<Compile Include="Effects\BaseContainerEffect.cs" /> <Compile Include="Effects\BaseContainerEffect.cs" />
<Compile Include="Activities\SplashActivity.cs" /> <Compile Include="Activities\SplashActivity.cs" />
<Compile Include="Services\SettingsServiceImplementation.cs" />
<Compile Include="Services\PermissionsService.cs" /> <Compile Include="Services\PermissionsService.cs" />
<Compile Include="Services\LocationServiceImplementation.cs" /> <Compile Include="Services\LocationServiceImplementation.cs" />
<Compile Include="Services\GeolocationSingleListener.cs" /> <Compile Include="Services\GeolocationSingleListener.cs" />

View File

@ -13,6 +13,7 @@
<AssemblyName>eShopOnContainersTestRunneriOS</AssemblyName> <AssemblyName>eShopOnContainersTestRunneriOS</AssemblyName>
<NuGetPackageImportStamp> <NuGetPackageImportStamp>
</NuGetPackageImportStamp> </NuGetPackageImportStamp>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -145,7 +146,6 @@
<Reference Include="xunit.execution.dotnet"> <Reference Include="xunit.execution.dotnet">
<HintPath>..\..\..\..\packages\xunit.extensibility.execution.2.3.1\lib\netstandard1.1\xunit.execution.dotnet.dll</HintPath> <HintPath>..\..\..\..\packages\xunit.extensibility.execution.2.3.1\lib\netstandard1.1\xunit.execution.dotnet.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json"> <Reference Include="Newtonsoft.Json">
<HintPath>..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll</HintPath> <HintPath>..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>

View File

@ -1,65 +1,110 @@
using eShopOnContainers.Core.Services.Settings; using eShopOnContainers.Core.Services.Settings;
using System; using System.Collections.Generic;
using System.Threading.Tasks;
namespace eShopOnContainers.UnitTests.Mocks namespace eShopOnContainers.UnitTests.Mocks
{ {
public class MockSettingsService : ISettingsService public class MockSettingsService : ISettingsService
{ {
string _accessTokenDefault = string.Empty; IDictionary<string, object> _settings = new Dictionary<string, object>();
string _idTokenDefault = string.Empty;
bool _useMocksDefault = true; const string AccessToken = "access_token";
string _urlBaseDefault = "https://13.88.8.119"; const string IdToken = "id_token";
bool _useFakeLocationDefault = false; const string IdUseMocks = "use_mocks";
bool _allowGpsLocationDefault = false; const string IdUrlBase = "url_base";
double _fakeLatitudeDefault = 47.604610d; const string IdUseFakeLocation = "use_fake_location";
double _fakeLongitudeDefault = -122.315752d; const string IdLatitude = "latitude";
const string IdLongitude = "longitude";
const string IdAllowGpsLocation = "allow_gps_location";
readonly string AccessTokenDefault = string.Empty;
readonly string IdTokenDefault = string.Empty;
readonly bool UseMocksDefault = true;
readonly bool UseFakeLocationDefault = false;
readonly bool AllowGpsLocationDefault = false;
readonly double FakeLatitudeDefault = 47.604610d;
readonly double FakeLongitudeDefault = -122.315752d;
readonly string UrlBaseDefault = "https://13.88.8.119";
public string AuthAccessToken public string AuthAccessToken
{ {
get { return _accessTokenDefault; } get => GetValueOrDefault(AccessToken, AccessTokenDefault);
set { _accessTokenDefault = value; } set => AddOrUpdateValue(AccessToken, value);
} }
public string AuthIdToken public string AuthIdToken
{ {
get { return _idTokenDefault; } get => GetValueOrDefault(IdToken, IdTokenDefault);
set { _idTokenDefault = value; } set => AddOrUpdateValue(IdToken, value);
} }
public bool UseMocks public bool UseMocks
{ {
get { return _useMocksDefault; } get => GetValueOrDefault(IdUseMocks, UseMocksDefault);
set { _useMocksDefault = value; } set => AddOrUpdateValue(IdUseMocks, value);
} }
public string UrlBase public string UrlBase
{ {
get { return _urlBaseDefault; } get => GetValueOrDefault(IdUrlBase, UrlBaseDefault);
set { _urlBaseDefault = value; } set => AddOrUpdateValue(IdUrlBase, value);
} }
public bool UseFakeLocation public bool UseFakeLocation
{ {
get { return _useFakeLocationDefault; } get => GetValueOrDefault(IdUseFakeLocation, UseFakeLocationDefault);
set { _useFakeLocationDefault = value; } set => AddOrUpdateValue(IdUseFakeLocation, value);
} }
public string Latitude public string Latitude
{ {
get { return _fakeLatitudeDefault.ToString(); } get => GetValueOrDefault(IdLatitude, FakeLatitudeDefault.ToString());
set { _fakeLatitudeDefault = Convert.ToDouble(value); } set => AddOrUpdateValue(IdLatitude, value);
} }
public string Longitude public string Longitude
{ {
get { return _fakeLongitudeDefault.ToString(); } get => GetValueOrDefault(IdLongitude, FakeLongitudeDefault.ToString());
set { _fakeLongitudeDefault = Convert.ToDouble(value); } set => AddOrUpdateValue(IdLongitude, value);
} }
public bool AllowGpsLocation public bool AllowGpsLocation
{ {
get { return _allowGpsLocationDefault; } get => GetValueOrDefault(IdAllowGpsLocation, AllowGpsLocationDefault);
set { _allowGpsLocationDefault = value; } set => AddOrUpdateValue(IdAllowGpsLocation, value);
}
public Task AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value);
public Task AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value);
public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
Task AddOrUpdateValueInternal<T>(string key, T value)
{
if (value == null)
{
Remove(key);
}
_settings[key] = value;
return Task.Delay(10);
}
T GetValueOrDefaultInternal<T>(string key, T defaultValue = default(T))
{
object value = null;
if (_settings.ContainsKey(key))
{
value = _settings[key];
}
return null != value ? (T)value : defaultValue;
}
void Remove(string key)
{
if (_settings[key] != null)
{
_settings.Remove(key);
}
} }
} }
} }

View File

@ -1,128 +0,0 @@
using eShopOnContainers.Core.Services.Settings;
using eShopOnContainers.iOS.Services;
using Foundation;
using System;
[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))]
namespace eShopOnContainers.iOS.Services
{
public class SettingsServiceImplementation : ISettingsServiceImplementation
{
#region Internal Implementation
readonly object _locker = new object();
NSUserDefaults GetUserDefaults() => NSUserDefaults.StandardUserDefaults;
bool AddOrUpdateValueInternal<T>(string key, T value)
{
if (value == null)
{
Remove(key);
return true;
}
var type = typeof(T);
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = Nullable.GetUnderlyingType(type);
}
var typeCode = Type.GetTypeCode(type);
lock (_locker)
{
var defaults = GetUserDefaults();
switch (typeCode)
{
case TypeCode.Boolean:
defaults.SetBool(Convert.ToBoolean(value), key);
break;
case TypeCode.String:
defaults.SetString(Convert.ToString(value), key);
break;
default:
throw new ArgumentException($"Value of type {typeCode} is unsupported.");
}
try
{
defaults.Synchronize();
}
catch (Exception ex)
{
Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message);
}
}
return true;
}
T GetValueOrDefaultInternal<T>(string key, T defaultValue = default(T))
{
lock (_locker)
{
var defaults = GetUserDefaults();
if (defaults[key] == null)
{
return defaultValue;
}
var type = typeof(T);
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = Nullable.GetUnderlyingType(type);
}
object value = null;
var typeCode = Type.GetTypeCode(type);
switch (typeCode)
{
case TypeCode.Boolean:
value = defaults.BoolForKey(key);
break;
case TypeCode.String:
value = defaults.StringForKey(key);
break;
default:
throw new ArgumentException($"Value of type {typeCode} is unsupported.");
}
return null != value ? (T)value : defaultValue;
}
}
#endregion
#region ISettingsServiceImplementation
public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value);
public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value);
public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue);
public void Remove(string key)
{
lock (_locker)
{
var defaults = GetUserDefaults();
try
{
if (defaults[key] != null)
{
defaults.RemoveObject(key);
defaults.Synchronize();
}
}
catch (Exception ex)
{
Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message);
}
}
}
#endregion
}
}

View File

@ -15,6 +15,7 @@
<NuGetPackageImportStamp> <NuGetPackageImportStamp>
</NuGetPackageImportStamp> </NuGetPackageImportStamp>
<SkipValidatePackageReferences>true</SkipValidatePackageReferences> <SkipValidatePackageReferences>true</SkipValidatePackageReferences>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -125,7 +126,6 @@
<BundleResource Include="Resources\menu_campaigns%402x.png" /> <BundleResource Include="Resources\menu_campaigns%402x.png" />
<BundleResource Include="Resources\menu_campaigns%403x.png" /> <BundleResource Include="Resources\menu_campaigns%403x.png" />
<None Include="packages.config" /> <None Include="packages.config" />
<Compile Include="Services\SettingsServiceImplementation.cs" />
<Compile Include="Services\LocationServiceImplementation.cs" /> <Compile Include="Services\LocationServiceImplementation.cs" />
<Compile Include="Services\PermissionsService.cs" /> <Compile Include="Services\PermissionsService.cs" />
<Compile Include="Services\GeolocationSingleUpdateDelegate.cs" /> <Compile Include="Services\GeolocationSingleUpdateDelegate.cs" />
@ -182,7 +182,6 @@
<Reference Include="Plugin.Settings"> <Reference Include="Plugin.Settings">
<HintPath>..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\Xamarin.iOS10\Plugin.Settings.dll</HintPath> <HintPath>..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\Xamarin.iOS10\Plugin.Settings.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json"> <Reference Include="Newtonsoft.Json">
<HintPath>..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll</HintPath> <HintPath>..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>