1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-16 23:43:21 +00:00

PM-3350 Fix iOS extensions navigation and Window/RootViewController handling for TapGestureRecognizer to work

This commit is contained in:
Federico Maccaroni
2023-12-07 17:40:20 -03:00
parent a5888827c9
commit a4a3d31c19
11 changed files with 279 additions and 149 deletions

View File

@@ -0,0 +1,51 @@
#if ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
using System;
using AuthenticationServices;
using Bit.App.Abstractions;
using Bit.Core.Utilities;
using Bit.iOS.Autofill.Models;
using Bit.iOS.Core.Utilities;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using UIKit;
namespace Bit.iOS.Autofill
{
public partial class CredentialProviderViewController : ASCredentialProviderViewController, IAccountsManagerHost
{
const string STORYBOARD_NAME = "MainInterface";
Lazy<UIStoryboard> _storyboard = new Lazy<UIStoryboard>(() => UIStoryboard.FromName(STORYBOARD_NAME, null));
public void InitWithContext(Context context)
{
_context = context;
}
public void DismissLockAndContinue()
{
if (UIApplication.SharedApplication.KeyWindow is null)
{
return;
}
UIApplication.SharedApplication.KeyWindow.RootViewController = _storyboard.Value.InstantiateInitialViewController();
if (UIApplication.SharedApplication.KeyWindow?.RootViewController is CredentialProviderViewController cpvc)
{
cpvc.InitWithContext(_context);
cpvc.OnLockDismissedAsync().FireAndForget();
}
}
private void NavigateToPage(ContentPage page)
{
var navigationPage = new NavigationPage(page);
var window = new Window(navigationPage);
window.ToHandler(MauiContextSingleton.Instance.MauiContext);
}
}
}
#endif

View File

@@ -1,4 +1,5 @@
using System;
using System.Text;
using System.Threading.Tasks;
using AuthenticationServices;
using Bit.App.Abstractions;
@@ -16,8 +17,7 @@ using Bit.iOS.Core.Views;
using CoreFoundation;
using CoreNFC;
using Foundation;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using Microsoft.Maui.ApplicationModel;
using UIKit;
namespace Bit.iOS.Autofill
@@ -29,7 +29,7 @@ namespace Bit.iOS.Autofill
private Core.NFCReaderDelegate _nfcDelegate = null;
private IAccountsManager _accountsManager;
private readonly LazyResolve<IStateService> _stateService = new LazyResolve<IStateService>("stateService");
private readonly LazyResolve<IStateService> _stateService = new LazyResolve<IStateService>();
public CredentialProviderViewController(IntPtr handle)
: base(handle)
@@ -37,11 +37,13 @@ namespace Bit.iOS.Autofill
ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
}
private ASCredentialProviderExtensionContext ASExtensionContext => _context?.ExtContext as ASCredentialProviderExtensionContext;
public override void ViewDidLoad()
{
try
{
InitApp();
InitAppIfNeeded();
base.ViewDidLoad();
@@ -51,6 +53,7 @@ namespace Bit.iOS.Autofill
{
ExtContext = ExtensionContext
};
}
catch (Exception ex)
{
@@ -171,7 +174,7 @@ namespace Bit.iOS.Autofill
if ((_context?.Configuring ?? true) && string.IsNullOrWhiteSpace(password))
{
ServiceContainer.Reset();
ExtensionContext?.CompleteExtensionConfigurationRequest();
ASExtensionContext?.CompleteExtensionConfigurationRequest();
return;
}
@@ -180,7 +183,7 @@ namespace Bit.iOS.Autofill
ServiceContainer.Reset();
var err = new NSError(new NSString("ASExtensionErrorDomain"),
Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null);
NSRunLoop.Main.BeginInvokeOnMainThread(() => ExtensionContext?.CancelRequest(err));
NSRunLoop.Main.BeginInvokeOnMainThread(() => ASExtensionContext?.CancelRequest(err));
return;
}
@@ -198,7 +201,7 @@ namespace Bit.iOS.Autofill
await eventService.CollectAsync(Bit.Core.Enums.EventType.Cipher_ClientAutofilled, id);
}
ServiceContainer.Reset();
ExtensionContext?.CompleteRequest(cred, null);
ASExtensionContext?.CompleteRequest(cred, null);
});
}
@@ -245,38 +248,53 @@ namespace Bit.iOS.Autofill
}
}
public void DismissLockAndContinue()
#if !ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
public async void DismissLockAndContinue()
{
DismissViewController(false, async () =>
{
try
{
if (_context.CredentialIdentity != null)
{
await ProvideCredentialAsync();
return;
}
if (_context.Configuring)
{
PerformSegue("setupSegue", this);
return;
}
ClipLogger.Log("Dismiss lock and continue");
if (_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
{
PerformSegue("loginSearchSegue", this);
}
else
{
PerformSegue("loginListSegue", this);
}
}
catch (Exception ex)
DismissViewController(false, async () => await OnLockDismissedAsync());
}
private void NavigateToPage(ContentPage page)
{
var navigationPage = new NavigationPage(page);
var uiController = navigationPage.ToUIViewController(MauiContextSingleton.Instance.MauiContext);
uiController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
PresentViewController(uiController, true, null);
}
#endif
public async Task OnLockDismissedAsync()
{
try
{
if (_context.CredentialIdentity != null)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
await MainThread.InvokeOnMainThreadAsync(() => ProvideCredentialAsync());
return;
}
});
if (_context.Configuring)
{
await MainThread.InvokeOnMainThreadAsync(() => PerformSegue("setupSegue", this));
return;
}
if (_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
{
await MainThread.InvokeOnMainThreadAsync(() => PerformSegue("loginSearchSegue", this));
}
else
{
await MainThread.InvokeOnMainThreadAsync(() => PerformSegue("loginListSegue", this));
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private async Task ProvideCredentialAsync(bool userInteraction = true)
@@ -407,46 +425,25 @@ namespace Bit.iOS.Autofill
}
}
private void NavigateToPage(ContentPage page)
{
var navigationPage = new NavigationPage(page);
var window = new Window(navigationPage);
window.ToHandler(MauiContextSingleton.Instance.MauiContext);
var uiController = navigationPage.ToUIViewController(MauiContextSingleton.Instance.MauiContext);
uiController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
PresentViewController(uiController, true, null);
}
private void LaunchHomePage()
{
try
var appOptions = new AppOptions { IosExtension = true };
var homePage = new HomePage(appOptions);
var app = new App.App(appOptions);
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesTo(homePage);
if (homePage.BindingContext is HomeViewModel vm)
{
var appOptions = new AppOptions { IosExtension = true };
var homePage = new HomePage(appOptions);
var app = new App.App(appOptions);
ThemeManager.SetTheme(app.Resources);
ThemeManager.ApplyResourcesTo(homePage);
if (homePage.BindingContext is HomeViewModel vm)
{
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
vm.StartRegisterAction = () => DismissViewController(false, () => LaunchRegisterFlow());
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
vm.StartEnvironmentAction = () => DismissViewController(false, () => LaunchEnvironmentFlow());
vm.CloseAction = () => CompleteRequest();
}
NavigateToPage(homePage);
LogoutIfAuthed();
}
catch (Exception ex)
{
UIPasteboard.General.String = ex.ToString();
_labelErr.Text = ex.ToString();
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
vm.StartRegisterAction = () => DismissViewController(false, () => LaunchRegisterFlow());
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
vm.StartEnvironmentAction = () => DismissViewController(false, () => LaunchEnvironmentFlow());
vm.CloseAction = () => CompleteRequest();
}
NavigateToPage(homePage);
LogoutIfAuthed();
}
private void LaunchEnvironmentFlow()

View File

@@ -12,9 +12,6 @@ namespace Bit.iOS.Autofill
[Register ("CredentialProviderViewController")]
partial class CredentialProviderViewController
{
[Outlet]
UIKit.UILabel _labelErr { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIImageView Logo { get; set; }
@@ -25,11 +22,6 @@ namespace Bit.iOS.Autofill
Logo.Dispose ();
Logo = null;
}
if (_labelErr != null) {
_labelErr.Dispose ();
_labelErr = null;
}
}
}
}

View File

@@ -18,28 +18,18 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="logo.png" translatesAutoresizingMaskIntoConstraints="NO" id="1713">
<rect key="frame" x="66" y="676" width="282" height="44"/>
<rect key="frame" x="66" y="396" width="282" height="44"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No Error" textAlignment="natural" lineBreakMode="characterWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HVp-Yu-JIZ">
<rect key="frame" x="5" y="187" width="404" height="26.5"/>
<fontDescription key="fontDescription" type="system" pointSize="22"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="YG6-2d-qpF"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="1713" firstAttribute="centerY" secondItem="44" secondAttribute="centerY" constant="250" id="1763"/>
<constraint firstItem="1713" firstAttribute="centerY" secondItem="44" secondAttribute="centerY" constant="-30" id="1763"/>
<constraint firstItem="1713" firstAttribute="centerX" secondItem="YG6-2d-qpF" secondAttribute="centerX" id="1764"/>
<constraint firstItem="HVp-Yu-JIZ" firstAttribute="top" secondItem="YG6-2d-qpF" secondAttribute="top" constant="139" id="Ay0-v3-kTO"/>
<constraint firstItem="HVp-Yu-JIZ" firstAttribute="trailing" secondItem="YG6-2d-qpF" secondAttribute="trailing" constant="-5" id="TdY-Cu-YFt"/>
<constraint firstItem="YG6-2d-qpF" firstAttribute="leading" secondItem="HVp-Yu-JIZ" secondAttribute="leading" constant="-5" id="lfV-6C-INq"/>
</constraints>
</view>
<connections>
<outlet property="Logo" destination="1713" id="name-outlet-1713"/>
<outlet property="_labelErr" destination="HVp-Yu-JIZ" id="PjY-be-dhX"/>
<segue destination="oCZ-GQ-aOK" kind="show" identifier="loginListSegue" id="1679"/>
<segue destination="6855" kind="presentation" identifier="lockPasswordSegue" id="9874"/>
<segue destination="10580" kind="presentation" identifier="setupSegue" modalTransitionStyle="coverVertical" id="11089"/>

View File

@@ -10,6 +10,8 @@
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<DefineConstants>$(DefineConstants);ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND</DefineConstants>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">12.0</SupportedOSPlatformVersion>
</PropertyGroup>
@@ -78,6 +80,7 @@
<Compile Include="Models\Context.cs" />
<BundleResource Include="Resources\MaterialIcons_Regular.ttf" />
<BundleResource Include="Resources\bwi-font.ttf" />
<Compile Include="CredentialProviderViewController.TapGestureHack.cs" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\check.png" />