1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-15 15:53:44 +00:00

Compare commits

..

1 Commits

65 changed files with 581 additions and 958 deletions

View File

@@ -60,11 +60,6 @@ jobs:
runs-on: windows-2019 runs-on: windows-2019
needs: setup needs: setup
steps: steps:
- name: Setup NuGet
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with:
nuget-version: 5.9.0
- name: Set up MSBuild - name: Set up MSBuild
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
@@ -214,11 +209,6 @@ jobs:
name: F-Droid Build name: F-Droid Build
runs-on: windows-2019 runs-on: windows-2019
steps: steps:
- name: Setup NuGet
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with:
nuget-version: 5.9.0
- name: Set up MSBuild - name: Set up MSBuild
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
@@ -378,11 +368,6 @@ jobs:
runs-on: macos-11 runs-on: macos-11
needs: setup needs: setup
steps: steps:
- name: Setup NuGet
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with:
nuget-version: 5.9.0
- name: Print environment - name: Print environment
run: | run: |
nuget help | grep Version nuget help | grep Version

View File

@@ -12,7 +12,7 @@ The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin
# Build/Run # Build/Run
Please refer to the [Mobile section](https://contributing.bitwarden.com/mobile) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. Please refer to the [Mobile section](https://contributing.bitwarden.com/clients/mobile) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
# We're Hiring! # We're Hiring!

View File

@@ -99,13 +99,12 @@ namespace Bit.Droid
{ {
ServiceContainer.Register<INativeLogService>("nativeLogService", new AndroidLogService()); ServiceContainer.Register<INativeLogService>("nativeLogService", new AndroidLogService());
#if FDROID #if FDROID
var logger = new StubLogger(); ServiceContainer.Register<ILogger>("logger", new StubLogger());
#elif DEBUG #elif DEBUG
var logger = DebugLogger.Instance; ServiceContainer.Register<ILogger>("logger", DebugLogger.Instance);
#else #else
var logger = Logger.Instance; ServiceContainer.Register<ILogger>("logger", Logger.Instance);
#endif #endif
ServiceContainer.Register("logger", logger);
// Note: This might cause a race condition. Investigate more. // Note: This might cause a race condition. Investigate more.
Task.Run(() => Task.Run(() =>
@@ -125,13 +124,13 @@ namespace Bit.Droid
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var liteDbStorage = new LiteDbStorageService(Path.Combine(documentsPath, "bitwarden.db")); var liteDbStorage = new LiteDbStorageService(Path.Combine(documentsPath, "bitwarden.db"));
var localizeService = new LocalizeService(); var localizeService = new LocalizeService();
var broadcasterService = new BroadcasterService(logger); var broadcasterService = new BroadcasterService();
var messagingService = new MobileBroadcasterMessagingService(broadcasterService); var messagingService = new MobileBroadcasterMessagingService(broadcasterService);
var i18nService = new MobileI18nService(localizeService.GetCurrentCultureInfo()); var i18nService = new MobileI18nService(localizeService.GetCurrentCultureInfo());
var secureStorageService = new SecureStorageService(); var secureStorageService = new SecureStorageService();
var cryptoPrimitiveService = new CryptoPrimitiveService(); var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage); var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var stateService = new StateService(mobileStorageService, secureStorageService, messagingService); var stateService = new StateService(mobileStorageService, secureStorageService);
var stateMigrationService = var stateMigrationService =
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService); new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
var clipboardService = new ClipboardService(stateService); var clipboardService = new ClipboardService(stateService);

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2022.6.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2022.05.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>

View File

@@ -28,25 +28,17 @@ namespace Bit.Droid.Services
public async Task CopyTextAsync(string text, int expiresInMs = -1, bool isSensitive = true) public async Task CopyTextAsync(string text, int expiresInMs = -1, bool isSensitive = true)
{ {
try // Xamarin.Essentials.Clipboard currently doesn't support the IS_SENSITIVE flag for API 33+
if ((int)Build.VERSION.SdkInt < 33)
{ {
// Xamarin.Essentials.Clipboard currently doesn't support the IS_SENSITIVE flag for API 33+ await Clipboard.SetTextAsync(text);
if ((int)Build.VERSION.SdkInt < 33) }
{ else
await Clipboard.SetTextAsync(text); {
} CopyToClipboard(text, isSensitive);
else }
{
CopyToClipboard(text, isSensitive);
}
await ClearClipboardAlarmAsync(expiresInMs); await ClearClipboardAlarmAsync(expiresInMs);
}
catch (Java.Lang.SecurityException ex) when (ex.Message.Contains("does not belong to"))
{
// #1962 Just ignore, the content is copied either way but there is some app interfiering in the process
// that the OS catches and just throws this exception.
}
} }
public bool IsCopyNotificationHandledByPlatform() public bool IsCopyNotificationHandledByPlatform()

View File

@@ -59,10 +59,10 @@ namespace Bit.Droid.Utilities
{ {
if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled) if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled)
{ {
theme = ThemeManager.Dark; theme = "dark";
} }
if (theme == ThemeManager.Dark || theme == ThemeManager.Black || theme == ThemeManager.Nord) if (theme == "dark" || theme == "black" || theme == "nord")
{ {
LightTheme = false; LightTheme = false;
} }

View File

@@ -10,7 +10,6 @@ using Bit.App.Utilities.AccountManagement;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
@@ -57,93 +56,86 @@ namespace Bit.App
Bootstrap(); Bootstrap();
_broadcasterService.Subscribe(nameof(App), async (message) => _broadcasterService.Subscribe(nameof(App), async (message) =>
{ {
try if (message.Command == "showDialog")
{ {
if (message.Command == "showDialog") var details = message.Data as DialogDetails;
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
Device.BeginInvokeOnMainThread(async () =>
{ {
var details = message.Data as DialogDetails; if (!string.IsNullOrWhiteSpace(details.CancelText))
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
Device.BeginInvokeOnMainThread(async () =>
{ {
if (!string.IsNullOrWhiteSpace(details.CancelText)) confirmed = await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText,
{ details.CancelText);
confirmed = await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText,
details.CancelText);
}
else
{
await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText);
}
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
}
else if (message.Command == "resumed")
{
if (Device.RuntimePlatform == Device.iOS)
{
ResumedAsync().FireAndForget();
} }
} else
else if (message.Command == "slept")
{
if (Device.RuntimePlatform == Device.iOS)
{ {
await SleptAsync(); await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText);
} }
} _messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
else if (message.Command == "migrated") });
}
else if (message.Command == "resumed")
{
if (Device.RuntimePlatform == Device.iOS)
{ {
await Task.Delay(1000); ResumedAsync().FireAndForget();
await _accountsManager.NavigateOnAccountChangeAsync();
}
else if (message.Command == "popAllAndGoToTabGenerator" ||
message.Command == "popAllAndGoToTabMyVault" ||
message.Command == "popAllAndGoToTabSend" ||
message.Command == "popAllAndGoToAutofillCiphers")
{
Device.BeginInvokeOnMainThread(async () =>
{
if (Current.MainPage is TabsPage tabsPage)
{
while (tabsPage.Navigation.ModalStack.Count > 0)
{
await tabsPage.Navigation.PopModalAsync(false);
}
if (message.Command == "popAllAndGoToAutofillCiphers")
{
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
}
else if (message.Command == "popAllAndGoToTabMyVault")
{
Options.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else if (message.Command == "popAllAndGoToTabGenerator")
{
Options.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
else if (message.Command == "popAllAndGoToTabSend")
{
tabsPage.ResetToSendPage();
}
}
});
}
else if (message.Command == "convertAccountToKeyConnector")
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(
new NavigationPage(new RemoveMasterPasswordPage()));
});
} }
} }
catch (Exception ex) else if (message.Command == "slept")
{ {
LoggerHelper.LogEvenIfCantBeResolved(ex); if (Device.RuntimePlatform == Device.iOS)
{
await SleptAsync();
}
}
else if (message.Command == "migrated")
{
await Task.Delay(1000);
await _accountsManager.NavigateOnAccountChangeAsync();
}
else if (message.Command == "popAllAndGoToTabGenerator" ||
message.Command == "popAllAndGoToTabMyVault" ||
message.Command == "popAllAndGoToTabSend" ||
message.Command == "popAllAndGoToAutofillCiphers")
{
Device.BeginInvokeOnMainThread(async () =>
{
if (Current.MainPage is TabsPage tabsPage)
{
while (tabsPage.Navigation.ModalStack.Count > 0)
{
await tabsPage.Navigation.PopModalAsync(false);
}
if (message.Command == "popAllAndGoToAutofillCiphers")
{
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
}
else if (message.Command == "popAllAndGoToTabMyVault")
{
Options.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else if (message.Command == "popAllAndGoToTabGenerator")
{
Options.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
else if (message.Command == "popAllAndGoToTabSend")
{
tabsPage.ResetToSendPage();
}
}
});
}
else if (message.Command == "convertAccountToKeyConnector")
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(
new NavigationPage(new RemoveMasterPasswordPage()));
});
} }
}); });
} }
@@ -210,7 +202,6 @@ namespace Bit.App
private async Task ResumedAsync() private async Task ResumedAsync()
{ {
await _stateService.CheckExtensionActiveUserAndSwitchIfNeededAsync();
await _vaultTimeoutService.CheckVaultTimeoutAsync(); await _vaultTimeoutService.CheckVaultTimeoutAsync();
_messagingService.Send("startEventTimer"); _messagingService.Send("startEventTimer");
await UpdateThemeAsync(); await UpdateThemeAsync();

View File

@@ -65,8 +65,6 @@ namespace Bit.App.Controls
public bool LongPressAccountEnabled { get; set; } = true; public bool LongPressAccountEnabled { get; set; } = true;
public Action AfterHide { get; set; }
public async Task ToggleVisibilityAsync() public async Task ToggleVisibilityAsync()
{ {
if (IsVisible) if (IsVisible)
@@ -139,8 +137,6 @@ namespace Bit.App.Controls
// remove overlay // remove overlay
IsVisible = false; IsVisible = false;
AfterHide?.Invoke();
}); });
} }

View File

@@ -45,28 +45,23 @@ namespace Bit.App.Controls
public ICommand LongPressAccountCommand { get; } public ICommand LongPressAccountCommand { get; }
public bool FromIOSExtension { get; set; }
private async Task SelectAccountAsync(AccountViewCellViewModel item) private async Task SelectAccountAsync(AccountViewCellViewModel item)
{ {
if (!item.AccountView.IsAccount) if (item.AccountView.IsAccount)
{ {
_messagingService.Send(AccountsManagerMessageCommands.ADD_ACCOUNT); if (!item.AccountView.IsActive)
return;
}
if (!item.AccountView.IsActive)
{
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
if (FromIOSExtension)
{ {
await _stateService.SaveExtensionActiveUserIdToStorageAsync(item.AccountView.UserId); await _stateService.SetActiveUserAsync(item.AccountView.UserId);
_messagingService.Send("switchedAccount");
}
else if (AllowActiveAccountSelection)
{
_messagingService.Send("switchedAccount");
} }
} }
else if (AllowActiveAccountSelection) else
{ {
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT); _messagingService.Send("addAccount");
} }
} }

View File

@@ -72,7 +72,8 @@ namespace Bit.App.Pages
} }
public string ShowPasswordIcon => ShowPassword ? "" : ""; public string ShowPasswordIcon => ShowPassword ? "" : "";
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public string MasterPassword { get; set; } public string MasterPassword { get; set; }
public string ConfirmMasterPassword { get; set; } public string ConfirmMasterPassword { get; set; }
public string Hint { get; set; } public string Hint { get; set; }

View File

@@ -54,7 +54,7 @@ namespace Bit.App.Pages
{ {
_vm.AvatarImageSource = await GetAvatarImageSourceAsync(); _vm.AvatarImageSource = await GetAvatarImageSourceAsync();
} }
_broadcasterService.Subscribe(nameof(HomePage), (message) => _broadcasterService.Subscribe(nameof(HomePage), async (message) =>
{ {
if (message.Command == "updatedTheme") if (message.Command == "updatedTheme")
{ {

View File

@@ -80,8 +80,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}" />
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
</Grid> </Grid>
<Grid <Grid
x:Name="_passwordGrid" x:Name="_passwordGrid"
@@ -120,7 +119,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" /> AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid> </Grid>
<StackLayout <StackLayout

View File

@@ -25,6 +25,8 @@ namespace Bit.App.Pages
_vm = BindingContext as LockPageViewModel; _vm = BindingContext as LockPageViewModel;
_vm.Page = this; _vm.Page = this;
_vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync()); _vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync());
MasterPasswordEntry = _masterPassword;
PinEntry = _pin;
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
@@ -36,17 +38,8 @@ namespace Bit.App.Pages
} }
} }
public Entry SecretEntry public Entry MasterPasswordEntry { get; set; }
{ public Entry PinEntry { get; set; }
get
{
if (_vm?.PinLock ?? false)
{
return _pin;
}
return _masterPassword;
}
}
public async Task PromptBiometricAfterResumeAsync() public async Task PromptBiometricAfterResumeAsync()
{ {
@@ -77,12 +70,16 @@ namespace Bit.App.Pages
_vm.AvatarImageSource = await GetAvatarImageSourceAsync(); _vm.AvatarImageSource = await GetAvatarImageSourceAsync();
await _vm.InitAsync(); await _vm.InitAsync();
_vm.FocusSecretEntry += PerformFocusSecretEntry;
if (!_vm.BiometricLock) if (!_vm.BiometricLock)
{ {
RequestFocus(SecretEntry); if (_vm.PinLock)
{
RequestFocus(PinEntry);
}
else
{
RequestFocus(MasterPasswordEntry);
}
} }
else else
{ {
@@ -102,18 +99,6 @@ namespace Bit.App.Pages
} }
} }
private void PerformFocusSecretEntry(int? cursorPosition)
{
Device.BeginInvokeOnMainThread(() =>
{
SecretEntry.Focus();
if (cursorPosition.HasValue)
{
SecretEntry.CursorPosition = cursorPosition.Value;
}
});
}
protected override bool OnBackButtonPressed() protected override bool OnBackButtonPressed()
{ {
if (_accountListOverlay.IsVisible) if (_accountListOverlay.IsVisible)

View File

@@ -10,7 +10,6 @@ using Bit.Core.Enums;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Models.Request; using Bit.Core.Models.Request;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.Helpers;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -28,7 +27,6 @@ namespace Bit.App.Pages
private readonly IBiometricService _biometricService; private readonly IBiometricService _biometricService;
private readonly IKeyConnectorService _keyConnectorService; private readonly IKeyConnectorService _keyConnectorService;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
private string _email; private string _email;
private bool _showPassword; private bool _showPassword;
@@ -131,15 +129,11 @@ namespace Bit.App.Pages
public Command SubmitCommand { get; } public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public string MasterPassword { get; set; } public string MasterPassword { get; set; }
public string Pin { get; set; } public string Pin { get; set; }
public Action UnlockedAction { get; set; } public Action UnlockedAction { get; set; }
public event Action<int?> FocusSecretEntry
{
add => _secretEntryFocusWeakEventManager.AddEventHandler(value);
remove => _secretEntryFocusWeakEventManager.RemoveEventHandler(value);
}
public async Task InitAsync() public async Task InitAsync()
{ {
@@ -353,8 +347,11 @@ namespace Bit.App.Pages
public void TogglePassword() public void TogglePassword()
{ {
ShowPassword = !ShowPassword; ShowPassword = !ShowPassword;
var secret = PinLock ? Pin : MasterPassword; var page = (Page as LockPage);
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry)); var entry = PinLock ? page.PinEntry : page.MasterPasswordEntry;
var str = PinLock ? Pin : MasterPassword;
entry.Focus();
entry.CursorPosition = String.IsNullOrEmpty(str) ? 0 : str.Length;
} }
public async Task PromptBiometricAsync() public async Task PromptBiometricAsync()
@@ -365,8 +362,18 @@ namespace Bit.App.Pages
return; return;
} }
var success = await _platformUtilsService.AuthenticateBiometricAsync(null, var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
PinLock ? AppResources.PIN : AppResources.MasterPassword, PinLock ? AppResources.PIN : AppResources.MasterPassword, () =>
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry))); {
var page = Page as LockPage;
if (PinLock)
{
page.PinEntry.Focus();
}
else
{
page.MasterPasswordEntry.Focus();
}
});
await _stateService.SetBiometricLockedAsync(!success); await _stateService.SetBiometricLockedAsync(!success);
if (success) if (success)
{ {

View File

@@ -101,8 +101,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
</Grid> </Grid>
</StackLayout> </StackLayout>
<StackLayout Padding="10, 0"> <StackLayout Padding="10, 0">

View File

@@ -86,7 +86,8 @@ namespace Bit.App.Pages
public Command LogInCommand { get; } public Command LogInCommand { get; }
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public Action StartTwoFactorAction { get; set; } public Action StartTwoFactorAction { get; set; }
public Action LogInSuccessAction { get; set; } public Action LogInSuccessAction { get; set; }
public Action UpdateTempPasswordAction { get; set; } public Action UpdateTempPasswordAction { get; set; }

View File

@@ -81,12 +81,10 @@ namespace Bit.App.Pages
} }
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn); await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
string ssoToken;
try try
{ {
var response = await _apiService.PreValidateSso(OrgIdentifier); await _apiService.PreValidateSso(OrgIdentifier);
ssoToken = response.Token;
} }
catch (ApiException e) catch (ApiException e)
{ {
@@ -114,8 +112,7 @@ namespace Bit.App.Pages
"response_type=code&scope=api%20offline_access&" + "response_type=code&scope=api%20offline_access&" +
"state=" + state + "&code_challenge=" + codeChallenge + "&" + "state=" + state + "&code_challenge=" + codeChallenge + "&" +
"code_challenge_method=S256&response_mode=query&" + "code_challenge_method=S256&response_mode=query&" +
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier) + "&" + "domain_hint=" + Uri.EscapeDataString(OrgIdentifier);
"ssoToken=" + Uri.EscapeDataString(ssoToken);
WebAuthenticatorResult authResult = null; WebAuthenticatorResult authResult = null;
try try

View File

@@ -68,8 +68,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}" />
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
</Grid> </Grid>
<Label <Label
Text="{u:I18n MasterPasswordDescription}" Text="{u:I18n MasterPasswordDescription}"
@@ -107,8 +106,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid> </Grid>
<StackLayout StyleClass="box-row"> <StackLayout StyleClass="box-row">
<Label <Label

View File

@@ -74,7 +74,8 @@ namespace Bit.App.Pages
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public Command ToggleConfirmPasswordCommand { get; } public Command ToggleConfirmPasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public string Name { get; set; } public string Name { get; set; }
public string Email { get; set; } public string Email { get; set; }
public string MasterPassword { get; set; } public string MasterPassword { get; set; }

View File

@@ -107,8 +107,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid> </Grid>
<Label <Label
Text="{u:I18n MasterPasswordDescription}" Text="{u:I18n MasterPasswordDescription}"
@@ -146,8 +145,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid> </Grid>
<StackLayout StyleClass="box-row"> <StackLayout StyleClass="box-row">
<Label <Label

View File

@@ -90,7 +90,8 @@ namespace Bit.App.Pages
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public Command ToggleConfirmPasswordCommand { get; } public Command ToggleConfirmPasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public string MasterPassword { get; set; } public string MasterPassword { get; set; }
public string ConfirmMasterPassword { get; set; } public string ConfirmMasterPassword { get; set; }
public string Hint { get; set; } public string Hint { get; set; }

View File

@@ -105,8 +105,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid> </Grid>
</StackLayout> </StackLayout>
<StackLayout StyleClass="box"> <StackLayout StyleClass="box">
@@ -141,8 +140,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}" />
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid> </Grid>
<StackLayout StyleClass="box-row"> <StackLayout StyleClass="box-row">
<Label <Label

View File

@@ -444,8 +444,7 @@
Command="{Binding TogglePasswordCommand}" Command="{Binding TogglePasswordCommand}"
Margin="10,0,0,0" Margin="10,0,0,0"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n PasswordInfo}" Text="{u:I18n PasswordInfo}"

View File

@@ -241,7 +241,8 @@ namespace Bit.App.Pages
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6; public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7; public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public string FileTypeAccessibilityLabel => IsFile ? AppResources.FileTypeIsSelected : AppResources.FileTypeIsNotSelected; public string FileTypeAccessibilityLabel => IsFile ? AppResources.FileTypeIsSelected : AppResources.FileTypeIsNotSelected;
public string TextTypeAccessibilityLabel => IsText ? AppResources.TextTypeIsSelected : AppResources.TextTypeIsNotSelected; public string TextTypeAccessibilityLabel => IsText ? AppResources.TextTypeIsSelected : AppResources.TextTypeIsNotSelected;

View File

@@ -5,7 +5,6 @@ using Bit.App.Controls;
using Bit.App.Models; using Bit.App.Models;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
@@ -69,28 +68,21 @@ namespace Bit.App.Pages
_broadcasterService.Subscribe(_pageName, async (message) => _broadcasterService.Subscribe(_pageName, async (message) =>
{ {
try if (message.Command == "syncStarted")
{ {
if (message.Command == "syncStarted") Device.BeginInvokeOnMainThread(() => IsBusy = true);
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted" || message.Command == "sendUpdated")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
} }
catch (Exception ex) else if (message.Command == "syncCompleted" || message.Command == "sendUpdated")
{ {
LoggerHelper.LogEvenIfCantBeResolved(ex); await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
} }
}); });

View File

@@ -104,8 +104,7 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/> IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
<Label <Label
Text="{u:I18n ConfirmYourIdentity}" Text="{u:I18n ConfirmYourIdentity}"

View File

@@ -143,7 +143,8 @@ namespace Bit.App.Pages
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public void TogglePassword() public void TogglePassword()
{ {

View File

@@ -33,23 +33,6 @@
StyleClass="box-footer-label" StyleClass="box-footer-label"
Text="{u:I18n ThemeDescription}" /> Text="{u:I18n ThemeDescription}" />
</StackLayout> </StackLayout>
<StackLayout
StyleClass="box"
IsVisible="{Binding ShowAutoDarkThemeOptions}">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label
Text="{u:I18n DefaultDarkTheme}"
StyleClass="box-label" />
<Picker
x:Name="_autoDarkThemePicker"
ItemsSource="{Binding AutoDarkThemeOptions, Mode=OneTime}"
SelectedIndex="{Binding AutoDarkThemeSelectedIndex}"
StyleClass="box-value" />
</StackLayout>
<Label
StyleClass="box-footer-label"
Text="{u:I18n DefaultDarkThemeDescription}" />
</StackLayout>
<StackLayout StyleClass="box"> <StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform"> <StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
<Label <Label

View File

@@ -19,7 +19,6 @@ namespace Bit.App.Pages
_vm = BindingContext as OptionsPageViewModel; _vm = BindingContext as OptionsPageViewModel;
_vm.Page = this; _vm.Page = this;
_themePicker.ItemDisplayBinding = new Binding("Value"); _themePicker.ItemDisplayBinding = new Binding("Value");
_autoDarkThemePicker.ItemDisplayBinding = new Binding("Value");
_uriMatchPicker.ItemDisplayBinding = new Binding("Value"); _uriMatchPicker.ItemDisplayBinding = new Binding("Value");
_clearClipboardPicker.ItemDisplayBinding = new Binding("Value"); _clearClipboardPicker.ItemDisplayBinding = new Binding("Value");
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
@@ -30,7 +29,6 @@ namespace Bit.App.Pages
else else
{ {
_themePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished); _themePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_autoDarkThemePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_uriMatchPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished); _uriMatchPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_clearClipboardPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished); _clearClipboardPicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
} }

View File

@@ -23,7 +23,6 @@ namespace Bit.App.Pages
private bool _disableAutoTotpCopy; private bool _disableAutoTotpCopy;
private int _clearClipboardSelectedIndex; private int _clearClipboardSelectedIndex;
private int _themeSelectedIndex; private int _themeSelectedIndex;
private int _autoDarkThemeSelectedIndex;
private int _uriMatchSelectedIndex; private int _uriMatchSelectedIndex;
private bool _inited; private bool _inited;
private bool _updatingAutofill; private bool _updatingAutofill;
@@ -54,16 +53,10 @@ namespace Bit.App.Pages
ThemeOptions = new List<KeyValuePair<string, string>> ThemeOptions = new List<KeyValuePair<string, string>>
{ {
new KeyValuePair<string, string>(null, AppResources.ThemeDefault), new KeyValuePair<string, string>(null, AppResources.ThemeDefault),
new KeyValuePair<string, string>(ThemeManager.Light, AppResources.Light), new KeyValuePair<string, string>("light", AppResources.Light),
new KeyValuePair<string, string>(ThemeManager.Dark, AppResources.Dark), new KeyValuePair<string, string>("dark", AppResources.Dark),
new KeyValuePair<string, string>(ThemeManager.Black, AppResources.Black), new KeyValuePair<string, string>("black", AppResources.Black),
new KeyValuePair<string, string>(ThemeManager.Nord, AppResources.Nord), new KeyValuePair<string, string>("nord", "Nord"),
};
AutoDarkThemeOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(ThemeManager.Dark, AppResources.Dark),
new KeyValuePair<string, string>(ThemeManager.Black, AppResources.Black),
new KeyValuePair<string, string>(ThemeManager.Nord, AppResources.Nord),
}; };
UriMatchOptions = new List<KeyValuePair<UriMatchType?, string>> UriMatchOptions = new List<KeyValuePair<UriMatchType?, string>>
{ {
@@ -78,7 +71,6 @@ namespace Bit.App.Pages
public List<KeyValuePair<int?, string>> ClearClipboardOptions { get; set; } public List<KeyValuePair<int?, string>> ClearClipboardOptions { get; set; }
public List<KeyValuePair<string, string>> ThemeOptions { get; set; } public List<KeyValuePair<string, string>> ThemeOptions { get; set; }
public List<KeyValuePair<string, string>> AutoDarkThemeOptions { get; set; }
public List<KeyValuePair<UriMatchType?, string>> UriMatchOptions { get; set; } public List<KeyValuePair<UriMatchType?, string>> UriMatchOptions { get; set; }
public int ClearClipboardSelectedIndex public int ClearClipboardSelectedIndex
@@ -88,7 +80,7 @@ namespace Bit.App.Pages
{ {
if (SetProperty(ref _clearClipboardSelectedIndex, value)) if (SetProperty(ref _clearClipboardSelectedIndex, value))
{ {
SaveClipboardChangedAsync().FireAndForget(); var task = SaveClipboardChangedAsync();
} }
} }
} }
@@ -98,25 +90,9 @@ namespace Bit.App.Pages
get => _themeSelectedIndex; get => _themeSelectedIndex;
set set
{ {
if (SetProperty(ref _themeSelectedIndex, value, if (SetProperty(ref _themeSelectedIndex, value))
additionalPropertyNames: new[] { nameof(ShowAutoDarkThemeOptions) })
)
{ {
SaveThemeAsync().FireAndForget(); var task = SaveThemeAsync();
}
}
}
public bool ShowAutoDarkThemeOptions => ThemeOptions[ThemeSelectedIndex].Key == null;
public int AutoDarkThemeSelectedIndex
{
get => _autoDarkThemeSelectedIndex;
set
{
if (SetProperty(ref _autoDarkThemeSelectedIndex, value))
{
SaveThemeAsync().FireAndForget();
} }
} }
} }
@@ -128,7 +104,7 @@ namespace Bit.App.Pages
{ {
if (SetProperty(ref _uriMatchSelectedIndex, value)) if (SetProperty(ref _uriMatchSelectedIndex, value))
{ {
SaveDefaultUriAsync().FireAndForget(); var task = SaveDefaultUriAsync();
} }
} }
} }
@@ -140,7 +116,7 @@ namespace Bit.App.Pages
{ {
if (SetProperty(ref _disableFavicon, value)) if (SetProperty(ref _disableFavicon, value))
{ {
UpdateDisableFaviconAsync().FireAndForget(); var task = UpdateDisableFaviconAsync();
} }
} }
} }
@@ -152,7 +128,7 @@ namespace Bit.App.Pages
{ {
if (SetProperty(ref _disableAutoTotpCopy, value)) if (SetProperty(ref _disableAutoTotpCopy, value))
{ {
UpdateAutoTotpCopyAsync().FireAndForget(); var task = UpdateAutoTotpCopyAsync();
} }
} }
} }
@@ -164,7 +140,7 @@ namespace Bit.App.Pages
{ {
if (SetProperty(ref _autofillDisableSavePrompt, value)) if (SetProperty(ref _autofillDisableSavePrompt, value))
{ {
UpdateAutofillDisableSavePromptAsync().FireAndForget(); var task = UpdateAutofillDisableSavePromptAsync();
} }
} }
} }
@@ -190,8 +166,6 @@ namespace Bit.App.Pages
DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault(); DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
var theme = await _stateService.GetThemeAsync(); var theme = await _stateService.GetThemeAsync();
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme); ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
var autoDarkTheme = await _stateService.GetAutoDarkThemeAsync() ?? "dark";
AutoDarkThemeSelectedIndex = AutoDarkThemeOptions.FindIndex(k => k.Key == autoDarkTheme);
var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync(); var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
UriMatchSelectedIndex = defaultUriMatch == null ? 0 : UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch); UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
@@ -228,8 +202,8 @@ namespace Bit.App.Pages
{ {
if (_inited && ThemeSelectedIndex > -1) if (_inited && ThemeSelectedIndex > -1)
{ {
await _stateService.SetThemeAsync(ThemeOptions[ThemeSelectedIndex].Key); var theme = ThemeOptions[ThemeSelectedIndex].Key;
await _stateService.SetAutoDarkThemeAsync(AutoDarkThemeOptions[AutoDarkThemeSelectedIndex].Key); await _stateService.SetThemeAsync(theme);
ThemeManager.SetTheme(Application.Current.Resources); ThemeManager.SetTheme(Application.Current.Resources);
_messagingService.Send("updatedTheme"); _messagingService.Send("updatedTheme");
} }

View File

@@ -161,8 +161,7 @@
Grid.Column="2" Grid.Column="2"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
IsVisible="{Binding Cipher.ViewPassword}" /> IsVisible="{Binding Cipher.ViewPassword}" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"

View File

@@ -299,7 +299,8 @@ namespace Bit.App.Pages
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2; public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
public bool AllowPersonal { get; set; } public bool AllowPersonal { get; set; }
public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None; public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public void Init() public void Init()
{ {

View File

@@ -6,7 +6,6 @@ using Bit.App.Models;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
@@ -56,28 +55,21 @@ namespace Bit.App.Pages
_broadcasterService.Subscribe(nameof(AutofillCiphersPage), async (message) => _broadcasterService.Subscribe(nameof(AutofillCiphersPage), async (message) =>
{ {
try if (message.Command == "syncStarted")
{ {
if (message.Command == "syncStarted") Device.BeginInvokeOnMainThread(() => IsBusy = true);
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
} }
catch (Exception ex) else if (message.Command == "syncCompleted")
{ {
LoggerHelper.LogEvenIfCantBeResolved(ex); await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
} }
}); });

View File

@@ -3,11 +3,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Domain;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
@@ -15,7 +18,7 @@ using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class CiphersPageViewModel : VaultFilterViewModel public class CiphersPageViewModel : BaseViewModel
{ {
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
@@ -28,9 +31,12 @@ namespace Bit.App.Pages
private CancellationTokenSource _searchCancellationTokenSource; private CancellationTokenSource _searchCancellationTokenSource;
private readonly ILogger _logger; private readonly ILogger _logger;
private bool _showVaultFilter;
private string _vaultFilterSelection;
private bool _showNoData; private bool _showNoData;
private bool _showList; private bool _showList;
private bool _websiteIconsEnabled; private bool _websiteIconsEnabled;
private List<Organization> _organizations;
public CiphersPageViewModel() public CiphersPageViewModel()
{ {
@@ -46,19 +52,18 @@ namespace Bit.App.Pages
Ciphers = new ExtendedObservableCollection<CipherView>(); Ciphers = new ExtendedObservableCollection<CipherView>();
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync); CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
VaultFilterCommand = new AsyncCommand(VaultFilterOptionsAsync,
onException: ex => _logger.Exception(ex),
allowsMultipleExecutions: false);
} }
public Command CipherOptionsCommand { get; set; } public Command CipherOptionsCommand { get; set; }
public ICommand VaultFilterCommand { get; }
public ExtendedObservableCollection<CipherView> Ciphers { get; set; } public ExtendedObservableCollection<CipherView> Ciphers { get; set; }
public Func<CipherView, bool> Filter { get; set; } public Func<CipherView, bool> Filter { get; set; }
public string AutofillUrl { get; set; } public string AutofillUrl { get; set; }
public bool Deleted { get; set; } public bool Deleted { get; set; }
protected override ICipherService cipherService => _cipherService;
protected override IPolicyService policyService => _policyService;
protected override IOrganizationService organizationService => _organizationService;
protected override ILogger logger => _logger;
public bool ShowNoData public bool ShowNoData
{ {
get => _showNoData; get => _showNoData;
@@ -76,6 +81,23 @@ namespace Bit.App.Pages
nameof(ShowSearchDirection) nameof(ShowSearchDirection)
}); });
} }
public bool ShowVaultFilter
{
get => _showVaultFilter;
set => SetProperty(ref _showVaultFilter, value);
}
public string VaultFilterDescription
{
get
{
if (_vaultFilterSelection == null || _vaultFilterSelection == AppResources.AllVaults)
{
return string.Format(AppResources.VaultFilterDescription, AppResources.All);
}
return string.Format(AppResources.VaultFilterDescription, _vaultFilterSelection);
}
set => SetProperty(ref _vaultFilterSelection, value);
}
public bool ShowSearchDirection => !ShowList && !ShowNoData; public bool ShowSearchDirection => !ShowList && !ShowNoData;
@@ -87,7 +109,12 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
await InitVaultFilterAsync(true); _organizations = await _organizationService.GetAllAsync();
ShowVaultFilter = await _policyService.ShouldShowVaultFilterAsync();
if (ShowVaultFilter && _vaultFilterSelection == null)
{
_vaultFilterSelection = AppResources.AllVaults;
}
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault(); WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
PerformSearchIfPopulated(); PerformSearchIfPopulated();
} }
@@ -210,11 +237,50 @@ namespace Bit.App.Pages
} }
} }
protected override async Task OnVaultFilterSelectedAsync() private async Task VaultFilterOptionsAsync()
{ {
var options = new List<string> { AppResources.AllVaults, AppResources.MyVault };
if (_organizations.Any())
{
options.AddRange(_organizations.OrderBy(o => o.Name).Select(o => o.Name));
}
var selection = await Page.DisplayActionSheet(AppResources.FilterByVault, AppResources.Cancel, null,
options.ToArray());
if (selection == null || selection == AppResources.Cancel ||
(_vaultFilterSelection == null && selection == AppResources.AllVaults) ||
(_vaultFilterSelection != null && _vaultFilterSelection == selection))
{
return;
}
VaultFilterDescription = selection;
PerformSearchIfPopulated(); PerformSearchIfPopulated();
} }
private async Task<List<CipherView>> GetAllCiphersAsync()
{
var decCiphers = await _cipherService.GetAllDecryptedAsync();
if (IsVaultFilterMyVault)
{
return decCiphers.Where(c => c.OrganizationId == null).ToList();
}
if (IsVaultFilterOrgVault)
{
var orgId = GetVaultFilterOrgId();
return decCiphers.Where(c => c.OrganizationId == orgId).ToList();
}
return decCiphers;
}
private bool IsVaultFilterMyVault => _vaultFilterSelection == AppResources.MyVault;
private bool IsVaultFilterOrgVault => _vaultFilterSelection != AppResources.AllVaults &&
_vaultFilterSelection != AppResources.MyVault;
private string GetVaultFilterOrgId()
{
return _organizations?.FirstOrDefault(o => o.Name == _vaultFilterSelection)?.Id;
}
private async void CipherOptionsAsync(CipherView cipher) private async void CipherOptionsAsync(CipherView cipher)
{ {
if ((Page as BaseContentPage).DoOnce()) if ((Page as BaseContentPage).DoOnce())

View File

@@ -7,7 +7,6 @@ using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
@@ -96,28 +95,21 @@ namespace Bit.App.Pages
_broadcasterService.Subscribe(_pageName, async (message) => _broadcasterService.Subscribe(_pageName, async (message) =>
{ {
try if (message.Command == "syncStarted")
{ {
if (message.Command == "syncStarted") Device.BeginInvokeOnMainThread(() => IsBusy = true);
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
} }
catch (Exception ex) else if (message.Command == "syncCompleted")
{ {
LoggerHelper.LogEvenIfCantBeResolved(ex); await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
} }
}); });

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Controls; using Bit.App.Controls;
using Bit.App.Resources; using Bit.App.Resources;
@@ -16,7 +17,7 @@ using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class GroupingsPageViewModel : VaultFilterViewModel public class GroupingsPageViewModel : BaseViewModel
{ {
private const int NoFolderListSize = 100; private const int NoFolderListSize = 100;
@@ -29,7 +30,10 @@ namespace Bit.App.Pages
private bool _showList; private bool _showList;
private bool _websiteIconsEnabled; private bool _websiteIconsEnabled;
private bool _syncRefreshing; private bool _syncRefreshing;
private bool _showVaultFilter;
private string _vaultFilterSelection;
private string _noDataText; private string _noDataText;
private List<Organization> _organizations;
private List<CipherView> _allCiphers; private List<CipherView> _allCiphers;
private Dictionary<string, int> _folderCounts = new Dictionary<string, int>(); private Dictionary<string, int> _folderCounts = new Dictionary<string, int>();
private Dictionary<string, int> _collectionCounts = new Dictionary<string, int>(); private Dictionary<string, int> _collectionCounts = new Dictionary<string, int>();
@@ -74,6 +78,9 @@ namespace Bit.App.Pages
await LoadAsync(); await LoadAsync();
}); });
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync); CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
VaultFilterCommand = new AsyncCommand(VaultFilterOptionsAsync,
onException: ex => _logger.Exception(ex),
allowsMultipleExecutions: false);
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger) AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
{ {
@@ -101,11 +108,6 @@ namespace Bit.App.Pages
public List<Core.Models.View.CollectionView> Collections { get; set; } public List<Core.Models.View.CollectionView> Collections { get; set; }
public List<TreeNode<Core.Models.View.CollectionView>> NestedCollections { get; set; } public List<TreeNode<Core.Models.View.CollectionView>> NestedCollections { get; set; }
protected override ICipherService cipherService => _cipherService;
protected override IPolicyService policyService => _policyService;
protected override IOrganizationService organizationService => _organizationService;
protected override ILogger logger => _logger;
public bool Refreshing public bool Refreshing
{ {
get => _refreshing; get => _refreshing;
@@ -151,12 +153,30 @@ namespace Bit.App.Pages
get => _websiteIconsEnabled; get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value); set => SetProperty(ref _websiteIconsEnabled, value);
} }
public bool ShowVaultFilter
{
get => _showVaultFilter;
set => SetProperty(ref _showVaultFilter, value);
}
public string VaultFilterDescription
{
get
{
if (_vaultFilterSelection == null || _vaultFilterSelection == AppResources.AllVaults)
{
return string.Format(AppResources.VaultFilterDescription, AppResources.All);
}
return string.Format(AppResources.VaultFilterDescription, _vaultFilterSelection);
}
set => SetProperty(ref _vaultFilterSelection, value);
}
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; } public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; } public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
public Command RefreshCommand { get; set; } public Command RefreshCommand { get; set; }
public Command<CipherView> CipherOptionsCommand { get; set; } public Command<CipherView> CipherOptionsCommand { get; set; }
public ICommand VaultFilterCommand { get; }
public bool LoadedOnce { get; set; } public bool LoadedOnce { get; set; }
public async Task LoadAsync() public async Task LoadAsync()
@@ -181,9 +201,14 @@ namespace Bit.App.Pages
return; return;
} }
await InitVaultFilterAsync(MainPage); _organizations = await _organizationService.GetAllAsync();
if (MainPage) if (MainPage)
{ {
ShowVaultFilter = await _policyService.ShouldShowVaultFilterAsync();
if (ShowVaultFilter && _vaultFilterSelection == null)
{
_vaultFilterSelection = AppResources.AllVaults;
}
PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault; PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault;
} }
@@ -369,11 +394,30 @@ namespace Bit.App.Pages
SyncRefreshing = false; SyncRefreshing = false;
} }
protected override async Task OnVaultFilterSelectedAsync() public async Task VaultFilterOptionsAsync()
{ {
var options = new List<string> { AppResources.AllVaults, AppResources.MyVault };
if (_organizations.Any())
{
options.AddRange(_organizations.OrderBy(o => o.Name).Select(o => o.Name));
}
var selection = await Page.DisplayActionSheet(AppResources.FilterByVault, AppResources.Cancel, null,
options.ToArray());
if (selection == null || selection == AppResources.Cancel ||
(_vaultFilterSelection == null && selection == AppResources.AllVaults) ||
(_vaultFilterSelection != null && _vaultFilterSelection == selection))
{
return;
}
VaultFilterDescription = selection;
await LoadAsync(); await LoadAsync();
} }
public string GetVaultFilterOrgId()
{
return _organizations?.FirstOrDefault(o => o.Name == _vaultFilterSelection)?.Id;
}
public async Task SelectCipherAsync(CipherView cipher) public async Task SelectCipherAsync(CipherView cipher)
{ {
var page = new ViewPage(cipher.Id); var page = new ViewPage(cipher.Id);
@@ -457,8 +501,8 @@ namespace Bit.App.Pages
private async Task LoadDataAsync() private async Task LoadDataAsync()
{ {
var orgId = await FillAllCiphersAndGetOrgIdIfNeededAsync();
NoDataText = AppResources.NoItems; NoDataText = AppResources.NoItems;
_allCiphers = await GetAllCiphersAsync();
HasCiphers = _allCiphers.Any(); HasCiphers = _allCiphers.Any();
FavoriteCiphers?.Clear(); FavoriteCiphers?.Clear();
NoFolderCiphers?.Clear(); NoFolderCiphers?.Clear();
@@ -472,7 +516,7 @@ namespace Bit.App.Pages
if (MainPage) if (MainPage)
{ {
await FillFoldersAndCollectionsAsync(); await FillFoldersAndCollectionsAsync(orgId);
NestedFolders = await _folderService.GetAllNestedAsync(Folders); NestedFolders = await _folderService.GetAllNestedAsync(Folders);
HasFolders = NestedFolders.Any(f => f.Node?.Id != null); HasFolders = NestedFolders.Any(f => f.Node?.Id != null);
NestedCollections = Collections != null ? await _collectionService.GetAllNestedAsync(Collections) : null; NestedCollections = Collections != null ? await _collectionService.GetAllNestedAsync(Collections) : null;
@@ -596,9 +640,28 @@ namespace Bit.App.Pages
} }
} }
private async Task FillFoldersAndCollectionsAsync() private async Task<string> FillAllCiphersAndGetOrgIdIfNeededAsync()
{
string orgId = null;
var decCiphers = await _cipherService.GetAllDecryptedAsync();
if (IsVaultFilterMyVault)
{
_allCiphers = decCiphers.Where(c => c.OrganizationId == null).ToList();
}
else if (IsVaultFilterOrgVault)
{
orgId = GetVaultFilterOrgId();
_allCiphers = decCiphers.Where(c => c.OrganizationId == orgId).ToList();
}
else
{
_allCiphers = decCiphers;
}
return orgId;
}
private async Task FillFoldersAndCollectionsAsync(string orgId)
{ {
var orgId = GetVaultFilterOrgId();
var decFolders = await _folderService.GetAllDecryptedAsync(); var decFolders = await _folderService.GetAllDecryptedAsync();
var decCollections = await _collectionService.GetAllDecryptedAsync(); var decCollections = await _collectionService.GetAllDecryptedAsync();
if (IsVaultFilterMyVault) if (IsVaultFilterMyVault)
@@ -618,6 +681,11 @@ namespace Bit.App.Pages
} }
} }
private bool IsVaultFilterMyVault => _vaultFilterSelection == AppResources.MyVault;
private bool IsVaultFilterOrgVault => _vaultFilterSelection != AppResources.AllVaults &&
_vaultFilterSelection != AppResources.MyVault;
private List<FolderView> BuildFolders(List<FolderView> decFolders) private List<FolderView> BuildFolders(List<FolderView> decFolders)
{ {
var folders = decFolders.Where(f => _allCiphers.Any(c => c.FolderId == f.Id)).ToList(); var folders = decFolders.Where(f => _allCiphers.Any(c => c.FolderId == f.Id)).ToList();

View File

@@ -144,8 +144,7 @@
Grid.Column="2" Grid.Column="2"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
IsVisible="{Binding Cipher.ViewPassword}" /> IsVisible="{Binding Cipher.ViewPassword}" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
@@ -57,44 +56,37 @@ namespace Bit.App.Pages
_broadcasterService.Subscribe(nameof(ViewPage), async (message) => _broadcasterService.Subscribe(nameof(ViewPage), async (message) =>
{ {
try if (message.Command == "syncStarted")
{ {
if (message.Command == "syncStarted") Device.BeginInvokeOnMainThread(() => IsBusy = true);
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
{
var success = data["successfully"] as bool?;
if (success.GetValueOrDefault())
{
var task = _vm.LoadAsync(() => AdjustToolbar());
}
}
});
}
else if (message.Command == "selectSaveFileResult")
{
Device.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<string, string>;
if (data == null)
{
return;
}
_vm.SaveFileSelected(data.Item1, data.Item2);
});
}
} }
catch (Exception ex) else if (message.Command == "syncCompleted")
{ {
LoggerHelper.LogEvenIfCantBeResolved(ex); await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
{
var success = data["successfully"] as bool?;
if (success.GetValueOrDefault())
{
var task = _vm.LoadAsync(() => AdjustToolbar());
}
}
});
}
else if (message.Command == "selectSaveFileResult")
{
Device.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<string, string>;
if (data == null)
{
return;
}
_vm.SaveFileSelected(data.Item1, data.Item2);
});
} }
}); });
await LoadOnAppearedAsync(_scrollView, true, async () => await LoadOnAppearedAsync(_scrollView, true, async () =>

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities; using Bit.App.Utilities;
@@ -12,7 +11,6 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -30,7 +28,6 @@ namespace Bit.App.Pages
private readonly IPasswordRepromptService _passwordRepromptService; private readonly IPasswordRepromptService _passwordRepromptService;
private readonly ILocalizeService _localizeService; private readonly ILocalizeService _localizeService;
private readonly IClipboardService _clipboardService; private readonly IClipboardService _clipboardService;
private readonly ILogger _logger;
private CipherView _cipher; private CipherView _cipher;
private List<ViewPageFieldViewModel> _fields; private List<ViewPageFieldViewModel> _fields;
@@ -61,11 +58,10 @@ namespace Bit.App.Pages
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"); _passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService"); _localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService"); _clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
CopyCommand = new AsyncCommand<string>((id) => CopyAsync(id, null), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false); CopyCommand = new Command<string>((id) => CopyAsync(id, null));
CopyUriCommand = new AsyncCommand<LoginUriView>(uriView => CopyAsync("LoginUri", uriView.Uri), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false); CopyUriCommand = new Command<LoginUriView>(CopyUri);
CopyFieldCommand = new AsyncCommand<FieldView>(field => CopyAsync(field.Type == FieldType.Hidden ? "H_FieldValue" : "FieldValue", field.Value), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false); CopyFieldCommand = new Command<FieldView>(CopyField);
LaunchUriCommand = new Command<LoginUriView>(LaunchUri); LaunchUriCommand = new Command<LoginUriView>(LaunchUri);
TogglePasswordCommand = new Command(TogglePassword); TogglePasswordCommand = new Command(TogglePassword);
ToggleCardNumberCommand = new Command(ToggleCardNumber); ToggleCardNumberCommand = new Command(ToggleCardNumber);
@@ -76,9 +72,9 @@ namespace Bit.App.Pages
PageTitle = AppResources.ViewItem; PageTitle = AppResources.ViewItem;
} }
public ICommand CopyCommand { get; set; } public Command CopyCommand { get; set; }
public ICommand CopyUriCommand { get; set; } public Command CopyUriCommand { get; set; }
public ICommand CopyFieldCommand { get; set; } public Command CopyFieldCommand { get; set; }
public Command LaunchUriCommand { get; set; } public Command LaunchUriCommand { get; set; }
public Command TogglePasswordCommand { get; set; } public Command TogglePasswordCommand { get; set; }
public Command ToggleCardNumberCommand { get; set; } public Command ToggleCardNumberCommand { get; set; }
@@ -218,7 +214,8 @@ namespace Bit.App.Pages
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowCardNumberIcon => ShowCardNumber ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowCardNumberIcon => ShowCardNumber ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowCardCodeIcon => ShowCardCode ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowCardCodeIcon => ShowCardCode ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
public string TotpCodeFormatted public string TotpCodeFormatted
{ {
get => _totpCodeFormatted; get => _totpCodeFormatted;
@@ -620,7 +617,7 @@ namespace Bit.App.Pages
_attachmentFilename = null; _attachmentFilename = null;
} }
private async Task CopyAsync(string id, string text = null) private async void CopyAsync(string id, string text = null)
{ {
if (_passwordRepromptService.ProtectedFields.Contains(id) && !await PromptPasswordAsync()) if (_passwordRepromptService.ProtectedFields.Contains(id) && !await PromptPasswordAsync())
{ {
@@ -684,6 +681,16 @@ namespace Bit.App.Pages
} }
} }
private void CopyUri(LoginUriView uri)
{
CopyAsync("LoginUri", uri.Uri);
}
private void CopyField(FieldView field)
{
CopyAsync(field.Type == Core.Enums.FieldType.Hidden ? "H_FieldValue" : "FieldValue", field.Value);
}
private void LaunchUri(LoginUriView uri) private void LaunchUri(LoginUriView uri)
{ {
if (uri.CanLaunch && (Page as BaseContentPage).DoOnce()) if (uri.CanLaunch && (Page as BaseContentPage).DoOnce())

View File

@@ -1,123 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.View;
using Xamarin.CommunityToolkit.ObjectModel;
namespace Bit.App.Pages
{
public abstract class VaultFilterViewModel : BaseViewModel
{
protected abstract ICipherService cipherService { get; }
protected abstract IPolicyService policyService { get; }
protected abstract IOrganizationService organizationService { get; }
protected abstract ILogger logger { get; }
protected bool _showVaultFilter;
protected bool _personalOwnershipPolicyApplies;
protected string _vaultFilterSelection;
protected List<Organization> _organizations;
public VaultFilterViewModel()
{
VaultFilterCommand = new AsyncCommand(VaultFilterOptionsAsync,
onException: ex => logger.Exception(ex),
allowsMultipleExecutions: false);
}
public ICommand VaultFilterCommand { get; set; }
public bool ShowVaultFilter
{
get => _showVaultFilter;
set => SetProperty(ref _showVaultFilter, value);
}
public string VaultFilterDescription
{
get
{
if (_vaultFilterSelection == null || _vaultFilterSelection == AppResources.AllVaults)
{
return string.Format(AppResources.VaultFilterDescription, AppResources.All);
}
return string.Format(AppResources.VaultFilterDescription, _vaultFilterSelection);
}
set => SetProperty(ref _vaultFilterSelection, value);
}
public string GetVaultFilterOrgId()
{
return _organizations?.FirstOrDefault(o => o.Name == _vaultFilterSelection)?.Id;
}
protected bool IsVaultFilterMyVault => _vaultFilterSelection == AppResources.MyVault;
protected bool IsVaultFilterOrgVault => _vaultFilterSelection != AppResources.AllVaults &&
_vaultFilterSelection != AppResources.MyVault;
protected async Task InitVaultFilterAsync(bool shouldUpdateShowVaultFilter)
{
_organizations = await organizationService.GetAllAsync();
if (_organizations?.Any() ?? false)
{
_personalOwnershipPolicyApplies = await policyService.PolicyAppliesToUser(PolicyType.PersonalOwnership);
var singleOrgPolicyApplies = await policyService.PolicyAppliesToUser(PolicyType.OnlyOrg);
if (_vaultFilterSelection == null || (_personalOwnershipPolicyApplies && singleOrgPolicyApplies))
{
VaultFilterDescription = AppResources.AllVaults;
}
}
if (shouldUpdateShowVaultFilter)
{
await Task.Delay(100);
ShowVaultFilter = await policyService.ShouldShowVaultFilterAsync();
}
}
protected async Task<List<CipherView>> GetAllCiphersAsync()
{
var decCiphers = await cipherService.GetAllDecryptedAsync();
if (IsVaultFilterMyVault)
{
return decCiphers.Where(c => c.OrganizationId == null).ToList();
}
if (IsVaultFilterOrgVault)
{
var orgId = GetVaultFilterOrgId();
return decCiphers.Where(c => c.OrganizationId == orgId).ToList();
}
return decCiphers;
}
protected async Task VaultFilterOptionsAsync()
{
var options = new List<string> { AppResources.AllVaults };
if (!_personalOwnershipPolicyApplies)
{
options.Add(AppResources.MyVault);
}
if (_organizations.Any())
{
options.AddRange(_organizations.OrderBy(o => o.Name).Select(o => o.Name));
}
var selection = await Page.DisplayActionSheet(AppResources.FilterByVault, AppResources.Cancel, null,
options.ToArray());
if (selection == null || selection == AppResources.Cancel ||
(_vaultFilterSelection == null && selection == AppResources.AllVaults) ||
(_vaultFilterSelection != null && _vaultFilterSelection == selection))
{
return;
}
VaultFilterDescription = selection;
await OnVaultFilterSelectedAsync();
}
protected abstract Task OnVaultFilterSelectedAsync();
}
}

View File

@@ -1,6 +1,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@@ -9,9 +10,10 @@
namespace Bit.App.Resources { namespace Bit.App.Resources {
using System; using System;
using System.Reflection;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.1.0.0")] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()] [System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class AppResources { public class AppResources {
@@ -2697,18 +2699,6 @@ namespace Bit.App.Resources {
} }
} }
public static string DefaultDarkTheme {
get {
return ResourceManager.GetString("DefaultDarkTheme", resourceCulture);
}
}
public static string DefaultDarkThemeDescription {
get {
return ResourceManager.GetString("DefaultDarkThemeDescription", resourceCulture);
}
}
public static string CopyNotes { public static string CopyNotes {
get { get {
return ResourceManager.GetString("CopyNotes", resourceCulture); return ResourceManager.GetString("CopyNotes", resourceCulture);
@@ -2739,12 +2729,6 @@ namespace Bit.App.Resources {
} }
} }
public static string Nord {
get {
return ResourceManager.GetString("Nord", resourceCulture);
}
}
public static string BlacklistedUris { public static string BlacklistedUris {
get { get {
return ResourceManager.GetString("BlacklistedUris", resourceCulture); return ResourceManager.GetString("BlacklistedUris", resourceCulture);
@@ -3993,15 +3977,15 @@ namespace Bit.App.Resources {
} }
} }
public static string PasswordIsVisibleTapToHide { public static string VisibilityTogglePasswordIsVisibleActivateToHide {
get { get {
return ResourceManager.GetString("PasswordIsVisibleTapToHide", resourceCulture); return ResourceManager.GetString("VisibilityTogglePasswordIsVisibleActivateToHide", resourceCulture);
} }
} }
public static string PasswordIsNotVisibleTapToShow { public static string VisibilityTogglePasswordIsNotVisibleActivateToHide {
get { get {
return ResourceManager.GetString("PasswordIsNotVisibleTapToShow", resourceCulture); return ResourceManager.GetString("VisibilityTogglePasswordIsNotVisibleActivateToHide", resourceCulture);
} }
} }

View File

@@ -1541,12 +1541,6 @@
<data name="ThemeDefault" xml:space="preserve"> <data name="ThemeDefault" xml:space="preserve">
<value>Default (System)</value> <value>Default (System)</value>
</data> </data>
<data name="DefaultDarkTheme" xml:space="preserve">
<value>Default Dark Theme</value>
</data>
<data name="DefaultDarkThemeDescription" xml:space="preserve">
<value>Choose the dark theme to use when using Default (System) theme while your device's dark mode is enabled</value>
</data>
<data name="CopyNotes" xml:space="preserve"> <data name="CopyNotes" xml:space="preserve">
<value>Copy Note</value> <value>Copy Note</value>
</data> </data>
@@ -1563,10 +1557,6 @@
<value>Black</value> <value>Black</value>
<comment>The color black</comment> <comment>The color black</comment>
</data> </data>
<data name="Nord" xml:space="preserve">
<value>Nord</value>
<comment>'Nord' is the name of a specific color scheme. It should not be translated.</comment>
</data>
<data name="BlacklistedUris" xml:space="preserve"> <data name="BlacklistedUris" xml:space="preserve">
<value>Blacklisted URIs</value> <value>Blacklisted URIs</value>
</data> </data>
@@ -2233,11 +2223,11 @@
<data name="TapToGoBack" xml:space="preserve"> <data name="TapToGoBack" xml:space="preserve">
<value>Tap to go back</value> <value>Tap to go back</value>
</data> </data>
<data name="PasswordIsVisibleTapToHide" xml:space="preserve"> <data name="VisibilityTogglePasswordIsVisibleActivateToHide" xml:space="preserve">
<value>Password is visible, tap to hide.</value> <value>Visibility Toggle, Password is visible, activate to hide.</value>
</data> </data>
<data name="PasswordIsNotVisibleTapToShow" xml:space="preserve"> <data name="VisibilityTogglePasswordIsNotVisibleActivateToHide" xml:space="preserve">
<value>Password is not visible, tap to show.</value> <value>Visibility Toggle, Password is not visible, activate to show.</value>
</data> </data>
<data name="FilterByVault" xml:space="preserve"> <data name="FilterByVault" xml:space="preserve">
<value>Filter items by vault</value> <value>Filter items by vault</value>

View File

@@ -25,7 +25,6 @@ namespace Bit.App.Services
Constants.LastBuildKey, Constants.LastBuildKey,
Constants.ClearCiphersCacheKey, Constants.ClearCiphersCacheKey,
Constants.BiometricIntegrityKey, Constants.BiometricIntegrityKey,
Constants.iOSExtensionActiveUserIdKey,
Constants.iOSAutoFillClearCiphersCacheKey, Constants.iOSAutoFillClearCiphersCacheKey,
Constants.iOSAutoFillBiometricIntegrityKey, Constants.iOSAutoFillBiometricIntegrityKey,
Constants.iOSExtensionClearCiphersCacheKey, Constants.iOSExtensionClearCiphersCacheKey,
@@ -33,7 +32,7 @@ namespace Bit.App.Services
Constants.iOSShareExtensionClearCiphersCacheKey, Constants.iOSShareExtensionClearCiphersCacheKey,
Constants.iOSShareExtensionBiometricIntegrityKey, Constants.iOSShareExtensionBiometricIntegrityKey,
Constants.RememberedEmailKey, Constants.RememberedEmailKey,
Constants.RememberedOrgIdentifierKey Constants.RememberedOrgIdentifierKey,
}; };
public MobileStorageService( public MobileStorageService(

View File

@@ -6,11 +6,21 @@ using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Utilities.AccountManagement namespace Bit.App.Utilities.AccountManagement
{ {
public static class AccountsManagerMessageCommands
{
public const string LOCKED = "locked";
public const string LOCK_VAULT = "lockVault";
public const string LOGOUT = "logout";
public const string LOGGED_OUT = "loggedOut";
public const string ADD_ACCOUNT = "addAccount";
public const string ACCOUNT_ADDED = "accountAdded";
public const string SWITCHED_ACCOUNT = "switchedAccount";
}
public class AccountsManager : IAccountsManager public class AccountsManager : IAccountsManager
{ {
private readonly IBroadcasterService _broadcasterService; private readonly IBroadcasterService _broadcasterService;
@@ -199,7 +209,7 @@ namespace Bit.App.Utilities.AccountManagement
private async Task SwitchedAccountAsync() private async Task SwitchedAccountAsync()
{ {
await AppHelpers.OnAccountSwitchAsync(); await AppHelpers.OnAccountSwitchAsync();
await Device.InvokeOnMainThreadAsync(async () => Device.BeginInvokeOnMainThread(async () =>
{ {
if (await _vaultTimeoutService.ShouldTimeoutAsync()) if (await _vaultTimeoutService.ShouldTimeoutAsync())
{ {

View File

@@ -17,18 +17,13 @@ namespace Bit.App.Utilities
public static bool IsThemeDirty = false; public static bool IsThemeDirty = false;
public const string Light = "light"; public static void SetThemeStyle(string name, ResourceDictionary resources)
public const string Dark = "dark";
public const string Black = "black";
public const string Nord = "nord";
public static void SetThemeStyle(string name, string autoDarkName, ResourceDictionary resources)
{ {
try try
{ {
Resources = () => resources; Resources = () => resources;
var newTheme = NeedsThemeUpdate(name, autoDarkName, resources); var newTheme = NeedsThemeUpdate(name, resources);
if (newTheme is null) if (newTheme is null)
{ {
return; return;
@@ -90,30 +85,22 @@ namespace Bit.App.Utilities
: Activator.CreateInstance(themeType) as ResourceDictionary; : Activator.CreateInstance(themeType) as ResourceDictionary;
} }
static ResourceDictionary NeedsThemeUpdate(string themeName, string autoDarkThemeName, ResourceDictionary resources) static ResourceDictionary NeedsThemeUpdate(string themeName, ResourceDictionary resources)
{ {
switch (themeName) switch (themeName)
{ {
case Dark: case "dark":
return CheckAndGetThemeForMergedDictionaries(typeof(Dark), resources); return CheckAndGetThemeForMergedDictionaries(typeof(Dark), resources);
case Black: case "black":
return CheckAndGetThemeForMergedDictionaries(typeof(Black), resources); return CheckAndGetThemeForMergedDictionaries(typeof(Black), resources);
case Nord: case "nord":
return CheckAndGetThemeForMergedDictionaries(typeof(Nord), resources); return CheckAndGetThemeForMergedDictionaries(typeof(Nord), resources);
case Light: case "light":
return CheckAndGetThemeForMergedDictionaries(typeof(Light), resources); return CheckAndGetThemeForMergedDictionaries(typeof(Light), resources);
default: default:
if (OsDarkModeEnabled()) if (OsDarkModeEnabled())
{ {
switch (autoDarkThemeName) return CheckAndGetThemeForMergedDictionaries(typeof(Dark), resources);
{
case Black:
return CheckAndGetThemeForMergedDictionaries(typeof(Black), resources);
case Nord:
return CheckAndGetThemeForMergedDictionaries(typeof(Nord), resources);
default:
return CheckAndGetThemeForMergedDictionaries(typeof(Dark), resources);
}
} }
return CheckAndGetThemeForMergedDictionaries(typeof(Light), resources); return CheckAndGetThemeForMergedDictionaries(typeof(Light), resources);
} }
@@ -121,7 +108,7 @@ namespace Bit.App.Utilities
public static void SetTheme(ResourceDictionary resources) public static void SetTheme(ResourceDictionary resources)
{ {
SetThemeStyle(GetTheme(), GetAutoDarkTheme(), resources); SetThemeStyle(GetTheme(), resources);
} }
public static string GetTheme() public static string GetTheme()
@@ -130,12 +117,6 @@ namespace Bit.App.Utilities
return stateService.GetThemeAsync().GetAwaiter().GetResult(); return stateService.GetThemeAsync().GetAwaiter().GetResult();
} }
public static string GetAutoDarkTheme()
{
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
return stateService.GetAutoDarkThemeAsync().GetAwaiter().GetResult();
}
public static bool OsDarkModeEnabled() public static bool OsDarkModeEnabled()
{ {
if (Application.Current == null) if (Application.Current == null)

View File

@@ -44,7 +44,7 @@ namespace Bit.Core.Abstractions
Task PutDeleteCipherAsync(string id); Task PutDeleteCipherAsync(string id);
Task<CipherResponse> PutRestoreCipherAsync(string id); Task<CipherResponse> PutRestoreCipherAsync(string id);
Task RefreshIdentityTokenAsync(); Task RefreshIdentityTokenAsync();
Task<SsoPrevalidateResponse> PreValidateSso(string identifier); Task<object> PreValidateSso(string identifier);
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path, Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
TRequest body, bool authed, bool hasResponse, bool logoutOnUnauthorized = true); TRequest body, bool authed, bool hasResponse, bool logoutOnUnauthorized = true);
void SetUrls(EnvironmentUrls urls); void SetUrls(EnvironmentUrls urls);

View File

@@ -5,8 +5,7 @@ namespace Bit.Core.Abstractions
{ {
public interface IBroadcasterService public interface IBroadcasterService
{ {
void Send(Message message); void Send(Message message, string id = null);
void Send(Message message, string id);
void Subscribe(string id, Action<Message> messageCallback); void Subscribe(string id, Action<Message> messageCallback);
void Unsubscribe(string id); void Unsubscribe(string id);
} }

View File

@@ -14,7 +14,6 @@ namespace Bit.Core.Abstractions
Task<string> GetActiveUserIdAsync(); Task<string> GetActiveUserIdAsync();
Task<bool> IsActiveAccountAsync(string userId = null); Task<bool> IsActiveAccountAsync(string userId = null);
Task SetActiveUserAsync(string userId); Task SetActiveUserAsync(string userId);
Task CheckExtensionActiveUserAndSwitchIfNeededAsync();
Task<bool> IsAuthenticatedAsync(string userId = null); Task<bool> IsAuthenticatedAsync(string userId = null);
Task<string> GetUserIdAsync(string email); Task<string> GetUserIdAsync(string email);
Task RefreshAccountViewsAsync(bool allowAddAccountRow); Task RefreshAccountViewsAsync(bool allowAddAccountRow);
@@ -110,8 +109,6 @@ namespace Bit.Core.Abstractions
Task SetRememberedOrgIdentifierAsync(string value); Task SetRememberedOrgIdentifierAsync(string value);
Task<string> GetThemeAsync(string userId = null); Task<string> GetThemeAsync(string userId = null);
Task SetThemeAsync(string value, string userId = null); Task SetThemeAsync(string value, string userId = null);
Task<string> GetAutoDarkThemeAsync(string userId = null);
Task SetAutoDarkThemeAsync(string value, string userId = null);
Task<bool?> GetAddSitePromptShownAsync(string userId = null); Task<bool?> GetAddSitePromptShownAsync(string userId = null);
Task SetAddSitePromptShownAsync(bool? value, string userId = null); Task SetAddSitePromptShownAsync(bool? value, string userId = null);
Task<bool?> GetPushInitialPromptShownAsync(); Task<bool?> GetPushInitialPromptShownAsync();
@@ -148,6 +145,5 @@ namespace Bit.Core.Abstractions
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null); Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
Task<string> GetTwoFactorTokenAsync(string email = null); Task<string> GetTwoFactorTokenAsync(string email = null);
Task SetTwoFactorTokenAsync(string value, string email = null); Task SetTwoFactorTokenAsync(string value, string email = null);
Task SaveExtensionActiveUserIdToStorageAsync(string userId);
} }
} }

View File

@@ -25,7 +25,6 @@
public static string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState"; public static string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState";
public static string iOSShareExtensionClearCiphersCacheKey = "iOSShareExtensionClearCiphersCache"; public static string iOSShareExtensionClearCiphersCacheKey = "iOSShareExtensionClearCiphersCache";
public static string iOSShareExtensionBiometricIntegrityKey = "iOSShareExtensionBiometricIntegrityState"; public static string iOSShareExtensionBiometricIntegrityKey = "iOSShareExtensionBiometricIntegrityState";
public static string iOSExtensionActiveUserIdKey = "iOSExtensionActiveUserId";
public static string EventCollectionKey = "eventCollection"; public static string EventCollectionKey = "eventCollection";
public static string RememberedEmailKey = "rememberedEmail"; public static string RememberedEmailKey = "rememberedEmail";
public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier"; public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier";
@@ -73,7 +72,6 @@
public static string DisableFaviconKey(string userId) => $"disableFavicon_{userId}"; public static string DisableFaviconKey(string userId) => $"disableFavicon_{userId}";
public static string DefaultUriMatchKey(string userId) => $"defaultUriMatch_{userId}"; public static string DefaultUriMatchKey(string userId) => $"defaultUriMatch_{userId}";
public static string ThemeKey(string userId) => $"theme_{userId}"; public static string ThemeKey(string userId) => $"theme_{userId}";
public static string AutoDarkThemeKey(string userId) => $"autoDarkTheme_{userId}";
public static string DisableAutoTotpCopyKey(string userId) => $"disableAutoTotpCopy_{userId}"; public static string DisableAutoTotpCopyKey(string userId) => $"disableAutoTotpCopy_{userId}";
public static string PreviousPageKey(string userId) => $"previousPage_{userId}"; public static string PreviousPageKey(string userId) => $"previousPage_{userId}";
public static string PasswordRepromptAutofillKey(string userId) => $"passwordRepromptAutofillKey_{userId}"; public static string PasswordRepromptAutofillKey(string userId) => $"passwordRepromptAutofillKey_{userId}";

View File

@@ -1,7 +0,0 @@
namespace Bit.Core.Models.Response
{
public class SsoPrevalidateResponse
{
public string Token { get; set; }
}
}

View File

@@ -547,7 +547,7 @@ namespace Bit.Core.Services
return accessToken; return accessToken;
} }
public async Task<SsoPrevalidateResponse> PreValidateSso(string identifier) public async Task<object> PreValidateSso(string identifier)
{ {
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier); var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
using (var requestMessage = new HttpRequestMessage()) using (var requestMessage = new HttpRequestMessage())
@@ -571,8 +571,7 @@ namespace Bit.Core.Services
var error = await HandleErrorAsync(response, false, true); var error = await HandleErrorAsync(response, false, true);
throw new ApiException(error); throw new ApiException(error);
} }
var responseJsonString = await response.Content.ReadAsStringAsync(); return null;
return JsonConvert.DeserializeObject<SsoPrevalidateResponse>(responseJsonString);
} }
} }

View File

@@ -8,58 +8,24 @@ namespace Bit.App.Services
{ {
public class BroadcasterService : IBroadcasterService public class BroadcasterService : IBroadcasterService
{ {
private readonly ILogger _logger;
private readonly Dictionary<string, Action<Message>> _subscribers = new Dictionary<string, Action<Message>>(); private readonly Dictionary<string, Action<Message>> _subscribers = new Dictionary<string, Action<Message>>();
private object _myLock = new object(); private object _myLock = new object();
public BroadcasterService(ILogger logger) public void Send(Message message, string id = null)
{
_logger = logger;
}
public void Send(Message message)
{ {
lock (_myLock) lock (_myLock)
{ {
if (!string.IsNullOrWhiteSpace(id))
{
if (_subscribers.ContainsKey(id))
{
Task.Run(() => _subscribers[id].Invoke(message));
}
return;
}
foreach (var sub in _subscribers) foreach (var sub in _subscribers)
{ {
Task.Run(() => Task.Run(() => sub.Value.Invoke(message));
{
try
{
sub.Value(message);
}
catch (Exception ex)
{
_logger.Exception(ex);
}
});
}
}
}
public void Send(Message message, string id)
{
if (string.IsNullOrWhiteSpace(id))
{
return;
}
lock (_myLock)
{
if (_subscribers.TryGetValue(id, out var action))
{
Task.Run(() =>
{
try
{
action(message);
}
catch (Exception ex)
{
_logger.Exception(ex);
}
});
} }
} }
} }
@@ -68,7 +34,14 @@ namespace Bit.App.Services
{ {
lock (_myLock) lock (_myLock)
{ {
_subscribers[id] = messageCallback; if (_subscribers.ContainsKey(id))
{
_subscribers[id] = messageCallback;
}
else
{
_subscribers.Add(id, messageCallback);
}
} }
} }

View File

@@ -249,12 +249,6 @@ namespace Bit.Core.Services
public async Task<bool> ShouldShowVaultFilterAsync() public async Task<bool> ShouldShowVaultFilterAsync()
{ {
var personalOwnershipPolicyApplies = await PolicyAppliesToUser(PolicyType.PersonalOwnership);
var singleOrgPolicyApplies = await PolicyAppliesToUser(PolicyType.OnlyOrg);
if (personalOwnershipPolicyApplies && singleOrgPolicyApplies)
{
return false;
}
var organizations = await _organizationService.GetAllAsync(); var organizations = await _organizationService.GetAllAsync();
return organizations?.Any() ?? false; return organizations?.Any() ?? false;
} }

View File

@@ -15,20 +15,16 @@ namespace Bit.Core.Services
{ {
private readonly IStorageService _storageService; private readonly IStorageService _storageService;
private readonly IStorageService _secureStorageService; private readonly IStorageService _secureStorageService;
private readonly IMessagingService _messagingService;
private State _state; private State _state;
private bool _migrationChecked; private bool _migrationChecked;
public List<AccountView> AccountViews { get; set; } public List<AccountView> AccountViews { get; set; }
public StateService(IStorageService storageService, public StateService(IStorageService storageService, IStorageService secureStorageService)
IStorageService secureStorageService,
IMessagingService messagingService)
{ {
_storageService = storageService; _storageService = storageService;
_secureStorageService = secureStorageService; _secureStorageService = secureStorageService;
_messagingService = messagingService;
} }
public async Task<string> GetActiveUserIdAsync() public async Task<string> GetActiveUserIdAsync()
@@ -71,28 +67,6 @@ namespace Bit.Core.Services
await SetPreAuthEnvironmentUrlsAsync(await GetEnvironmentUrlsAsync()); await SetPreAuthEnvironmentUrlsAsync(await GetEnvironmentUrlsAsync());
} }
public async Task CheckExtensionActiveUserAndSwitchIfNeededAsync()
{
var extensionUserId = await GetExtensionActiveUserIdFromStorageAsync();
if (string.IsNullOrEmpty(extensionUserId))
{
return;
}
if (_state?.ActiveUserId == extensionUserId)
{
// Clear the value in case the user changes the active user from the app
// so if that happens and the user sends the app to background and comes back
// the user is not changed again.
await SaveExtensionActiveUserIdToStorageAsync(null);
return;
}
await SetActiveUserAsync(extensionUserId);
await SaveExtensionActiveUserIdToStorageAsync(null);
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
}
public async Task<bool> IsAuthenticatedAsync(string userId = null) public async Task<bool> IsAuthenticatedAsync(string userId = null)
{ {
return await GetAccessTokenAsync(userId) != null; return await GetAccessTokenAsync(userId) != null;
@@ -924,25 +898,6 @@ namespace Bit.Core.Services
SetValueGloballyAsync(Constants.ThemeKey, value, reconciledOptions).FireAndForget(); SetValueGloballyAsync(Constants.ThemeKey, value, reconciledOptions).FireAndForget();
} }
public async Task<string> GetAutoDarkThemeAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var key = Constants.AutoDarkThemeKey(reconciledOptions.UserId);
return await GetValueAsync<string>(key, reconciledOptions);
}
public async Task SetAutoDarkThemeAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var key = Constants.AutoDarkThemeKey(reconciledOptions.UserId);
await SetValueAsync(key, value, reconciledOptions);
// TODO remove this to restore per-account Theme support
SetValueGloballyAsync(Constants.AutoDarkThemeKey, value, reconciledOptions).FireAndForget();
}
public async Task<bool?> GetAddSitePromptShownAsync(string userId = null) public async Task<bool?> GetAddSitePromptShownAsync(string userId = null)
{ {
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1433,7 +1388,6 @@ namespace Bit.Core.Services
await SetPasswordVerifiedAutofillAsync(null, userId); await SetPasswordVerifiedAutofillAsync(null, userId);
await SetSyncOnRefreshAsync(null, userId); await SetSyncOnRefreshAsync(null, userId);
await SetThemeAsync(null, userId); await SetThemeAsync(null, userId);
await SetAutoDarkThemeAsync(null, userId);
await SetAddSitePromptShownAsync(null, userId); await SetAddSitePromptShownAsync(null, userId);
await SetPasswordGenerationOptionsAsync(null, userId); await SetPasswordGenerationOptionsAsync(null, userId);
} }
@@ -1443,7 +1397,6 @@ namespace Bit.Core.Services
{ {
await CheckStateAsync(); await CheckStateAsync();
var currentTheme = await GetThemeAsync(); var currentTheme = await GetThemeAsync();
var currentAutoDarkTheme = await GetAutoDarkThemeAsync();
var currentDisableFavicons = await GetDisableFaviconAsync(); var currentDisableFavicons = await GetDisableFaviconAsync();
account.Settings.EnvironmentUrls = await GetPreAuthEnvironmentUrlsAsync(); account.Settings.EnvironmentUrls = await GetPreAuthEnvironmentUrlsAsync();
@@ -1473,7 +1426,6 @@ namespace Bit.Core.Services
account.Settings.VaultTimeoutAction = VaultTimeoutAction.Lock; account.Settings.VaultTimeoutAction = VaultTimeoutAction.Lock;
} }
await SetThemeAsync(currentTheme, account.Profile.UserId); await SetThemeAsync(currentTheme, account.Profile.UserId);
await SetAutoDarkThemeAsync(currentAutoDarkTheme, account.Profile.UserId);
await SetDisableFaviconAsync(currentDisableFavicons, account.Profile.UserId); await SetDisableFaviconAsync(currentDisableFavicons, account.Profile.UserId);
state.Accounts[account.Profile.UserId] = account; state.Accounts[account.Profile.UserId] = account;
@@ -1558,16 +1510,6 @@ namespace Bit.Core.Services
await _storageService.SaveAsync(Constants.StateKey, state); await _storageService.SaveAsync(Constants.StateKey, state);
} }
private async Task<string> GetExtensionActiveUserIdFromStorageAsync()
{
return await _storageService.GetAsync<string>(Constants.iOSExtensionActiveUserIdKey);
}
public async Task SaveExtensionActiveUserIdToStorageAsync(string userId)
{
await _storageService.SaveAsync(Constants.iOSExtensionActiveUserIdKey, userId);
}
private async Task CheckStateAsync() private async Task CheckStateAsync()
{ {
if (!_migrationChecked) if (!_migrationChecked)

View File

@@ -1,13 +0,0 @@
namespace Bit.Core.Utilities
{
public static class AccountsManagerMessageCommands
{
public const string LOCKED = "locked";
public const string LOCK_VAULT = "lockVault";
public const string LOGOUT = "logout";
public const string LOGGED_OUT = "loggedOut";
public const string ADD_ACCOUNT = "addAccount";
public const string ACCOUNT_ADDED = "accountAdded";
public const string SWITCHED_ACCOUNT = "switchedAccount";
}
}

View File

@@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Services; using Bit.Core.Services;
@@ -10,7 +8,7 @@ namespace Bit.Core.Utilities
{ {
public static class ServiceContainer public static class ServiceContainer
{ {
public static ConcurrentDictionary<string, object> RegisteredServices { get; set; } = new ConcurrentDictionary<string, object>(); public static Dictionary<string, object> RegisteredServices { get; set; } = new Dictionary<string, object>();
public static bool Inited { get; set; } public static bool Inited { get; set; }
public static void Init(string customUserAgent = null, string clearCipherCacheKey = null, public static void Init(string customUserAgent = null, string clearCipherCacheKey = null,
@@ -111,17 +109,18 @@ namespace Bit.Core.Utilities
public static void Register<T>(string serviceName, T obj) public static void Register<T>(string serviceName, T obj)
{ {
if (!RegisteredServices.TryAdd(serviceName, obj)) if (RegisteredServices.ContainsKey(serviceName))
{ {
throw new Exception($"Service {serviceName} has already been registered."); throw new Exception($"Service {serviceName} has already been registered.");
} }
RegisteredServices.Add(serviceName, obj);
} }
public static T Resolve<T>(string serviceName, bool dontThrow = false) public static T Resolve<T>(string serviceName, bool dontThrow = false)
{ {
if (RegisteredServices.TryGetValue(serviceName, out var service)) if (RegisteredServices.ContainsKey(serviceName))
{ {
return (T)service; return (T)RegisteredServices[serviceName];
} }
if (dontThrow) if (dontThrow)
{ {
@@ -130,59 +129,6 @@ namespace Bit.Core.Utilities
throw new Exception($"Service {serviceName} is not registered."); throw new Exception($"Service {serviceName} is not registered.");
} }
public static void Register<T>(T obj)
where T : class
{
Register(typeof(T), obj);
}
public static void Register(Type type, object obj)
{
var serviceName = GetServiceRegistrationName(type);
if (!RegisteredServices.TryAdd(serviceName, obj))
{
throw new Exception($"Service {serviceName} has already been registered.");
}
}
public static T Resolve<T>()
where T : class
{
return (T)Resolve(typeof(T));
}
public static object Resolve(Type type)
{
var serviceName = GetServiceRegistrationName(type);
if (RegisteredServices.TryGetValue(serviceName, out var service))
{
return service;
}
throw new Exception($"Service {serviceName} is not registered.");
}
public static bool TryResolve<T>(out T service)
where T : class
{
try
{
var toReturn = TryResolve(typeof(T), out var serviceObj);
service = (T)serviceObj;
return toReturn;
}
catch (Exception)
{
service = null;
return false;
}
}
public static bool TryResolve(Type type, out object service)
{
var serviceName = GetServiceRegistrationName(type);
return RegisteredServices.TryGetValue(serviceName, out service);
}
public static void Reset() public static void Reset()
{ {
foreach (var service in RegisteredServices) foreach (var service in RegisteredServices)
@@ -194,33 +140,7 @@ namespace Bit.Core.Utilities
} }
Inited = false; Inited = false;
RegisteredServices.Clear(); RegisteredServices.Clear();
RegisteredServices = new ConcurrentDictionary<string, object>(); RegisteredServices = new Dictionary<string, object>();
}
/// <summary>
/// Gets the service registration name
/// </summary>
/// <param name="type">Type of the service</param>
/// <remarks>
/// In order to work with already register/resolve we need to maintain the naming convention
/// of camelCase without the first "I" on the services interfaces
/// e.g. "ITokenService" -> "tokenService"
/// </remarks>
static string GetServiceRegistrationName(Type type)
{
var typeName = type.Name;
var sb = new StringBuilder();
var indexToLowerCase = 0;
if (typeName[0] == 'I' && char.IsUpper(typeName[1]))
{
// if it's an interface then we ignore the first char
// and lower case the 2nd one (index 1)
indexToLowerCase = 1;
}
sb.Append(char.ToLower(typeName[indexToLowerCase]));
sb.Append(typeName.Substring(++indexToLowerCase));
return sb.ToString();
} }
} }
} }

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.autofill</string> <string>com.8bit.bitwarden.autofill</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2022.6.1</string> <string>2022.05.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>CFBundleLocalizations</key> <key>CFBundleLocalizations</key>

View File

@@ -21,7 +21,7 @@ namespace Bit.iOS.Core.Renderers
public CustomTabbedRenderer() public CustomTabbedRenderer()
{ {
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_broadcasterService.Subscribe(nameof(CustomTabbedRenderer), (message) => _broadcasterService.Subscribe(nameof(CustomTabbedRenderer), async (message) =>
{ {
if (message.Command == "updatedTheme") if (message.Command == "updatedTheme")
{ {

View File

@@ -32,22 +32,11 @@ namespace Bit.iOS.Core.Utilities
{ {
var overlay = new AccountSwitchingOverlayView() var overlay = new AccountSwitchingOverlayView()
{ {
LongPressAccountEnabled = false, LongPressAccountEnabled = false
AfterHide = () =>
{
if (containerView != null)
{
containerView.Hidden = true;
}
}
}; };
var vm = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger) var vm = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger);
{
FromIOSExtension = true
};
overlay.BindingContext = vm; overlay.BindingContext = vm;
overlay.IsVisible = false;
var renderer = Platform.CreateRenderer(overlay.Content); var renderer = Platform.CreateRenderer(overlay.Content);
renderer.SetElementSize(new Size(containerView.Frame.Size.Width, containerView.Frame.Size.Height)); renderer.SetElementSize(new Size(containerView.Frame.Size.Width, containerView.Frame.Size.Height));
@@ -71,13 +60,8 @@ namespace Bit.iOS.Core.Utilities
public void OnToolbarItemActivated(AccountSwitchingOverlayView accountSwitchingOverlayView, UIView containerView) public void OnToolbarItemActivated(AccountSwitchingOverlayView accountSwitchingOverlayView, UIView containerView)
{ {
var overlayVisible = accountSwitchingOverlayView.IsVisible; var overlayVisible = accountSwitchingOverlayView.IsVisible;
if (!overlayVisible)
{
// So that the animation doesn't break we only care about showing it
// and the hiding if done through AccountSwitchingOverlayView -> AfterHide
containerView.Hidden = false;
}
accountSwitchingOverlayView.ToggleVisibililtyCommand.Execute(null); accountSwitchingOverlayView.ToggleVisibililtyCommand.Execute(null);
containerView.Hidden = false;
containerView.UserInteractionEnabled = !overlayVisible; containerView.UserInteractionEnabled = !overlayVisible;
containerView.Subviews[0].UserInteractionEnabled = !overlayVisible; containerView.Subviews[0].UserInteractionEnabled = !overlayVisible;
} }

View File

@@ -83,10 +83,10 @@ namespace Bit.iOS.Core.Utilities
{ {
if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled) if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled)
{ {
theme = ThemeManager.Dark; theme = "dark";
} }
if (theme == ThemeManager.Dark || theme == ThemeManager.Black || theme == ThemeManager.Nord) if (theme == "dark" || theme == "black" || theme == "nord")
{ {
LightTheme = false; LightTheme = false;
} }

View File

@@ -38,15 +38,13 @@ namespace Bit.iOS.Core.Utilities
ServiceContainer.Register<INativeLogService>("nativeLogService", new ConsoleLogService()); ServiceContainer.Register<INativeLogService>("nativeLogService", new ConsoleLogService());
} }
ILogger logger = null;
if (ServiceContainer.Resolve<ILogger>("logger", true) == null) if (ServiceContainer.Resolve<ILogger>("logger", true) == null)
{ {
#if DEBUG #if DEBUG
logger = DebugLogger.Instance; ServiceContainer.Register<ILogger>("logger", DebugLogger.Instance);
#else #else
logger = Logger.Instance; ServiceContainer.Register<ILogger>("logger", Logger.Instance);
#endif #endif
ServiceContainer.Register("logger", logger);
} }
var preferencesStorage = new PreferencesStorageService(AppGroupId); var preferencesStorage = new PreferencesStorageService(AppGroupId);
@@ -54,14 +52,14 @@ namespace Bit.iOS.Core.Utilities
var liteDbStorage = new LiteDbStorageService( var liteDbStorage = new LiteDbStorageService(
Path.Combine(appGroupContainer.Path, "Library", "bitwarden.db")); Path.Combine(appGroupContainer.Path, "Library", "bitwarden.db"));
var localizeService = new LocalizeService(); var localizeService = new LocalizeService();
var broadcasterService = new BroadcasterService(logger); var broadcasterService = new BroadcasterService();
var messagingService = new MobileBroadcasterMessagingService(broadcasterService); var messagingService = new MobileBroadcasterMessagingService(broadcasterService);
var i18nService = new MobileI18nService(localizeService.GetCurrentCultureInfo()); var i18nService = new MobileI18nService(localizeService.GetCurrentCultureInfo());
var secureStorageService = new KeyChainStorageService(AppId, AccessGroup, var secureStorageService = new KeyChainStorageService(AppId, AccessGroup,
() => ServiceContainer.Resolve<IAppIdService>("appIdService").GetAppIdAsync()); () => ServiceContainer.Resolve<IAppIdService>("appIdService").GetAppIdAsync());
var cryptoPrimitiveService = new CryptoPrimitiveService(); var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage); var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var stateService = new StateService(mobileStorageService, secureStorageService, messagingService); var stateService = new StateService(mobileStorageService, secureStorageService);
var stateMigrationService = var stateMigrationService =
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService); new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
var deviceActionService = new DeviceActionService(stateService, messagingService); var deviceActionService = new DeviceActionService(stateService, messagingService);

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.find-login-action-extension</string> <string>com.8bit.bitwarden.find-login-action-extension</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2022.6.1</string> <string>2022.05.1</string>
<key>CFBundleLocalizations</key> <key>CFBundleLocalizations</key>
<array> <array>
<string>en</string> <string>en</string>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XPC!</string> <string>XPC!</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2022.6.1</string> <string>2022.05.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>

View File

@@ -59,121 +59,114 @@ namespace Bit.iOS
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) => _broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
{ {
try if (message.Command == "startEventTimer")
{ {
if (message.Command == "startEventTimer") StartEventTimer();
}
else if (message.Command == "stopEventTimer")
{
var task = StopEventTimerAsync();
}
else if (message.Command == "updatedTheme")
{
Device.BeginInvokeOnMainThread(() =>
{ {
StartEventTimer(); iOSCoreHelpers.AppearanceAdjustments();
} });
else if (message.Command == "stopEventTimer") }
{ else if (message.Command == "listenYubiKeyOTP")
var task = StopEventTimerAsync(); {
} iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
else if (message.Command == "updatedTheme") }
{ else if (message.Command == "unlocked")
Device.BeginInvokeOnMainThread(() => {
{ var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
iOSCoreHelpers.AppearanceAdjustments(); Core.Constants.AutofillNeedsIdentityReplacementKey);
}); if (needsAutofillReplacement.GetValueOrDefault())
}
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.ReplaceAllIdentities();
}
}
else if (message.Command == "showAppExtension")
{
Device.BeginInvokeOnMainThread(() => ShowAppExtension((ExtensionPageViewModel)message.Data));
}
else if (message.Command == "syncCompleted")
{
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
{
var success = data["successfully"] as bool?;
if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
{
await ASHelpers.ReplaceAllIdentities();
}
}
}
else if (message.Command == "addedCipher" || message.Command == "editedCipher" ||
message.Command == "restoredCipher")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
if (await ASHelpers.IdentitiesCanIncremental())
{
var cipherId = message.Data as string;
if (message.Command == "addedCipher" && !string.IsNullOrWhiteSpace(cipherId))
{
var identity = await ASHelpers.GetCipherIdentityAsync(cipherId);
if (identity == null)
{
return;
}
await ASCredentialIdentityStore.SharedStore?.SaveCredentialIdentitiesAsync(
new ASPasswordCredentialIdentity[] { identity });
return;
}
}
await ASHelpers.ReplaceAllIdentities();
}
}
else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
if (await ASHelpers.IdentitiesCanIncremental())
{
var identity = ASHelpers.ToCredentialIdentity(
message.Data as Bit.Core.Models.View.CipherView);
if (identity == null)
{
return;
}
await ASCredentialIdentityStore.SharedStore?.RemoveCredentialIdentitiesAsync(
new ASPasswordCredentialIdentity[] { identity });
return;
}
await ASHelpers.ReplaceAllIdentities();
}
}
else if (message.Command == "logout")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
}
}
else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher")
&& _deviceActionService.SystemMajorVersion() >= 12)
{ {
await ASHelpers.ReplaceAllIdentities(); await ASHelpers.ReplaceAllIdentities();
} }
else if (message.Command == "vaultTimeoutActionChanged") }
else if (message.Command == "showAppExtension")
{
Device.BeginInvokeOnMainThread(() => ShowAppExtension((ExtensionPageViewModel)message.Data));
}
else if (message.Command == "syncCompleted")
{
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
{ {
var timeoutAction = await _stateService.GetVaultTimeoutActionAsync(); var success = data["successfully"] as bool?;
if (timeoutAction == VaultTimeoutAction.Logout) if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
{
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
}
else
{ {
await ASHelpers.ReplaceAllIdentities(); await ASHelpers.ReplaceAllIdentities();
} }
} }
} }
catch (Exception ex) else if (message.Command == "addedCipher" || message.Command == "editedCipher" ||
message.Command == "restoredCipher")
{ {
LoggerHelper.LogEvenIfCantBeResolved(ex); if (_deviceActionService.SystemMajorVersion() >= 12)
{
if (await ASHelpers.IdentitiesCanIncremental())
{
var cipherId = message.Data as string;
if (message.Command == "addedCipher" && !string.IsNullOrWhiteSpace(cipherId))
{
var identity = await ASHelpers.GetCipherIdentityAsync(cipherId);
if (identity == null)
{
return;
}
await ASCredentialIdentityStore.SharedStore?.SaveCredentialIdentitiesAsync(
new ASPasswordCredentialIdentity[] { identity });
return;
}
}
await ASHelpers.ReplaceAllIdentities();
}
}
else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
if (await ASHelpers.IdentitiesCanIncremental())
{
var identity = ASHelpers.ToCredentialIdentity(
message.Data as Bit.Core.Models.View.CipherView);
if (identity == null)
{
return;
}
await ASCredentialIdentityStore.SharedStore?.RemoveCredentialIdentitiesAsync(
new ASPasswordCredentialIdentity[] { identity });
return;
}
await ASHelpers.ReplaceAllIdentities();
}
}
else if (message.Command == "logout")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
}
}
else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher")
&& _deviceActionService.SystemMajorVersion() >= 12)
{
await ASHelpers.ReplaceAllIdentities();
}
else if (message.Command == "vaultTimeoutActionChanged")
{
var timeoutAction = await _stateService.GetVaultTimeoutActionAsync();
if (timeoutAction == VaultTimeoutAction.Logout)
{
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
}
else
{
await ASHelpers.ReplaceAllIdentities();
}
} }
}); });

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden</string> <string>com.8bit.bitwarden</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2022.6.1</string> <string>2022.05.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>CFBundleIconName</key> <key>CFBundleIconName</key>