1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

PM-7052 WIP fix theme change on iOS Autofill extension (with ClipLogger activated)

This commit is contained in:
Federico Maccaroni
2024-03-26 10:58:09 -03:00
parent 1b3d5e5eb2
commit f312e8c4d2
15 changed files with 306 additions and 88 deletions

View File

@@ -1,66 +1,66 @@
//using System.Runtime.CompilerServices;
//using System.Text;
//using Bit.Core.Abstractions;
using System.Runtime.CompilerServices;
using System.Text;
using Bit.Core.Abstractions;
//#if IOS
//using UIKit;
//#endif
#if IOS
using UIKit;
#endif
//namespace Bit.Core.Services
//{
// /// <summary>
// /// This logger can be used to help debug iOS extensions where we cannot use the .NET debugger yet
// /// so we can use this that copies the logs to the clipboard so one
// /// can paste them and analyze its output.
// /// </summary>
// public class ClipLogger : ILogger
// {
// private static readonly StringBuilder _currentBreadcrumbs = new StringBuilder();
namespace Bit.Core.Services
{
/// <summary>
/// This logger can be used to help debug iOS extensions where we cannot use the .NET debugger yet
/// so we can use this that copies the logs to the clipboard so one
/// can paste them and analyze its output.
/// </summary>
public class ClipLogger : ILogger
{
private static readonly StringBuilder _currentBreadcrumbs = new StringBuilder();
// static ILogger _instance;
// public static ILogger Instance
// {
// get
// {
// if (_instance is null)
// {
// _instance = new ClipLogger();
// }
// return _instance;
// }
// }
static ILogger _instance;
public static ILogger Instance
{
get
{
if (_instance is null)
{
_instance = new ClipLogger();
}
return _instance;
}
}
// protected ClipLogger()
// {
// }
protected ClipLogger()
{
}
// public static void Log(string breadcrumb)
// {
// _currentBreadcrumbs.AppendLine($"{DateTime.Now.ToShortTimeString()}: {breadcrumb}");
//#if IOS
// UIPasteboard.General.String = _currentBreadcrumbs.ToString();
//#endif
// }
public static void Log(string breadcrumb)
{
_currentBreadcrumbs.AppendLine($"{DateTime.Now.ToShortTimeString()}: {breadcrumb}");
#if IOS
MainThread.BeginInvokeOnMainThread(() => UIPasteboard.General.String = _currentBreadcrumbs.ToString());
#endif
}
// public void Error(string message, IDictionary<string, string> extraData = null, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
// {
// var classAndMethod = $"{Path.GetFileNameWithoutExtension(sourceFilePath)}.{memberName}";
// var filePathAndLineNumber = $"{Path.GetFileName(sourceFilePath)}:{sourceLineNumber}";
// var properties = new Dictionary<string, string>
// {
// ["File"] = filePathAndLineNumber,
// ["Method"] = memberName
// };
public void Error(string message, IDictionary<string, string> extraData = null, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
var classAndMethod = $"{Path.GetFileNameWithoutExtension(sourceFilePath)}.{memberName}";
var filePathAndLineNumber = $"{Path.GetFileName(sourceFilePath)}:{sourceLineNumber}";
var properties = new Dictionary<string, string>
{
["File"] = filePathAndLineNumber,
["Method"] = memberName
};
// Log(message ?? $"Error found in: {classAndMethod}, {filePathAndLineNumber}");
// }
Log(message ?? $"Error found in: {classAndMethod}, {filePathAndLineNumber}");
}
// public void Exception(Exception ex) => Log(ex?.ToString());
public void Exception(Exception ex) => Log(ex?.ToString());
// public Task InitAsync() => Task.CompletedTask;
public Task InitAsync() => Task.CompletedTask;
// public Task<bool> IsEnabled() => Task.FromResult(true);
public Task<bool> IsEnabled() => Task.FromResult(true);
// public Task SetEnabled(bool value) => Task.CompletedTask;
// }
//}
public Task SetEnabled(bool value) => Task.CompletedTask;
}
}

View File

@@ -21,7 +21,8 @@ namespace Bit.Core.Services
#if !FDROID
// just in case the caller throws the exception in a moment where the logger can't be resolved
// we need to track the error as well
Microsoft.AppCenter.Crashes.Crashes.TrackError(ex);
//Microsoft.AppCenter.Crashes.Crashes.TrackError(ex);
ClipLogger.Log(ex?.ToString());
#endif
}
}

View File

@@ -154,7 +154,11 @@ namespace Bit.App.Utilities
// Currently on iOS when resuming the app after showing a System "Share/Sheet" (or other similar UI)
// MAUI reports the incorrect Theme. To avoid this we are fetching the current OS Theme directly on iOS from the iOS API.
// MAUI Issue: https://github.com/dotnet/maui/issues/19614
#if IOS
public static bool OsDarkModeEnabled(UITraitCollection? traitCollection = null)
#else
public static bool OsDarkModeEnabled()
#endif
{
#if UT
return false;
@@ -167,8 +171,19 @@ namespace Bit.App.Utilities
if (!OperatingSystem.IsIOSVersionAtLeast(13, 0))
return false;
var traits = InvokeOnMainThread(() => WindowStateManager.Default.GetCurrentUIViewController()?.TraitCollection) ?? UITraitCollection.CurrentTraitCollection;
var uiStyle = traits.UserInterfaceStyle;
ClipLogger.Log($"TC, UIStyle: {traitCollection?.UserInterfaceStyle}");
var uiStyle = traitCollection?.UserInterfaceStyle;
if (traitCollection is null)
{
ClipLogger.Log($"TC null getting trait collection from wsm");
var traits = InvokeOnMainThread(() =>
{
return WindowStateManager.Default.GetCurrentUIViewController()?.TraitCollection;
}) ?? UITraitCollection.CurrentTraitCollection;
uiStyle = traits.UserInterfaceStyle;
}
ClipLogger.Log($"UIStyle: {uiStyle}");
requestedTheme = uiStyle switch
{

View File

@@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using AuthenticationServices;
using Bit.App.Abstractions;
@@ -47,6 +48,10 @@ namespace Bit.iOS.Autofill
{
try
{
ClipLogger.Log($"[ViewDidLoad]");
ClipLogger.Log($"ViewDidLoad: {TraitCollection?.UserInterfaceStyle}");
InitAppIfNeededAsync().FireAndForget(ex => OnProvidingCredentialException(ex));
base.ViewDidLoad();
@@ -149,18 +154,6 @@ namespace Bit.iOS.Autofill
}
}
//public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
//{
// try
// {
// await ProvideCredentialWithoutUserInteractionAsync(credentialIdentity);
// }
// catch (Exception ex)
// {
// OnProvidingCredentialException(ex);
// }
//}
[Export("prepareInterfaceToProvideCredentialForRequest:")]
public override async void PrepareInterfaceToProvideCredential(IASCredentialRequest credentialRequest)
{
@@ -194,18 +187,6 @@ namespace Bit.iOS.Autofill
}
}
//public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
//{
// try
// {
// await PrepareInterfaceToProvideCredentialAsync(c => c.PasswordCredentialIdentity = credentialIdentity);
// }
// catch (Exception ex)
// {
// OnProvidingCredentialException(ex);
// }
//}
public override async void PrepareInterfaceForExtensionConfiguration()
{
try
@@ -546,7 +527,45 @@ namespace Bit.iOS.Autofill
_nfcSession, out _nfcDelegate, out _accountsManager);
}
private async Task InitAppIfNeededAsync()
public override void ViewIsAppearing(bool animated)
{
base.ViewIsAppearing(animated);
if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
{
return;
}
ClipLogger.Log($"ViewIsAppearing: {TraitCollection?.UserInterfaceStyle}");
var appOptions = new AppOptions { IosExtension = true };
var app = new App.App(appOptions);
ThemeManager.SetTheme(app.Resources);
iOSCoreHelpers.AppearanceAdjustments();
View.BackgroundColor = UIColor.SystemBackground;
Logo.Image = UIImage.FromFile("splash_logo");
View.SetNeedsLayout();
ClipLogger.Log($"Window null: {View.Window is null}");
//View.Window?.UpdateTraitsIfNeeded();
ServiceContainer.Resolve<IMessagingService>().Send("update_traits");
//((IUITraitChangeObservable)this).RegisterForTraitChanges<UITraitUserInterfaceStyle>((env, traits) =>
//{
// MainThread.BeginInvokeOnMainThread(() =>
// {
// View.BackgroundColor = UIColor.SystemBackground;
// Logo.Image = UIImage.FromFile("splash_logo");
// });
//});
}
private async Task InitAppIfNeededAsync([CallerMemberName] string memberName = "")
{
if (ServiceContainer.RegisteredServices == null || ServiceContainer.RegisteredServices.Count == 0)
{
@@ -554,6 +573,13 @@ namespace Bit.iOS.Autofill
}
await _stateService.Value.ReloadStateAsync();
ClipLogger.Log($"InitAppIfNeeded {memberName}");
//if (_viewIsAppeared)
//{
// var app = new App.App(new AppOptions { IosExtension = true });
// ThemeManager.SetTheme(app.Resources);
// iOSCoreHelpers.AppearanceAdjustments();
//}
}
private void LaunchHomePage()

View File

@@ -1,6 +1,7 @@
using Bit.Core.Services;
using Bit.iOS.Core.Utilities;
using Foundation;
using Microsoft.Maui.ApplicationModel;
using ObjCRuntime;
using UIKit;
@@ -50,6 +51,22 @@ namespace Bit.iOS.Autofill.ListItems
_separator.TrailingAnchor.ConstraintEqualTo(ContentView.TrailingAnchor, -7),
_separator.BottomAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.BottomAnchor, 2)
});
if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
{
return;
}
((IUITraitChangeObservable)this).RegisterForTraitChanges<UITraitUserInterfaceStyle>((env, traits) =>
{
ClipLogger.Log($"[HeaderItemView] TraitCollection: {traits.UserInterfaceStyle}");
MainThread.BeginInvokeOnMainThread(() =>
{
ContentView.BackgroundColor = UIColor.SystemBackground;
_header.TextColor = ThemeHelpers.TextColor;
_separator.BackgroundColor = ThemeHelpers.SeparatorColor;
});
});
}
catch (System.Exception ex)
{

View File

@@ -3,6 +3,7 @@ using Bit.App.Controls;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.iOS.Core.Utilities;
using Microsoft.Maui.ApplicationModel;
using UIKit;
namespace Bit.iOS.Autofill

View File

@@ -161,6 +161,18 @@ namespace Bit.iOS.Autofill
}
}
protected override void OnUpdateTraitAppearance()
{
base.OnUpdateTraitAppearance();
_searchBar.BackgroundColor = _searchBar.BarTintColor = ThemeHelpers.ListHeaderBackgroundColor;
_searchBar.UpdateThemeIfNeeded();
TableView.BackgroundColor = ThemeHelpers.BackgroundColor;
_emptyViewButton.Layer.BorderColor = UIColor.FromName(ColorConstants.LIGHT_TEXT_MUTED).CGColor;
}
public async Task DoFido2GetAssertionAsync(IFido2GetAssertionUserInterface fido2GetAssertionUserInterface)
{
if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
@@ -436,6 +448,20 @@ namespace Bit.iOS.Autofill
}
});
}
if (message.Command == "update_traits" && UIDevice.CurrentDevice.CheckSystemVersion(17,0))
{
MainThread.InvokeOnMainThreadAsync(() =>
{
ClipLogger.Log($"[{nameof(LoginListViewController)}] updating traits");
ClipLogger.Log($"[{nameof(LoginListViewController)}] style: {TraitCollection?.UserInterfaceStyle}");
UpdateTraitsIfNeeded();
OnUpdateTraitAppearance();
View.SetNeedsLayout();
});
}
});
}
@@ -535,6 +561,17 @@ namespace Bit.iOS.Autofill
}
}
//public override void ViewIsAppearing(bool animated)
//{
// base.ViewIsAppearing(animated);
// ClipLogger.Log($"[{nameof(LoginListViewController)}] TraitCollection: {TraitCollection?.UserInterfaceStyle}");
// ((IUITraitChangeObservable)this).RegisterForTraitChanges<UITraitUserInterfaceStyle>((env, traits) =>
// {
// ClipLogger.Log($"[LoginListViewController] changed TraitCollection: {traits.UserInterfaceStyle}");
// });
//}
public void ReloadTableViewData() => TableView.ReloadData();
public class TableSource : BaseLoginListTableSource<LoginListViewController>

View File

@@ -420,6 +420,8 @@
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="FcI-Ph-m9e">
<rect key="frame" x="0.0" y="0.0" width="414" height="830"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="separatorColor" name="LightSecondary300"/>
<color key="sectionIndexBackgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="oQZ-wW-5uB">
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
@@ -650,6 +652,9 @@
<image name="check.png" width="90" height="90"/>
<image name="empty_items_state" width="157" height="111"/>
<image name="splash_logo" width="282" height="44"/>
<namedColor name="LightSecondary300">
<color red="0.80800002813339233" green="0.83099997043609619" blue="0.86299997568130493" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</namedColor>
<namedColor name="LightTextMuted">
<color red="0.42699998617172241" green="0.45899999141693115" blue="0.49399998784065247" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>

View File

@@ -221,6 +221,29 @@ namespace Bit.iOS.Core.Controllers
}
}
//public override void ViewIsAppearing(bool animated)
//{
// base.ViewIsAppearing(animated);
// if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
// {
// return;
// }
// ClipLogger.Log($"baselock ViewIsAppearing: {TraitCollection?.UserInterfaceStyle}");
// ((IUITraitChangeObservable)this).RegisterForTraitChanges<UITraitUserInterfaceStyle>((env, traits) =>
// {
// ClipLogger.Log($"[BaseLockPass] TraitCollection: {traits.UserInterfaceStyle}");
// MainThread.BeginInvokeOnMainThread(() =>
// {
// View.BackgroundColor = UIColor.SystemBackground;
// TableView.BackgroundColor = ThemeHelpers.BackgroundColor;
// TableView.SeparatorColor = ThemeHelpers.SeparatorColor;
// });
// });
//}
public override void ViewDidAppear(bool animated)
{
try

View File

@@ -1,5 +1,4 @@
using Bit.iOS.Core.Utilities;
using System;
using UIKit;
namespace Bit.iOS.Core.Controllers
@@ -24,6 +23,35 @@ namespace Bit.iOS.Core.Controllers
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
{
OnUpdateTraitAppearance();
}
}
public override void ViewIsAppearing(bool animated)
{
base.ViewIsAppearing(animated);
if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
{
return;
}
OnUpdateTraitAppearance();
((IUITraitChangeObservable)this).RegisterForTraitChanges<UITraitUserInterfaceStyle>((env, traits) =>
{
MainThread.BeginInvokeOnMainThread(() =>
{
OnUpdateTraitAppearance();
});
});
}
protected virtual void OnUpdateTraitAppearance()
{
if (View != null)
{
View.BackgroundColor = ThemeHelpers.BackgroundColor;

View File

@@ -1,5 +1,5 @@
using Bit.Core.Services;
using Bit.iOS.Core.Utilities;
using System;
using UIKit;
namespace Bit.iOS.Core.Controllers
@@ -28,9 +28,39 @@ namespace Bit.iOS.Core.Controllers
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
{
OnUpdateTraitAppearance();
}
}
public override void ViewIsAppearing(bool animated)
{
base.ViewIsAppearing(animated);
if (!UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
{
return;
}
OnUpdateTraitAppearance();
((IUITraitChangeObservable)this).RegisterForTraitChanges<UITraitUserInterfaceStyle>((env, traits) =>
{
MainThread.BeginInvokeOnMainThread(() =>
{
OnUpdateTraitAppearance();
});
});
}
protected virtual void OnUpdateTraitAppearance()
{
if (View != null)
{
View.BackgroundColor = ThemeHelpers.BackgroundColor;
ClipLogger.Log($"[{GetType().FullName}] back color: {View.BackgroundColor}");
}
UpdateNavigationBarTheme();
}

View File

@@ -1,6 +1,7 @@
using Bit.App.Pages;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.iOS.Core.Utilities;
using Microsoft.Maui.Controls.Handlers.Compatibility;
@@ -21,6 +22,7 @@ namespace Bit.iOS.Core.Handlers
{
if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
{
ClipLogger.Log($"CustomTabbedHandler ThemeManager.UPDATED_THEME_MESSAGE_KEY");
MainThread.BeginInvokeOnMainThread(() =>
{
iOSCoreHelpers.AppearanceAdjustments();

View File

@@ -1,4 +1,5 @@
using Bit.App.Utilities;
using Bit.Core.Services;
using Microsoft.Maui.Platform;
using UIKit;
@@ -80,6 +81,8 @@ namespace Bit.iOS.Core.Utilities
private static void SetThemeVariables(string theme, bool osDarkModeEnabled)
{
ClipLogger.Log($"[SetThemeVariables] {theme}, {osDarkModeEnabled}");
if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled)
{
theme = ThemeManager.Dark;

View File

@@ -124,7 +124,7 @@ namespace Bit.iOS.Core.Utilities
else
{
#if DEBUG
logger = DebugLogger.Instance;
logger = ClipLogger.Instance;
#else
logger = Logger.Instance;
#endif
@@ -248,9 +248,9 @@ namespace Bit.iOS.Core.Utilities
var bootstrapTask = BootstrapAsync(postBootstrapFunc);
}
public static void AppearanceAdjustments()
public static void AppearanceAdjustments(UITraitCollection? traitCollection = null)
{
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled(traitCollection));
UIApplication.SharedApplication.StatusBarHidden = false;
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
}
@@ -292,6 +292,21 @@ namespace Bit.iOS.Core.Utilities
{
ListenYubiKey(listen, deviceActionService, nfcSession, nfcDelegate);
}
else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
{
MainThread.BeginInvokeOnMainThread(() =>
{
try
{
ClipLogger.Log($"message updated theme message key");
AppearanceAdjustments();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
});
}
});
}

View File

@@ -93,6 +93,21 @@ namespace Bit.iOS.Core.Views
_separator.TrailingAnchor.ConstraintEqualTo(ContentView.TrailingAnchor, -7),
_separator.BottomAnchor.ConstraintEqualTo(ContentView.BottomAnchor, 0)
});
((IUITraitChangeObservable)this).RegisterForTraitChanges<UITraitUserInterfaceStyle>((env, traits) =>
{
ClipLogger.Log($"[CipherLoginTableViewCell] TraitCollection: {traits.UserInterfaceStyle}");
MainThread.BeginInvokeOnMainThread(() =>
{
ContentView.BackgroundColor = UIColor.SystemBackground;
_title.TextColor = ThemeHelpers.TextColor;
_subtitle.TextColor = ThemeHelpers.MutedColor;
_mainIcon.TextColor = ThemeHelpers.PrimaryColor;
_orgIcon.TextColor = ThemeHelpers.MutedColor;
_separator.BackgroundColor = ThemeHelpers.SeparatorColor;
_mainStackView.BackgroundColor = UIColor.SystemCyan;
});
});
}
catch (System.Exception ex)
{