mirror of
https://github.com/bitwarden/mobile.git
synced 2024-09-28 03:57:43 +02:00
Merge branch 'feature/maui-migration' into feature/maui-migration-passkeys
# Conflicts: # src/App/Platforms/iOS/AppDelegate.cs
This commit is contained in:
commit
14f845d623
12
.github/CODEOWNERS
vendored
12
.github/CODEOWNERS
vendored
@ -1,12 +1,14 @@
|
||||
# Please sort lines alphabetically, this will ensure we don't accidentally add duplicates.
|
||||
# Please sort into logical groups with comment headers. Sort groups in order of specificity.
|
||||
# For example, default owners should always be the first group.
|
||||
# Sort lines alphabetically within these groups to avoid accidentally adding duplicates.
|
||||
#
|
||||
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||
|
||||
# The following owners will be the default owners for everything in the repo.
|
||||
# Unless a later match takes precedence
|
||||
# @bitwarden/tech-leads
|
||||
# Default file owners
|
||||
* @bitwarden/dept-development-mobile
|
||||
|
||||
@bitwarden/dept-development-mobile
|
||||
# DevOps for Actions and other workflow changes
|
||||
.github/workflows @bitwarden/dept-devops
|
||||
|
||||
## Auth team files ##
|
||||
|
||||
|
16
.github/renovate.json
vendored
16
.github/renovate.json
vendored
@ -8,16 +8,14 @@
|
||||
":pinAllExceptPeerDependencies",
|
||||
":prConcurrentLimit10",
|
||||
":rebaseStalePrs",
|
||||
"schedule:weekends",
|
||||
":separateMajorReleases"
|
||||
":separateMajorReleases",
|
||||
"group:monorepos",
|
||||
"schedule:weekends"
|
||||
],
|
||||
"enabledManagers": ["cargo", "github-actions", "npm", "nuget"],
|
||||
"enabledManagers": ["github-actions", "npm", "nuget"],
|
||||
"commitMessagePrefix": "[deps]:",
|
||||
"commitMessageTopic": "{{depName}}",
|
||||
"packageRules": [
|
||||
{
|
||||
"groupName": "cargo minor",
|
||||
"matchManagers": ["cargo"],
|
||||
"matchUpdateTypes": ["minor", "patch"]
|
||||
},
|
||||
{
|
||||
"groupName": "gh minor",
|
||||
"matchManagers": ["github-actions"],
|
||||
@ -32,6 +30,6 @@
|
||||
"groupName": "nuget minor",
|
||||
"matchManagers": ["nuget"],
|
||||
"matchUpdateTypes": ["minor", "patch"]
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -495,7 +495,7 @@ jobs:
|
||||
- name: Set XCode version
|
||||
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
|
||||
with:
|
||||
xcode-version: 15.0.1
|
||||
xcode-version: 15.1
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -8,7 +8,7 @@
|
||||
"name": "bitwarden-mobile",
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"gh-pages": "^3.2.3"
|
||||
"gh-pages": "3.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/array-union": {
|
||||
|
@ -6,6 +6,6 @@
|
||||
"clean:l10n": "git push origin --delete l10n_master"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gh-pages": "^3.2.3"
|
||||
"gh-pages": "3.2.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using AndroidX.AppCompat.View.Menu;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Google.Android.Material.BottomNavigation;
|
||||
using Microsoft.Maui.Handlers;
|
||||
@ -90,7 +91,17 @@ namespace Bit.App.Handlers
|
||||
if(e.Item is MenuItemImpl item)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Tab '{item.Title}' was reselected so we'll PopToRoot.");
|
||||
MainThread.BeginInvokeOnMainThread(async () => await _tabbedPage.CurrentPage.Navigation.PopToRootAsync());
|
||||
MainThread.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await _tabbedPage.CurrentPage.Navigation.PopToRootAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ using CoreNFC;
|
||||
using Foundation;
|
||||
using Microsoft.Maui.Platform;
|
||||
using UIKit;
|
||||
using UserNotifications;
|
||||
using WatchConnectivity;
|
||||
|
||||
namespace Bit.iOS
|
||||
@ -41,73 +42,78 @@ namespace Bit.iOS
|
||||
private IStateService _stateService;
|
||||
private IEventService _eventService;
|
||||
|
||||
private LazyResolve<IDeepLinkContext> _deepLinkContext = new LazyResolve<IDeepLinkContext>();
|
||||
private readonly LazyResolve<IDeepLinkContext> _deepLinkContext = new LazyResolve<IDeepLinkContext>();
|
||||
|
||||
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
||||
{
|
||||
InitApp();
|
||||
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||
|
||||
ConnectToWatchIfNeededAsync().FireAndForget();
|
||||
|
||||
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
|
||||
try
|
||||
{
|
||||
try
|
||||
InitApp();
|
||||
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||
|
||||
ConnectToWatchIfNeededAsync().FireAndForget();
|
||||
|
||||
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
|
||||
{
|
||||
if (message.Command == "startEventTimer")
|
||||
try
|
||||
{
|
||||
StartEventTimer();
|
||||
}
|
||||
else if (message.Command == "stopEventTimer")
|
||||
{
|
||||
var task = StopEventTimerAsync();
|
||||
}
|
||||
else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
if (message.Command == "startEventTimer")
|
||||
{
|
||||
iOSCoreHelpers.AppearanceAdjustments();
|
||||
});
|
||||
}
|
||||
else if (message.Command == "listenYubiKeyOTP")
|
||||
{
|
||||
iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
|
||||
}
|
||||
else if (message.Command == "unlocked")
|
||||
{
|
||||
var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
|
||||
Core.Constants.AutofillNeedsIdentityReplacementKey);
|
||||
if (needsAutofillReplacement.GetValueOrDefault())
|
||||
{
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
StartEventTimer();
|
||||
}
|
||||
}
|
||||
else if (message.Command == "showAppExtension")
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => ShowAppExtension((ExtensionPageViewModel)message.Data));
|
||||
}
|
||||
else if (message.Command == "syncCompleted")
|
||||
{
|
||||
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
|
||||
else if (message.Command == "stopEventTimer")
|
||||
{
|
||||
var success = data["successfully"] as bool?;
|
||||
if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
|
||||
var task = StopEventTimerAsync();
|
||||
}
|
||||
else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
iOSCoreHelpers.AppearanceAdjustments();
|
||||
});
|
||||
}
|
||||
else if (message.Command == "listenYubiKeyOTP" && message.Data is bool listen)
|
||||
{
|
||||
iOSCoreHelpers.ListenYubiKey(listen, _deviceActionService, _nfcSession, _nfcDelegate);
|
||||
}
|
||||
else if (message.Command == "unlocked")
|
||||
{
|
||||
var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
|
||||
Core.Constants.AutofillNeedsIdentityReplacementKey);
|
||||
if (needsAutofillReplacement.GetValueOrDefault())
|
||||
{
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (message.Command == "addedCipher" || message.Command == "editedCipher" ||
|
||||
message.Command == "restoredCipher")
|
||||
{
|
||||
if (_deviceActionService.SystemMajorVersion() >= 12)
|
||||
else if (message.Command == "showAppExtension")
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(() => ShowAppExtension((ExtensionPageViewModel)message.Data));
|
||||
}
|
||||
else if (message.Command == "syncCompleted")
|
||||
{
|
||||
if (message.Data is Dictionary<string, object> data && data.TryGetValue("successfully", out var value))
|
||||
{
|
||||
var success = value as bool?;
|
||||
if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
|
||||
{
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (message.Command == "addedCipher" || message.Command == "editedCipher" ||
|
||||
message.Command == "restoredCipher")
|
||||
{
|
||||
if (!UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ASHelpers.IdentitiesSupportIncrementalAsync())
|
||||
{
|
||||
var cipherId = message.Data as string;
|
||||
@ -124,11 +130,13 @@ namespace Bit.iOS
|
||||
}
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher")
|
||||
{
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher")
|
||||
{
|
||||
if (!UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ASHelpers.IdentitiesSupportIncrementalAsync())
|
||||
{
|
||||
var identity = ASHelpers.ToPasswordCredentialIdentity(
|
||||
@ -142,105 +150,145 @@ namespace Bit.iOS
|
||||
}
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
else if (message.Command == "logout")
|
||||
{
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
else if (message.Command == "logout" && UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
await ASCredentialIdentityStore.SharedStore.RemoveAllCredentialIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher")
|
||||
&& UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
}
|
||||
else if (message.Command == AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND)
|
||||
{
|
||||
var timeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
||||
if (timeoutAction == VaultTimeoutAction.Logout && UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
await ASCredentialIdentityStore.SharedStore.RemoveAllCredentialIdentitiesAsync();
|
||||
}
|
||||
else
|
||||
else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher")
|
||||
&& UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
}
|
||||
else if (message.Command == AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND)
|
||||
{
|
||||
var timeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
||||
if (timeoutAction == VaultTimeoutAction.Logout)
|
||||
{
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
await ASCredentialIdentityStore.SharedStore.RemoveAllCredentialIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ASHelpers.ReplaceAllIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
});
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
});
|
||||
|
||||
var finishedLaunching = base.FinishedLaunching(app, options);
|
||||
var finishedLaunching = base.FinishedLaunching(app, options);
|
||||
|
||||
ThemeManager.SetTheme(Microsoft.Maui.Controls.Application.Current.Resources);
|
||||
iOSCoreHelpers.AppearanceAdjustments();
|
||||
ThemeManager.SetTheme(Microsoft.Maui.Controls.Application.Current.Resources);
|
||||
iOSCoreHelpers.AppearanceAdjustments();
|
||||
|
||||
return finishedLaunching;
|
||||
return finishedLaunching;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnResignActivation(UIApplication uiApplication)
|
||||
{
|
||||
if (UIApplication.SharedApplication.KeyWindow is null)
|
||||
try
|
||||
{
|
||||
if (UIApplication.SharedApplication.KeyWindow != null)
|
||||
{
|
||||
var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
||||
{
|
||||
Tag = SPLASH_VIEW_TAG
|
||||
};
|
||||
var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
||||
{
|
||||
BackgroundColor = ThemeManager.GetResourceColor("SplashBackgroundColor").ToPlatform()
|
||||
};
|
||||
var logo = new UIImage(!ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png");
|
||||
var frame = new CGRect(0, 0, 280, 100); //Setting image width to avoid it being larger and getting cropped on smaller devices. This harcoded size should be good even for very small devices.
|
||||
var imageView = new UIImageView(frame)
|
||||
{
|
||||
Image = logo,
|
||||
Center = new CGPoint(view.Center.X, view.Center.Y - 30),
|
||||
ContentMode = UIViewContentMode.ScaleAspectFit
|
||||
};
|
||||
view.AddSubview(backgroundView);
|
||||
view.AddSubview(imageView);
|
||||
UIApplication.SharedApplication.KeyWindow.AddSubview(view);
|
||||
UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view);
|
||||
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
|
||||
}
|
||||
base.OnResignActivation(uiApplication);
|
||||
return;
|
||||
}
|
||||
|
||||
var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Tag = SPLASH_VIEW_TAG
|
||||
};
|
||||
var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
||||
{
|
||||
BackgroundColor = ThemeManager.GetResourceColor("SplashBackgroundColor").ToPlatform()
|
||||
};
|
||||
var logo = new UIImage(!ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png");
|
||||
var frame = new CGRect(0, 0, 280, 100); //Setting image width to avoid it being larger and getting cropped on smaller devices. This harcoded size should be good even for very small devices.
|
||||
var imageView = new UIImageView(frame)
|
||||
{
|
||||
Image = logo,
|
||||
Center = new CGPoint(view.Center.X, view.Center.Y - 30),
|
||||
ContentMode = UIViewContentMode.ScaleAspectFit
|
||||
};
|
||||
view.AddSubview(backgroundView);
|
||||
view.AddSubview(imageView);
|
||||
UIApplication.SharedApplication.KeyWindow.AddSubview(view);
|
||||
UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view);
|
||||
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
|
||||
|
||||
base.OnResignActivation(uiApplication);
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void DidEnterBackground(UIApplication uiApplication)
|
||||
{
|
||||
if (_stateService != null && _deviceActionService != null)
|
||||
try
|
||||
{
|
||||
_stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
if (_stateService != null && _deviceActionService != null)
|
||||
{
|
||||
_stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
|
||||
_messagingService?.Send("slept");
|
||||
base.DidEnterBackground(uiApplication);
|
||||
_messagingService?.Send("slept");
|
||||
base.DidEnterBackground(uiApplication);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnActivated(UIApplication uiApplication)
|
||||
public override async void OnActivated(UIApplication uiApplication)
|
||||
{
|
||||
base.OnActivated(uiApplication);
|
||||
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
|
||||
UIApplication.SharedApplication.KeyWindow?
|
||||
.ViewWithTag(SPLASH_VIEW_TAG)?
|
||||
.RemoveFromSuperview();
|
||||
try
|
||||
{
|
||||
base.OnActivated(uiApplication);
|
||||
|
||||
ThemeManager.UpdateThemeOnPagesAsync();
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
|
||||
{
|
||||
await UNUserNotificationCenter.Current.SetBadgeCountAsync(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
|
||||
}
|
||||
|
||||
UIApplication.SharedApplication.KeyWindow?
|
||||
.ViewWithTag(SPLASH_VIEW_TAG)?
|
||||
.RemoveFromSuperview();
|
||||
|
||||
ThemeManager.UpdateThemeOnPagesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WillEnterForeground(UIApplication uiApplication)
|
||||
{
|
||||
_messagingService?.Send(AppHelpers.RESUMED_MESSAGE_COMMAND);
|
||||
base.WillEnterForeground(uiApplication);
|
||||
try
|
||||
{
|
||||
_messagingService?.Send(AppHelpers.RESUMED_MESSAGE_COMMAND);
|
||||
base.WillEnterForeground(uiApplication);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[Export("application:openURL:sourceApplication:annotation:")]
|
||||
@ -251,15 +299,30 @@ namespace Bit.iOS
|
||||
|
||||
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
|
||||
{
|
||||
return _deepLinkContext.Value.OnNewUri(url) || base.OpenUrl(app, url, options);
|
||||
try
|
||||
{
|
||||
return _deepLinkContext.Value.OnNewUri(url) || base.OpenUrl(app, url, options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity,
|
||||
UIApplicationRestorationHandler completionHandler)
|
||||
{
|
||||
if (Microsoft.Maui.ApplicationModel.Platform.ContinueUserActivity(application, userActivity, completionHandler))
|
||||
try
|
||||
{
|
||||
return true;
|
||||
if (Microsoft.Maui.ApplicationModel.Platform.ContinueUserActivity(application, userActivity, completionHandler))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
return base.ContinueUserActivity(application, userActivity, completionHandler);
|
||||
}
|
||||
@ -267,33 +330,68 @@ namespace Bit.iOS
|
||||
[Export("application:didFailToRegisterForRemoteNotificationsWithError:")]
|
||||
public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
|
||||
{
|
||||
_pushHandler?.OnErrorReceived(error);
|
||||
try
|
||||
{
|
||||
_pushHandler?.OnErrorReceived(error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]
|
||||
public void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
|
||||
{
|
||||
_pushHandler?.OnRegisteredSuccess(deviceToken);
|
||||
try
|
||||
{
|
||||
_pushHandler?.OnRegisteredSuccess(deviceToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[Export("application:didRegisterUserNotificationSettings:")]
|
||||
public void DidRegisterUserNotificationSettings(UIApplication application,
|
||||
UIUserNotificationSettings notificationSettings)
|
||||
{
|
||||
application.RegisterForRemoteNotifications();
|
||||
try
|
||||
{
|
||||
application.RegisterForRemoteNotifications();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]
|
||||
public void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
|
||||
Action<UIBackgroundFetchResult> completionHandler)
|
||||
{
|
||||
_pushHandler?.OnMessageReceived(userInfo);
|
||||
try
|
||||
{
|
||||
_pushHandler?.OnMessageReceived(userInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[Export("application:didReceiveRemoteNotification:")]
|
||||
public void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
|
||||
{
|
||||
_pushHandler?.OnMessageReceived(userInfo);
|
||||
try
|
||||
{
|
||||
_pushHandler?.OnMessageReceived(userInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitApp()
|
||||
@ -319,7 +417,7 @@ namespace Bit.iOS
|
||||
_nfcDelegate = new Core.NFCReaderDelegate((success, message) =>
|
||||
_messagingService.Send("gotYubiKeyOTP", message));
|
||||
|
||||
iOSCoreHelpers.Bootstrap(async () => await ApplyManagedSettingsAsync());
|
||||
iOSCoreHelpers.Bootstrap(ApplyManagedSettingsAsync);
|
||||
}
|
||||
|
||||
private void RegisterPush()
|
||||
@ -364,31 +462,45 @@ namespace Bit.iOS
|
||||
_eventTimer = null;
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
_eventTimer = NSTimer.CreateScheduledTimer(60, true, timer =>
|
||||
try
|
||||
{
|
||||
var task = Task.Run(() => _eventService.UploadEventsAsync());
|
||||
});
|
||||
_eventTimer = NSTimer.CreateScheduledTimer(60, true, timer =>
|
||||
{
|
||||
_eventService?.UploadEventsAsync().FireAndForget();
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task StopEventTimerAsync()
|
||||
{
|
||||
_eventTimer?.Invalidate();
|
||||
_eventTimer?.Dispose();
|
||||
_eventTimer = null;
|
||||
if (_eventBackgroundTaskId > 0)
|
||||
try
|
||||
{
|
||||
_eventTimer?.Invalidate();
|
||||
_eventTimer?.Dispose();
|
||||
_eventTimer = null;
|
||||
if (_eventBackgroundTaskId > 0)
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
}
|
||||
_eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
});
|
||||
await _eventService.UploadEventsAsync();
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
}
|
||||
_eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
||||
catch (Exception ex)
|
||||
{
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
});
|
||||
await _eventService.UploadEventsAsync();
|
||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||
_eventBackgroundTaskId = 0;
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyManagedSettingsAsync()
|
||||
|
@ -81,7 +81,7 @@ namespace Bit.App
|
||||
{
|
||||
get
|
||||
{
|
||||
return Application.Current.Windows.OfType<ResumeWindow>().FirstOrDefault(w => w.IsActive);
|
||||
return Application.Current?.Windows.OfType<ResumeWindow>().FirstOrDefault(w => w.IsActive);
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,11 +145,14 @@ namespace Bit.App
|
||||
{
|
||||
get
|
||||
{
|
||||
return Application.Current.MainPage;
|
||||
return Application.Current?.MainPage;
|
||||
}
|
||||
set
|
||||
{
|
||||
Application.Current.MainPage = value;
|
||||
if (Application.Current != null)
|
||||
{
|
||||
Application.Current.MainPage = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -153,7 +153,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task StartEnvironmentAsync()
|
||||
{
|
||||
await _accountListOverlay.HideAsync();
|
||||
await _accountListOverlay.HideAsync();
|
||||
var page = new EnvironmentPage();
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (message.Command == Constants.ClearSensitiveFields)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields);
|
||||
MainThread.BeginInvokeOnMainThread(() => _vm?.ResetPinPasswordFields());
|
||||
}
|
||||
});
|
||||
if (_appeared)
|
||||
|
@ -245,9 +245,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task SubmitAsync()
|
||||
{
|
||||
ShowPassword = false;
|
||||
try
|
||||
{
|
||||
ShowPassword = false;
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
if (PinEnabled)
|
||||
{
|
||||
@ -257,12 +257,15 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await UnlockWithMasterPasswordAsync(kdfConfig);
|
||||
}
|
||||
|
||||
}
|
||||
catch (LegacyUserException)
|
||||
{
|
||||
await HandleLegacyUserAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UnlockWithPinAsync(KdfConfig kdfConfig)
|
||||
|
@ -3,6 +3,7 @@ using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@ -74,7 +75,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (message.Command == Constants.ClearSensitiveFields)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(_vm.ResetPasswordField);
|
||||
MainThread.BeginInvokeOnMainThread(() => _vm?.ResetPasswordField());
|
||||
}
|
||||
});
|
||||
_mainContent.Content = _mainLayout;
|
||||
@ -188,12 +189,20 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task LogInSuccessAsync()
|
||||
{
|
||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||
try
|
||||
{
|
||||
return;
|
||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
throw;
|
||||
}
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
|
||||
private async Task UpdateTempPasswordAsync()
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@ -48,12 +49,20 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task LogInSuccessAsync()
|
||||
{
|
||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||
try
|
||||
{
|
||||
return;
|
||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
throw;
|
||||
}
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
|
||||
private async Task UpdateTempPasswordAsync()
|
||||
|
@ -77,6 +77,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_requestTimeCts?.Cancel();
|
||||
_requestTimeCts?.Dispose();
|
||||
_requestTimeCts = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@ -89,16 +90,30 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task StartTwoFactorAsync()
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
var page = new TwoFactorPage(true, _appOptions, _vm.OrgIdentifier);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
try
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
var page = new TwoFactorPage(true, _appOptions, _vm.OrgIdentifier);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartSetPasswordAsync()
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
try
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateTempPasswordAsync()
|
||||
@ -115,16 +130,23 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task SsoAuthSuccessAsync()
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
await AppHelpers.ClearPreviousPage();
|
||||
try
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
await AppHelpers.ClearPreviousPage();
|
||||
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
App.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
App.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||
}
|
||||
else
|
||||
{
|
||||
App.MainPage = new TabsPage(_appOptions, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.MainPage = new TabsPage(_appOptions, null);
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Services;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@ -64,12 +65,19 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task SetPasswordSuccessAsync()
|
||||
{
|
||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||
try
|
||||
{
|
||||
return;
|
||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@ -63,11 +64,11 @@ namespace Bit.App.Pages
|
||||
if (_vm.YubikeyMethod && !string.IsNullOrWhiteSpace(token) &&
|
||||
token.Length == 44 && !token.Contains(" "))
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(async () =>
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
_vm.Token = token;
|
||||
await _vm.SubmitAsync();
|
||||
});
|
||||
_vm.SubmitCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
else if (message.Command == "resumeYubiKey")
|
||||
@ -124,12 +125,9 @@ namespace Bit.App.Pages
|
||||
return base.OnBackButtonPressed();
|
||||
}
|
||||
|
||||
private async void Continue_Clicked(object sender, EventArgs e)
|
||||
private void Continue_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
await _vm.SubmitAsync();
|
||||
}
|
||||
_vm.SubmitCommand.Execute(null);
|
||||
}
|
||||
|
||||
private async void Methods_Clicked(object sender, EventArgs e)
|
||||
@ -158,17 +156,24 @@ namespace Bit.App.Pages
|
||||
|
||||
private async void TryAgain_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
try
|
||||
{
|
||||
if (_vm.Fido2Method)
|
||||
if (DoOnce())
|
||||
{
|
||||
await _vm.Fido2AuthenticateAsync();
|
||||
}
|
||||
else if (_vm.YubikeyMethod)
|
||||
{
|
||||
_messagingService.Send("listenYubiKeyOTP", true);
|
||||
if (_vm.Fido2Method)
|
||||
{
|
||||
await _vm.Fido2AuthenticateAsync();
|
||||
}
|
||||
else if (_vm.YubikeyMethod)
|
||||
{
|
||||
_messagingService.Send("listenYubiKeyOTP", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartSetPasswordAsync()
|
||||
|
@ -1,25 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Microsoft.Maui.Authentication;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class TwoFactorPageViewModel : CaptchaProtectedViewModel
|
||||
@ -62,7 +53,7 @@ namespace Bit.App.Pages
|
||||
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||
|
||||
PageTitle = AppResources.TwoStepLogin;
|
||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||
SubmitCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(async () => await SubmitAsync()), allowsMultipleExecutions: false);
|
||||
MoreCommand = CreateDefaultAsyncRelayCommand(MoreAsync, onException: _logger.Exception, allowsMultipleExecutions: false);
|
||||
}
|
||||
|
||||
@ -91,8 +82,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public bool TotpMethod => AuthenticatorMethod || EmailMethod;
|
||||
|
||||
public bool ShowTryAgain => (YubikeyMethod && // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
Device.RuntimePlatform == Device.iOS) || Fido2Method;
|
||||
public bool ShowTryAgain => (YubikeyMethod && DeviceInfo.Platform == DevicePlatform.iOS) || Fido2Method;
|
||||
|
||||
public bool ShowContinue
|
||||
{
|
||||
@ -106,9 +96,11 @@ Device.RuntimePlatform == Device.iOS) || Fido2Method;
|
||||
set => SetProperty(ref _enableContinue, value);
|
||||
}
|
||||
|
||||
public string YubikeyInstruction => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
|
||||
Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
||||
AppResources.YubiKeyInstruction;
|
||||
#if IOS
|
||||
public string YubikeyInstruction => AppResources.YubiKeyInstructionIos;
|
||||
#else
|
||||
public string YubikeyInstruction => AppResources.YubiKeyInstruction;
|
||||
#endif
|
||||
|
||||
public TwoFactorProviderType? SelectedProviderType
|
||||
{
|
||||
@ -124,7 +116,7 @@ Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
||||
nameof(ShowTryAgain),
|
||||
});
|
||||
}
|
||||
public Command SubmitCommand { get; }
|
||||
public ICommand SubmitCommand { get; }
|
||||
public ICommand MoreCommand { get; }
|
||||
public Action TwoFactorAuthSuccessAction { get; set; }
|
||||
public Action LockAction { get; set; }
|
||||
@ -186,7 +178,7 @@ Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
||||
page.DuoWebView.RegisterAction(sig =>
|
||||
{
|
||||
Token = sig;
|
||||
Device.BeginInvokeOnMainThread(async () => await SubmitAsync());
|
||||
SubmitCommand.Execute(null);
|
||||
});
|
||||
break;
|
||||
case TwoFactorProviderType.Email:
|
||||
@ -213,68 +205,76 @@ Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
||||
|
||||
public async Task Fido2AuthenticateAsync(Dictionary<string, object> providerData = null)
|
||||
{
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
||||
|
||||
if (providerData == null)
|
||||
{
|
||||
providerData = _authService.TwoFactorProvidersData[TwoFactorProviderType.Fido2WebAuthn];
|
||||
}
|
||||
|
||||
var callbackUri = "bitwarden://webauthn-callback";
|
||||
var data = AppHelpers.EncodeDataParameter(new
|
||||
{
|
||||
callbackUri = callbackUri,
|
||||
data = JsonConvert.SerializeObject(providerData),
|
||||
headerText = AppResources.Fido2Title,
|
||||
btnText = AppResources.Fido2AuthenticateWebAuthn,
|
||||
btnReturnText = AppResources.Fido2ReturnToApp,
|
||||
});
|
||||
|
||||
var url = _webVaultUrl + "/webauthn-mobile-connector.html?" + "data=" + data +
|
||||
"&parent=" + Uri.EscapeDataString(callbackUri) + "&v=2";
|
||||
|
||||
WebAuthenticatorResult authResult = null;
|
||||
try
|
||||
{
|
||||
var options = new WebAuthenticatorOptions
|
||||
{
|
||||
Url = new Uri(url),
|
||||
CallbackUrl = new Uri(callbackUri),
|
||||
PrefersEphemeralWebBrowserSession = true,
|
||||
};
|
||||
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// user canceled
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
return;
|
||||
}
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
||||
|
||||
string response = null;
|
||||
if (authResult != null && authResult.Properties.TryGetValue("data", out var resultData))
|
||||
{
|
||||
response = Uri.UnescapeDataString(resultData);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(response))
|
||||
{
|
||||
Token = response;
|
||||
await SubmitAsync(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
if (authResult != null && authResult.Properties.TryGetValue("error", out var resultError))
|
||||
if (providerData == null)
|
||||
{
|
||||
var message = AppResources.Fido2CheckBrowser + "\n\n" + resultError;
|
||||
await _platformUtilsService.ShowDialogAsync(message, AppResources.AnErrorHasOccurred,
|
||||
AppResources.Ok);
|
||||
providerData = _authService.TwoFactorProvidersData[TwoFactorProviderType.Fido2WebAuthn];
|
||||
}
|
||||
|
||||
var callbackUri = "bitwarden://webauthn-callback";
|
||||
var data = AppHelpers.EncodeDataParameter(new
|
||||
{
|
||||
callbackUri = callbackUri,
|
||||
data = JsonConvert.SerializeObject(providerData),
|
||||
headerText = AppResources.Fido2Title,
|
||||
btnText = AppResources.Fido2AuthenticateWebAuthn,
|
||||
btnReturnText = AppResources.Fido2ReturnToApp,
|
||||
});
|
||||
|
||||
var url = _webVaultUrl + "/webauthn-mobile-connector.html?" + "data=" + data +
|
||||
"&parent=" + Uri.EscapeDataString(callbackUri) + "&v=2";
|
||||
|
||||
WebAuthenticatorResult authResult = null;
|
||||
try
|
||||
{
|
||||
var options = new WebAuthenticatorOptions
|
||||
{
|
||||
Url = new Uri(url),
|
||||
CallbackUrl = new Uri(callbackUri),
|
||||
PrefersEphemeralWebBrowserSession = true,
|
||||
};
|
||||
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// user canceled
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
string response = null;
|
||||
if (authResult != null && authResult.Properties.TryGetValue("data", out var resultData))
|
||||
{
|
||||
response = Uri.UnescapeDataString(resultData);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(response))
|
||||
{
|
||||
Token = response;
|
||||
await SubmitAsync(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.Fido2CheckBrowser,
|
||||
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
if (authResult != null && authResult.Properties.TryGetValue("error", out var resultError))
|
||||
{
|
||||
var message = AppResources.Fido2CheckBrowser + "\n\n" + resultError;
|
||||
await _platformUtilsService.ShowDialogAsync(message, AppResources.AnErrorHasOccurred,
|
||||
AppResources.Ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.Fido2CheckBrowser,
|
||||
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,8 +170,15 @@ namespace Bit.App.Pages
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(ShowModalAnimationDelay);
|
||||
MainThread.BeginInvokeOnMainThread(() => input.Focus());
|
||||
try
|
||||
{
|
||||
await Task.Delay(ShowModalAnimationDelay);
|
||||
MainThread.BeginInvokeOnMainThread(() => input.Focus());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Value.Exception(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,9 @@
|
||||
Camera="{Binding Camera}"
|
||||
AutoStartPreview="{Binding AutoStartPreview}"
|
||||
NumCamerasDetected="{Binding NumCameras, Mode=OneWayToSource}"
|
||||
WidthRequest="{OnPlatform Android=150}"
|
||||
HeightRequest="{OnPlatform Android=150}"
|
||||
Scale="{OnPlatform Android=4}"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3" />
|
||||
|
@ -156,7 +156,7 @@
|
||||
<comment>The button text that allows a user to copy the login's password to their clipboard.</comment>
|
||||
</data>
|
||||
<data name="CopyUsername" xml:space="preserve">
|
||||
<value>İstifadəçi adını kopyalayın</value>
|
||||
<value>İstifadəçi adını kopyala</value>
|
||||
<comment>The button text that allows a user to copy the login's username to their clipboard.</comment>
|
||||
</data>
|
||||
<data name="Credits" xml:space="preserve">
|
||||
@ -176,7 +176,7 @@
|
||||
<comment>Confirmation alert message when deleteing something.</comment>
|
||||
</data>
|
||||
<data name="Edit" xml:space="preserve">
|
||||
<value>Redaktə edin</value>
|
||||
<value>Düzəliş et</value>
|
||||
</data>
|
||||
<data name="EditFolder" xml:space="preserve">
|
||||
<value>Qovluğa düzəliş et</value>
|
||||
|
@ -265,7 +265,7 @@
|
||||
<comment>The login button text (verb).</comment>
|
||||
</data>
|
||||
<data name="LogInNoun" xml:space="preserve">
|
||||
<value>Login</value>
|
||||
<value>Mewngofnodi</value>
|
||||
<comment>Title for login page. (noun)</comment>
|
||||
</data>
|
||||
<data name="LogOut" xml:space="preserve">
|
||||
@ -308,7 +308,7 @@
|
||||
<comment>Label for an entity name.</comment>
|
||||
</data>
|
||||
<data name="No" xml:space="preserve">
|
||||
<value>No</value>
|
||||
<value>Na</value>
|
||||
</data>
|
||||
<data name="Notes" xml:space="preserve">
|
||||
<value>Nodiadau</value>
|
||||
@ -327,7 +327,7 @@
|
||||
<comment>Button text for a save operation (verb).</comment>
|
||||
</data>
|
||||
<data name="Move" xml:space="preserve">
|
||||
<value>Move</value>
|
||||
<value>Symud</value>
|
||||
</data>
|
||||
<data name="Saving" xml:space="preserve">
|
||||
<value>Yn cadw...</value>
|
||||
@ -391,7 +391,7 @@
|
||||
<value>Fersiwn</value>
|
||||
</data>
|
||||
<data name="View" xml:space="preserve">
|
||||
<value>View</value>
|
||||
<value>Golwg</value>
|
||||
</data>
|
||||
<data name="VisitOurWebsite" xml:space="preserve">
|
||||
<value>Visit our website</value>
|
||||
@ -401,7 +401,7 @@
|
||||
<comment>Label for a website.</comment>
|
||||
</data>
|
||||
<data name="Yes" xml:space="preserve">
|
||||
<value>Yes</value>
|
||||
<value>Ydw</value>
|
||||
</data>
|
||||
<data name="Account" xml:space="preserve">
|
||||
<value>Cyfrif</value>
|
||||
@ -715,7 +715,7 @@
|
||||
<value>Verification code</value>
|
||||
</data>
|
||||
<data name="ViewItem" xml:space="preserve">
|
||||
<value>View item</value>
|
||||
<value>Gweld yr Eitem</value>
|
||||
</data>
|
||||
<data name="WebVault" xml:space="preserve">
|
||||
<value>Cell we Bitwarden</value>
|
||||
|
@ -2862,12 +2862,12 @@
|
||||
<value>帳戶已登出。</value>
|
||||
</data>
|
||||
<data name="YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" xml:space="preserve">
|
||||
<value>Your organization permissions were updated, requiring you to set a master password.</value>
|
||||
<value>您的組織權限已更新,需要您設定主密碼。</value>
|
||||
</data>
|
||||
<data name="YourOrganizationRequiresYouToSetAMasterPassword" xml:space="preserve">
|
||||
<value>Your organization requires you to set a master password.</value>
|
||||
<value>您的組織要求您設定主密碼。</value>
|
||||
</data>
|
||||
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
|
||||
<value>Set up an unlock option to change your vault timeout action.</value>
|
||||
<value>設定一個解鎖方式來變更您的密碼庫逾時動作。</value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui;
|
||||
using Bit.Core.Abstractions;
|
||||
|
||||
namespace Bit.App.Utilities
|
||||
{
|
||||
@ -37,7 +32,7 @@ namespace Bit.App.Utilities
|
||||
{
|
||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(async () =>
|
||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
if (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Pages;
|
||||
using Bit.App.Pages;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
@ -13,7 +12,7 @@ namespace Bit.iOS.Core.Handlers
|
||||
public partial class CustomTabbedHandler : TabbedRenderer
|
||||
{
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private UITabBarItem _previousSelectedItem;
|
||||
private UITabBarItem? _previousSelectedItem;
|
||||
|
||||
public CustomTabbedHandler()
|
||||
{
|
||||
@ -73,8 +72,7 @@ namespace Bit.iOS.Core.Handlers
|
||||
private void UpdateTabBarAppearance()
|
||||
{
|
||||
// https://developer.apple.com/forums/thread/682420
|
||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
if (deviceActionService.SystemMajorVersion() >= 15)
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(15,0))
|
||||
{
|
||||
var appearance = new UITabBarAppearance();
|
||||
appearance.ConfigureWithOpaqueBackground();
|
||||
|
@ -1,24 +0,0 @@
|
||||
using Microsoft.Maui.Handlers;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS.Core.Handlers
|
||||
{
|
||||
public class CustomWindowHandler : ElementHandler<IWindow, UIWindow>, IWindowHandler
|
||||
{
|
||||
public static IPropertyMapper<IWindow, IWindowHandler> Mapper = new PropertyMapper<IWindow, IWindowHandler>(ElementHandler.ElementMapper)
|
||||
{
|
||||
};
|
||||
|
||||
public CustomWindowHandler() : base(Mapper)
|
||||
{
|
||||
}
|
||||
|
||||
protected override UIWindow CreatePlatformElement()
|
||||
{
|
||||
// Haven't tested
|
||||
//return UIApplication.SharedApplication.Delegate.GetWindow();
|
||||
return Platform.GetCurrentUIViewController().View.Window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
@ -20,7 +19,7 @@ namespace Bit.iOS.Core.Services
|
||||
}
|
||||
|
||||
// This gets called a lot - try/catch can be expensive so consider caching or something
|
||||
CultureInfo ci = null;
|
||||
CultureInfo? ci;
|
||||
try
|
||||
{
|
||||
ci = new CultureInfo(netLanguage);
|
||||
@ -108,7 +107,7 @@ namespace Bit.iOS.Core.Services
|
||||
{
|
||||
df.Locale = NSLocale.CurrentLocale;
|
||||
df.DateStyle = NSDateFormatterStyle.Short;
|
||||
return df.StringFor((NSDate)date);
|
||||
return df.StringFor((NSDate?)date);
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +117,7 @@ namespace Bit.iOS.Core.Services
|
||||
{
|
||||
df.Locale = NSLocale.CurrentLocale;
|
||||
df.TimeStyle = NSDateFormatterStyle.Short;
|
||||
return df.StringFor((NSDate)time);
|
||||
return df.StringFor((NSDate?)time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Services;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
|
||||
@ -9,7 +7,7 @@ namespace Bit.iOS.Core.Views
|
||||
public class ExtensionSearchDelegate : UISearchBarDelegate
|
||||
{
|
||||
private readonly UITableView _tableView;
|
||||
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
||||
private CancellationTokenSource? _filterResultsCancellationTokenSource;
|
||||
|
||||
public ExtensionSearchDelegate(UITableView tableView)
|
||||
{
|
||||
@ -23,25 +21,34 @@ namespace Bit.iOS.Core.Views
|
||||
{
|
||||
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(searchText))
|
||||
{
|
||||
await Task.Delay(300);
|
||||
if (searchText != searchBar.Text)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_filterResultsCancellationTokenSource?.Cancel();
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
((ExtensionTableSource)_tableView.Source).FilterResults(searchText, cts.Token);
|
||||
_tableView.ReloadData();
|
||||
if (!string.IsNullOrWhiteSpace(searchText))
|
||||
{
|
||||
await Task.Delay(300);
|
||||
if (searchText != searchBar.Text)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_filterResultsCancellationTokenSource?.Cancel();
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
((ExtensionTableSource)_tableView.Source).FilterResults(searchText, cts.Token);
|
||||
_tableView.ReloadData();
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
_filterResultsCancellationTokenSource = cts;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
_filterResultsCancellationTokenSource?.Cancel();
|
||||
cts?.Cancel();
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
_filterResultsCancellationTokenSource = cts;
|
||||
});
|
||||
}, cts.Token);
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
#if ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Bit.iOS.Core.Utilities;
|
||||
using Bit.iOS.Extension.Models;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Platform;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS.Extension
|
||||
{
|
||||
public partial class LoadingViewController : UIViewController
|
||||
{
|
||||
const string STORYBOARD_NAME = "MainInterface";
|
||||
Lazy<UIStoryboard> _storyboard = new Lazy<UIStoryboard>(() => UIStoryboard.FromName(STORYBOARD_NAME, null));
|
||||
|
||||
public void InitWithContext(Context context)
|
||||
{
|
||||
_context = context;
|
||||
_shouldInitialize = false;
|
||||
}
|
||||
|
||||
public void DismissLockAndContinue()
|
||||
{
|
||||
if (UIApplication.SharedApplication.KeyWindow is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UIApplication.SharedApplication.KeyWindow.RootViewController = _storyboard.Value.InstantiateInitialViewController();
|
||||
|
||||
if (UIApplication.SharedApplication.KeyWindow?.RootViewController is UINavigationController navContr)
|
||||
{
|
||||
var rootVC = navContr.ViewControllers.FirstOrDefault();
|
||||
if (rootVC is LoadingViewController loadingVC)
|
||||
{
|
||||
loadingVC.InitWithContext(_context);
|
||||
loadingVC.ContinueOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigateToPage(ContentPage page)
|
||||
{
|
||||
var navigationPage = new NavigationPage(page);
|
||||
|
||||
var window = new Window(navigationPage);
|
||||
window.ToHandler(MauiContextSingleton.Instance.MauiContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -18,6 +18,8 @@ using Bit.iOS.Core.Views;
|
||||
using Bit.iOS.Extension.Models;
|
||||
using CoreNFC;
|
||||
using Foundation;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Platform;
|
||||
using MobileCoreServices;
|
||||
using UIKit;
|
||||
|
||||
@ -151,7 +153,6 @@ namespace Bit.iOS.Extension
|
||||
}
|
||||
}
|
||||
|
||||
#if !ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
|
||||
public void DismissLockAndContinue()
|
||||
{
|
||||
Debug.WriteLine("BW Log, Dismissing lock controller.");
|
||||
@ -166,7 +167,6 @@ namespace Bit.iOS.Extension
|
||||
|
||||
PresentViewController(uiController, true, null);
|
||||
}
|
||||
#endif
|
||||
|
||||
private void ContinueOn()
|
||||
{
|
||||
@ -479,16 +479,23 @@ namespace Bit.iOS.Extension
|
||||
{
|
||||
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
if (await IsAuthed())
|
||||
try
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
await AppHelpers.LogOutAsync(await stateService.GetActiveUserIdAsync());
|
||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
if (deviceActionService.SystemMajorVersion() >= 12)
|
||||
if (await IsAuthed())
|
||||
{
|
||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
await AppHelpers.LogOutAsync(await stateService.GetActiveUserIdAsync());
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,6 @@
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
|
||||
|
||||
<DefineConstants>$(DefineConstants);ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND</DefineConstants>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
|
||||
<RootNamespace>Bit.iOS.Extension</RootNamespace>
|
||||
@ -75,7 +73,6 @@
|
||||
</Compile>
|
||||
<BundleResource Include="Resources\MaterialIcons_Regular.ttf" />
|
||||
<BundleResource Include="Resources\bwi-font.ttf" />
|
||||
<Compile Include="LoadingViewController.TapGestureHack.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
|
@ -1,22 +0,0 @@
|
||||
#if ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
|
||||
|
||||
using Bit.iOS.Core.Utilities;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Platform;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS.ShareExtension
|
||||
{
|
||||
public partial class LoadingViewController : UIViewController
|
||||
{
|
||||
private void NavigateToPage(ContentPage page)
|
||||
{
|
||||
var navigationPage = new NavigationPage(page);
|
||||
|
||||
var window = new Window(navigationPage);
|
||||
window.ToHandler(MauiContextSingleton.Instance.MauiContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -137,23 +137,16 @@ namespace Bit.iOS.ShareExtension
|
||||
}
|
||||
}
|
||||
|
||||
#if !ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
|
||||
|
||||
private void NavigateToPage(ContentPage page)
|
||||
{
|
||||
var navigationPage = new NavigationPage(page);
|
||||
|
||||
var window = new Window(navigationPage);
|
||||
window.ToHandler(MauiContextSingleton.Instance.MauiContext);
|
||||
|
||||
_currentModalController = navigationPage.ToUIViewController(MauiContextSingleton.Instance.MauiContext);
|
||||
_currentModalController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||
_presentingOnNavigationPage = true;
|
||||
PresentViewController(_currentModalController, true, null);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public void DismissLockAndContinue()
|
||||
{
|
||||
Debug.WriteLine("BW Log, Dismissing lock controller.");
|
||||
@ -274,14 +267,22 @@ namespace Bit.iOS.ShareExtension
|
||||
{
|
||||
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
if (await IsAuthed())
|
||||
try
|
||||
{
|
||||
await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync());
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
if (await IsAuthed())
|
||||
{
|
||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
||||
await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync());
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||
{
|
||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,6 @@
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
|
||||
|
||||
<DefineConstants>$(DefineConstants);ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND</DefineConstants>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
|
||||
<RootNamespace>Bit.iOS.ShareExtension</RootNamespace>
|
||||
@ -67,7 +65,6 @@
|
||||
<Compile Include="ExtensionNavigationController.designer.cs">
|
||||
<DependentUpon>ExtensionNavigationController.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="LoadingViewController.TapGestureHack.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="..\App\Platforms\iOS\Resources\logo_white%403x.png">
|
||||
|
@ -5,6 +5,6 @@
|
||||
"SyncingItemsContainingVerificationCodes" = "Syncing items containing verification codes";
|
||||
"UnlockBitwardenOnYourIPhoneToViewVerificationCodes" = "Unlock Bitwarden on your iPhone to view verification codes";
|
||||
"SetUpBitwardenToViewItemsContainingVerificationCodes" = "Set up Bitwarden to view items containing verification codes";
|
||||
"Search" = "Search";
|
||||
"Search" = "Chwilio";
|
||||
"NoItemsFound" = "No items found";
|
||||
"SetUpAppleWatchPasscodeInOrderToUseBitwarden" = "Set up Apple Watch passcode in order to use Bitwarden";
|
||||
|
Loading…
Reference in New Issue
Block a user