mirror of
https://github.com/bitwarden/android.git
synced 2024-12-21 00:31:58 +03:00
Custom ISettings implemtation for app group support on iOS.
This commit is contained in:
parent
f97b62c51d
commit
9d8f54af9d
3 changed files with 251 additions and 19 deletions
|
@ -10,7 +10,6 @@ using Bit.App.Abstractions;
|
||||||
using Bit.App.Services;
|
using Bit.App.Services;
|
||||||
using Microsoft.Practices.Unity;
|
using Microsoft.Practices.Unity;
|
||||||
using Bit.iOS.Services;
|
using Bit.iOS.Services;
|
||||||
using Plugin.Settings;
|
|
||||||
using Plugin.Connectivity;
|
using Plugin.Connectivity;
|
||||||
using Acr.UserDialogs;
|
using Acr.UserDialogs;
|
||||||
using Bit.App.Repositories;
|
using Bit.App.Repositories;
|
||||||
|
@ -26,19 +25,11 @@ using Plugin.DeviceInfo;
|
||||||
|
|
||||||
namespace Bit.iOS
|
namespace Bit.iOS
|
||||||
{
|
{
|
||||||
// The UIApplicationDelegate for the application. This class is responsible for launching the
|
|
||||||
// User Interface of the application, as well as listening (and optionally responding) to
|
|
||||||
// application events from iOS.
|
|
||||||
[Register("AppDelegate")]
|
[Register("AppDelegate")]
|
||||||
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
|
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
|
||||||
{
|
{
|
||||||
//
|
public ISettings Settings { get; set; }
|
||||||
// This method is invoked when the application has loaded and is ready to run. In this
|
|
||||||
// method you should instantiate the window, load the UI into it and then make the window
|
|
||||||
// visible.
|
|
||||||
//
|
|
||||||
// You have 17 seconds to return from this method, or iOS will terminate your application.
|
|
||||||
//
|
|
||||||
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
||||||
{
|
{
|
||||||
CrossFingerprint.AllowReuse = false;
|
CrossFingerprint.AllowReuse = false;
|
||||||
|
@ -87,7 +78,8 @@ namespace Bit.iOS
|
||||||
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
|
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
|
||||||
|
|
||||||
// Log the date/time we last backgrounded
|
// Log the date/time we last backgrounded
|
||||||
CrossSettings.Current.AddOrUpdateValue(Constants.SettingLastBackgroundedDate, DateTime.UtcNow);
|
|
||||||
|
Settings.AddOrUpdateValue(Constants.SettingLastBackgroundedDate, DateTime.UtcNow);
|
||||||
|
|
||||||
base.DidEnterBackground(uiApplication);
|
base.DidEnterBackground(uiApplication);
|
||||||
Debug.WriteLine("DidEnterBackground");
|
Debug.WriteLine("DidEnterBackground");
|
||||||
|
@ -198,11 +190,13 @@ namespace Bit.iOS
|
||||||
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
// Other
|
// Other
|
||||||
.RegisterInstance(CrossDeviceInfo.Current, new ContainerControlledLifetimeManager())
|
.RegisterInstance(CrossDeviceInfo.Current, new ContainerControlledLifetimeManager())
|
||||||
.RegisterInstance(CrossSettings.Current, new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
|
.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
|
||||||
.RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager())
|
.RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager())
|
||||||
.RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager());
|
.RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager());
|
||||||
|
|
||||||
|
Settings = new Settings("group.com.8bit.bitwarden");
|
||||||
|
container.RegisterInstance(Settings, new ContainerControlledLifetimeManager());
|
||||||
|
|
||||||
CrossPushNotification.Initialize(container.Resolve<IPushNotificationListener>());
|
CrossPushNotification.Initialize(container.Resolve<IPushNotificationListener>());
|
||||||
container.RegisterInstance(CrossPushNotification.Current, new ContainerControlledLifetimeManager());
|
container.RegisterInstance(CrossPushNotification.Current, new ContainerControlledLifetimeManager());
|
||||||
|
|
||||||
|
|
235
src/iOS/Services/Settings.cs
Normal file
235
src/iOS/Services/Settings.cs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
|
||||||
|
using System;
|
||||||
|
#if __UNIFIED__
|
||||||
|
using Foundation;
|
||||||
|
#else
|
||||||
|
using MonoTouch.Foundation;
|
||||||
|
#endif
|
||||||
|
using Plugin.Settings.Abstractions;
|
||||||
|
|
||||||
|
namespace Bit.iOS.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Main implementation for ISettings
|
||||||
|
/// </summary>
|
||||||
|
public class Settings : ISettings
|
||||||
|
{
|
||||||
|
private readonly object locker = new object();
|
||||||
|
private readonly NSUserDefaults _defaults;
|
||||||
|
|
||||||
|
public Settings(string defaultsName)
|
||||||
|
{
|
||||||
|
_defaults = string.IsNullOrWhiteSpace(defaultsName) ? NSUserDefaults.StandardUserDefaults
|
||||||
|
: new NSUserDefaults(defaultsName, NSUserDefaultsType.SuiteName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current value or the default that you specify.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Vaue of t (bool, int, float, long, string)</typeparam>
|
||||||
|
/// <param name="key">Key for settings</param>
|
||||||
|
/// <param name="defaultValue">default value if not set</param>
|
||||||
|
/// <returns>Value or default</returns>
|
||||||
|
public T GetValueOrDefault<T>(string key, T defaultValue = default(T))
|
||||||
|
{
|
||||||
|
lock(locker)
|
||||||
|
{
|
||||||
|
var defaults = _defaults;
|
||||||
|
|
||||||
|
if(defaults.ValueForKey(new NSString(key)) == null)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
Type typeOf = typeof(T);
|
||||||
|
if(typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||||
|
{
|
||||||
|
typeOf = Nullable.GetUnderlyingType(typeOf);
|
||||||
|
}
|
||||||
|
object value = null;
|
||||||
|
var typeCode = Type.GetTypeCode(typeOf);
|
||||||
|
switch(typeCode)
|
||||||
|
{
|
||||||
|
case TypeCode.Decimal:
|
||||||
|
var savedDecimal = defaults.StringForKey(key);
|
||||||
|
value = Convert.ToDecimal(savedDecimal, System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
value = defaults.BoolForKey(key);
|
||||||
|
break;
|
||||||
|
case TypeCode.Int64:
|
||||||
|
var savedInt64 = defaults.StringForKey(key);
|
||||||
|
value = Convert.ToInt64(savedInt64, System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case TypeCode.Double:
|
||||||
|
value = defaults.DoubleForKey(key);
|
||||||
|
break;
|
||||||
|
case TypeCode.String:
|
||||||
|
value = defaults.StringForKey(key);
|
||||||
|
break;
|
||||||
|
case TypeCode.Int32:
|
||||||
|
#if __UNIFIED__
|
||||||
|
value = (Int32)defaults.IntForKey(key);
|
||||||
|
#else
|
||||||
|
value = defaults.IntForKey(key);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TypeCode.Single:
|
||||||
|
#if __UNIFIED__
|
||||||
|
value = (float)defaults.FloatForKey(key);
|
||||||
|
#else
|
||||||
|
value = defaults.FloatForKey(key);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.DateTime:
|
||||||
|
var savedTime = defaults.StringForKey(key);
|
||||||
|
if(string.IsNullOrWhiteSpace(savedTime))
|
||||||
|
{
|
||||||
|
value = defaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var ticks = Convert.ToInt64(savedTime, System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
if(ticks >= 0)
|
||||||
|
{
|
||||||
|
//Old value, stored before update to UTC values
|
||||||
|
value = new DateTime(ticks);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//New value, UTC
|
||||||
|
value = new DateTime(-ticks, DateTimeKind.Utc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
if(defaultValue is Guid)
|
||||||
|
{
|
||||||
|
var outGuid = Guid.Empty;
|
||||||
|
var savedGuid = defaults.StringForKey(key);
|
||||||
|
if(string.IsNullOrWhiteSpace(savedGuid))
|
||||||
|
{
|
||||||
|
value = outGuid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Guid.TryParse(savedGuid, out outGuid);
|
||||||
|
value = outGuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("Value of type {0} is not supported.", value.GetType().Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null != value ? (T)value : defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or updates a value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">key to update</param>
|
||||||
|
/// <param name="value">value to set</param>
|
||||||
|
/// <returns>True if added or update and you need to save</returns>
|
||||||
|
public bool AddOrUpdateValue<T>(string key, T value)
|
||||||
|
{
|
||||||
|
Type typeOf = typeof(T);
|
||||||
|
if(typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||||
|
{
|
||||||
|
typeOf = Nullable.GetUnderlyingType(typeOf);
|
||||||
|
}
|
||||||
|
var typeCode = Type.GetTypeCode(typeOf);
|
||||||
|
return AddOrUpdateValue(key, value, typeCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AddOrUpdateValue(string key, object value, TypeCode typeCode)
|
||||||
|
{
|
||||||
|
lock(locker)
|
||||||
|
{
|
||||||
|
var defaults = _defaults;
|
||||||
|
switch(typeCode)
|
||||||
|
{
|
||||||
|
case TypeCode.Decimal:
|
||||||
|
defaults.SetString(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture), key);
|
||||||
|
break;
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
defaults.SetBool(Convert.ToBoolean(value), key);
|
||||||
|
break;
|
||||||
|
case TypeCode.Int64:
|
||||||
|
defaults.SetString(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture), key);
|
||||||
|
break;
|
||||||
|
case TypeCode.Double:
|
||||||
|
defaults.SetDouble(Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture), key);
|
||||||
|
break;
|
||||||
|
case TypeCode.String:
|
||||||
|
defaults.SetString(Convert.ToString(value), key);
|
||||||
|
break;
|
||||||
|
case TypeCode.Int32:
|
||||||
|
defaults.SetInt(Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture), key);
|
||||||
|
break;
|
||||||
|
case TypeCode.Single:
|
||||||
|
defaults.SetFloat(Convert.ToSingle(value, System.Globalization.CultureInfo.InvariantCulture), key);
|
||||||
|
break;
|
||||||
|
case TypeCode.DateTime:
|
||||||
|
defaults.SetString(Convert.ToString(-(Convert.ToDateTime(value)).ToUniversalTime().Ticks), key);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(value is Guid)
|
||||||
|
{
|
||||||
|
if(value == null)
|
||||||
|
value = Guid.Empty;
|
||||||
|
|
||||||
|
defaults.SetString(((Guid)value).ToString(), key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("Value of type {0} is not supported.", value.GetType().Name));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
defaults.Synchronize();
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a desired key from the settings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key for setting</param>
|
||||||
|
public void Remove(string key)
|
||||||
|
{
|
||||||
|
lock(locker)
|
||||||
|
{
|
||||||
|
var defaults = _defaults;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var nsString = new NSString(key);
|
||||||
|
if(defaults.ValueForKey(nsString) != null)
|
||||||
|
{
|
||||||
|
defaults.RemoveObject(key);
|
||||||
|
defaults.Synchronize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -113,6 +113,7 @@
|
||||||
<Compile Include="Services\KeyChainStorageService.cs" />
|
<Compile Include="Services\KeyChainStorageService.cs" />
|
||||||
<Compile Include="Main.cs" />
|
<Compile Include="Main.cs" />
|
||||||
<Compile Include="AppDelegate.cs" />
|
<Compile Include="AppDelegate.cs" />
|
||||||
|
<Compile Include="Services\Settings.cs" />
|
||||||
<None Include="Entitlements.plist" />
|
<None Include="Entitlements.plist" />
|
||||||
<None Include="Info.plist" />
|
<None Include="Info.plist" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@ -133,6 +134,14 @@
|
||||||
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
|
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Plugin.Settings, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Plugin.Settings.Abstractions, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
@ -173,12 +182,6 @@
|
||||||
<Reference Include="Plugin.Fingerprint.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=null">
|
<Reference Include="Plugin.Fingerprint.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=null">
|
||||||
<HintPath>..\..\packages\Plugin.Fingerprint.1.1.1-beta\lib\Xamarin.iOS10\Plugin.Fingerprint.Abstractions.dll</HintPath>
|
<HintPath>..\..\packages\Plugin.Fingerprint.1.1.1-beta\lib\Xamarin.iOS10\Plugin.Fingerprint.Abstractions.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Plugin.Settings, Version=2.1.0.0, Culture=neutral, PublicKeyToken=null">
|
|
||||||
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Settings.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=null">
|
|
||||||
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, PublicKeyToken=null">
|
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, PublicKeyToken=null">
|
||||||
<HintPath>..\..\packages\Splat.1.6.2\lib\Xamarin.iOS10\Splat.dll</HintPath>
|
<HintPath>..\..\packages\Splat.1.6.2\lib\Xamarin.iOS10\Splat.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|
Loading…
Reference in a new issue