mirror of
https://github.com/bitwarden/mobile
synced 2026-01-18 08:23:15 +00:00
Merge branch 'feature/maui-migration' into feature/maui-migration-passkeys
# Conflicts: # src/App/Platforms/iOS/AppDelegate.cs
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Foundation;
|
||||
using Foundation;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.iOS.Core.Utilities
|
||||
@@ -15,6 +11,7 @@ namespace Bit.iOS.Core.Utilities
|
||||
}
|
||||
|
||||
public static NSDictionary<KTo,VTo> ToNSDictionary<KFrom,VFrom,KTo,VTo>(this Dictionary<KFrom, VFrom> dict, Func<KFrom, KTo> keyConverter, Func<VFrom, VTo> valueConverter)
|
||||
where KFrom : notnull
|
||||
where KTo : NSObject
|
||||
where VTo : NSObject
|
||||
{
|
||||
@@ -23,19 +20,20 @@ namespace Bit.iOS.Core.Utilities
|
||||
return NSDictionary<KTo, VTo>.FromObjectsAndKeys(NSValues, NSKeys, NSKeys.Count());
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ToDictionary(this NSDictionary<NSString, NSObject> nsDict)
|
||||
public static Dictionary<string, object?> ToDictionary(this NSDictionary<NSString, NSObject> nsDict)
|
||||
{
|
||||
return nsDict.ToDictionary(v => v?.ToString() as object);
|
||||
return nsDict.ToDictionary(v => v?.ToString());
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ToDictionary(this NSDictionary<NSString, NSObject> nsDict, Func<NSObject, object> valueTransformer)
|
||||
public static Dictionary<string, object?> ToDictionary(this NSDictionary<NSString, NSObject> nsDict, Func<NSObject, object?> valueTransformer)
|
||||
{
|
||||
return nsDict.ToDictionary(k => k.ToString(), v => valueTransformer(v));
|
||||
}
|
||||
|
||||
public static Dictionary<KTo, VTo> ToDictionary<KFrom, VFrom, KTo, VTo>(this NSDictionary<KFrom, VFrom> nsDict, Func<KFrom, KTo> keyConverter, Func<VFrom, VTo> valueConverter)
|
||||
public static Dictionary<KTo, VTo?> ToDictionary<KFrom, VFrom, KTo, VTo>(this NSDictionary<KFrom, VFrom> nsDict, Func<KFrom, KTo> keyConverter, Func<VFrom, VTo?> valueConverter)
|
||||
where KFrom : NSObject
|
||||
where VFrom : NSObject
|
||||
where KTo : notnull
|
||||
{
|
||||
var keys = nsDict.Keys.Select(k => keyConverter(k)).ToArray();
|
||||
var values = nsDict.Values.Select(v => valueConverter(v)).ToArray();
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Domain;
|
||||
using System.Diagnostics;
|
||||
using Bit.Core.Services;
|
||||
using Bit.iOS.Core.Utilities;
|
||||
using Foundation;
|
||||
using Newtonsoft.Json;
|
||||
using ObjCRuntime;
|
||||
|
||||
namespace WatchConnectivity
|
||||
{
|
||||
@@ -17,35 +11,45 @@ namespace WatchConnectivity
|
||||
// Setup is converted from https://www.natashatherobot.com/watchconnectivity-say-hello-to-wcsession/
|
||||
// with some extra bits
|
||||
private static readonly WCSessionManager sharedManager = new WCSessionManager();
|
||||
private static WCSession session = WCSession.IsSupported ? WCSession.DefaultSession : null;
|
||||
private static WCSession? session = WCSession.IsSupported ? WCSession.DefaultSession : null;
|
||||
|
||||
public event WCSessionReceiveDataHandler OnApplicationContextUpdated;
|
||||
public event WCSessionReceiveDataHandler OnMessagedReceived;
|
||||
public delegate void WCSessionReceiveDataHandler(WCSession session, Dictionary<string, object> data);
|
||||
public event WCSessionReceiveDataHandler? OnApplicationContextUpdated;
|
||||
public event WCSessionReceiveDataHandler? OnMessagedReceived;
|
||||
public delegate void WCSessionReceiveDataHandler(WCSession session, Dictionary<string, object?> data);
|
||||
|
||||
WCSessionUserInfoTransfer _transf;
|
||||
WCSessionUserInfoTransfer? _transf;
|
||||
|
||||
private WCSession validSession
|
||||
private WCSession? validSession
|
||||
{
|
||||
get
|
||||
{
|
||||
if (session is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Paired status:{(session.Paired ? '✓' : '✗')}\n");
|
||||
Debug.WriteLine($"Watch App Installed status:{(session.WatchAppInstalled ? '✓' : '✗')}\n");
|
||||
return (session.Paired && session.WatchAppInstalled) ? session : null;
|
||||
}
|
||||
}
|
||||
|
||||
private WCSession validReachableSession
|
||||
private WCSession? validReachableSession
|
||||
{
|
||||
get
|
||||
{
|
||||
if (session is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return session.Reachable ? validSession : null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValidSession => validSession != null;
|
||||
|
||||
public bool IsSessionReachable => session.Reachable;
|
||||
public bool IsSessionReachable => session?.Reachable ?? false;
|
||||
|
||||
public bool IsSessionActivated => validSession?.ActivationState == WCSessionActivationState.Activated;
|
||||
|
||||
@@ -71,7 +75,7 @@ namespace WatchConnectivity
|
||||
|
||||
public override void SessionReachabilityDidChange(WCSession session)
|
||||
{
|
||||
Debug.WriteLine($"Watch connectivity Reachable:{(session.Reachable ? '✓' : '✗')}");
|
||||
Debug.WriteLine($"Watch connectivity Reachable:{(session?.Reachable == true ? '✓' : '✗')}");
|
||||
}
|
||||
|
||||
public void SendBackgroundHighPriorityMessage(NSDictionary<NSString, NSObject> applicationContext)
|
||||
@@ -102,7 +106,7 @@ namespace WatchConnectivity
|
||||
|
||||
public void SendBackgroundFifoHighPriorityMessage(Dictionary<string, object> message)
|
||||
{
|
||||
if(validSession is null || validSession.ActivationState != WCSessionActivationState.Activated)
|
||||
if (session is null || validSession is null || validSession.ActivationState != WCSessionActivationState.Activated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -112,6 +116,10 @@ namespace WatchConnectivity
|
||||
Debug.WriteLine("Started transferring user info");
|
||||
|
||||
_transf = session.TransferUserInfo(message.ToNSDictionary());
|
||||
if (_transf is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
@@ -136,7 +144,7 @@ namespace WatchConnectivity
|
||||
if (OnApplicationContextUpdated != null)
|
||||
{
|
||||
var keys = applicationContext.Keys.Select(k => k.ToString()).ToArray();
|
||||
var values = applicationContext.Values.Select(v => JsonConvert.DeserializeObject(v.ToString())).ToArray();
|
||||
var values = applicationContext.Values.Select(v => v != null ? JsonConvert.DeserializeObject(v.ToString()) : null).ToArray();
|
||||
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ namespace Bit.iOS.Core.Utilities
|
||||
{
|
||||
var builder = Bit.Core.MauiProgram.ConfigureMauiAppBuilder(ConfigureMAUIEffects, handlers =>
|
||||
{
|
||||
// WORKAROUND: This is needed to make TapGestureRecognizer work on extensions.
|
||||
handlers.AddHandler(typeof(Window), typeof(Handlers.CustomWindowHandler));
|
||||
ConfigureMAUIHandlers(handlers);
|
||||
})
|
||||
.UseMauiEmbedding<Application>();
|
||||
@@ -116,9 +114,13 @@ namespace Bit.iOS.Core.Utilities
|
||||
ServiceContainer.Register<INativeLogService>("nativeLogService", new ConsoleLogService());
|
||||
}
|
||||
|
||||
ILogger logger = null;
|
||||
if (ServiceContainer.Resolve<ILogger>("logger", true) == null)
|
||||
ILogger? logger = null;
|
||||
if (ServiceContainer.TryResolve<ILogger>(out var resolvedLogger))
|
||||
{
|
||||
logger = resolvedLogger;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEBUG
|
||||
logger = DebugLogger.Instance;
|
||||
#else
|
||||
@@ -129,6 +131,12 @@ namespace Bit.iOS.Core.Utilities
|
||||
|
||||
var preferencesStorage = new PreferencesStorageService(AppGroupId);
|
||||
var appGroupContainer = new NSFileManager().GetContainerUrl(AppGroupId);
|
||||
if (appGroupContainer?.Path is null)
|
||||
{
|
||||
var nreAppGroupContainer = new NullReferenceException("appGroupContainer or its Path is null when registering local services");
|
||||
logger!.Exception(nreAppGroupContainer);
|
||||
throw nreAppGroupContainer;
|
||||
}
|
||||
var liteDbStorage = new LiteDbStorageService(
|
||||
Path.Combine(appGroupContainer.Path, "Library", "bitwarden.db"));
|
||||
var localizeService = new LocalizeService();
|
||||
@@ -187,14 +195,14 @@ namespace Bit.iOS.Core.Utilities
|
||||
ServiceContainer.Resolve<ILogger>()));
|
||||
}
|
||||
|
||||
public static void Bootstrap(Func<Task> postBootstrapFunc = null)
|
||||
public static void Bootstrap(Func<Task>? postBootstrapFunc = null)
|
||||
{
|
||||
var locale = ServiceContainer.Resolve<IStateService>().GetLocale();
|
||||
(ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService)
|
||||
.Init(locale != null ? new System.Globalization.CultureInfo(locale) : null);
|
||||
?.Init(locale != null ? new System.Globalization.CultureInfo(locale) : null);
|
||||
ServiceContainer.Resolve<IAuthService>("authService").Init();
|
||||
(ServiceContainer.
|
||||
Resolve<IPlatformUtilsService>("platformUtilsService") as MobilePlatformUtilsService).Init();
|
||||
Resolve<IPlatformUtilsService>("platformUtilsService") as MobilePlatformUtilsService)?.Init();
|
||||
|
||||
var accountsManager = new AccountsManager(
|
||||
ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"),
|
||||
@@ -231,20 +239,31 @@ namespace Bit.iOS.Core.Utilities
|
||||
if (message.Command == "showDialog")
|
||||
{
|
||||
var details = message.Data as DialogDetails;
|
||||
if (details is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
|
||||
AppResources.Ok : details.ConfirmText;
|
||||
|
||||
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
var result = await deviceActionService.DisplayAlertAsync(details.Title, details.Text,
|
||||
details.CancelText, confirmText);
|
||||
var confirmed = result == details.ConfirmText;
|
||||
messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
||||
try
|
||||
{
|
||||
var result = await deviceActionService.DisplayAlertAsync(details.Title, details.Text,
|
||||
details.CancelText, confirmText);
|
||||
var confirmed = result == details.ConfirmText;
|
||||
messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (message.Command == "listenYubiKeyOTP")
|
||||
else if (message.Command == "listenYubiKeyOTP" && message.Data is bool listen)
|
||||
{
|
||||
ListenYubiKey((bool)message.Data, deviceActionService, nfcSession, nfcDelegate);
|
||||
ListenYubiKey(listen, deviceActionService, nfcSession, nfcDelegate);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -268,29 +287,36 @@ namespace Bit.iOS.Core.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task BootstrapAsync(Func<Task> postBootstrapFunc = null)
|
||||
private static async Task BootstrapAsync(Func<Task>? postBootstrapFunc = null)
|
||||
{
|
||||
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
||||
|
||||
InitializeAppSetup();
|
||||
// TODO: Update when https://github.com/bitwarden/mobile/pull/1662 gets merged
|
||||
var deleteAccountActionFlowExecutioner = new DeleteAccountActionFlowExecutioner(
|
||||
ServiceContainer.Resolve<IApiService>("apiService"),
|
||||
ServiceContainer.Resolve<IMessagingService>("messagingService"),
|
||||
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||
ServiceContainer.Resolve<ILogger>("logger"));
|
||||
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
||||
|
||||
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
||||
ServiceContainer.Resolve<IUserVerificationService>());
|
||||
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
|
||||
|
||||
if (postBootstrapFunc != null)
|
||||
try
|
||||
{
|
||||
await postBootstrapFunc.Invoke();
|
||||
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
||||
|
||||
InitializeAppSetup();
|
||||
// TODO: Update when https://github.com/bitwarden/mobile/pull/1662 gets merged
|
||||
var deleteAccountActionFlowExecutioner = new DeleteAccountActionFlowExecutioner(
|
||||
ServiceContainer.Resolve<IApiService>("apiService"),
|
||||
ServiceContainer.Resolve<IMessagingService>("messagingService"),
|
||||
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||
ServiceContainer.Resolve<ILogger>("logger"));
|
||||
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
||||
|
||||
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
||||
ServiceContainer.Resolve<IUserVerificationService>());
|
||||
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
|
||||
|
||||
if (postBootstrapFunc != null)
|
||||
{
|
||||
await postBootstrapFunc.Invoke();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user