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

Compare commits

..

14 Commits

Author SHA1 Message Date
github-actions[bot]
efe128b932 Bumped version to 2022.6.2 (#1981)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit 7802da2b9c)
2022-07-21 11:16:57 -04:00
Federico Maccaroni
df2e52f82c EC-308 Fix crash produced by creating avatar image on AccountSwitchingOverlayHelper and also added more logging to see when it happens. (#1983)
(cherry picked from commit 846d3a85a2)
2022-07-20 17:51:00 -04:00
github-actions[bot]
2d224f5e22 Bumped version to 2022.6.1 (#1969)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit a6ddc2496f)
2022-07-06 18:56:39 -04:00
Matt Gibson
4831097c0b Add user verification to reset password request (#1980)
We only need master password hash because this is currently
only used for sso password setting after auto-provisioning. Key
Connector is not involved in these accounts

(cherry picked from commit 58a3662d0f)
2022-07-06 18:24:33 -04:00
github-actions[bot]
72f2983885 Bumped version to 2022.6.0 (#1968)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit d9a818279f)
2022-06-29 09:12:27 -07:00
Matt Gibson
ee68dcf3b0 Add ssoToken to limit lifetime of SSO redirect (#1965)
(cherry picked from commit 6e2e613fee)
2022-06-27 16:05:11 -04:00
Federico Andrés Maccaroni
daab473cbb SG-396 Fix tappable area after hiding account switching 2022-06-21 09:33:59 -03:00
Joseph Flinn
e6b99270cf Pin NuGet version (#1957)
* Pinning the version of NuGet to 5.x

* pinning NuGet verison to 5.9.x

* pinning NuGet to 5.9.0.7134

* pinning NuGet to 5.9.0

(cherry picked from commit c892e9fa57)
2022-06-16 14:48:47 -07:00
Federico Andrés Maccaroni
a8d9aaa7fe [SG-386] iOS Update user state when coming from background 2022-06-16 14:01:28 -03:00
mp-bw
542ef5f31a fix for missing personal items added prior to joining org with personal ownership policy (#1955) 2022-06-16 09:56:03 -04:00
mp-bw
0b2fc2a647 separate init and showVaultFilter property set (#1954) 2022-06-15 15:19:34 -04:00
mp-bw
a95fdf67a1 [SG-390] Fix for missing org items with single org & personal ownership enabled (#1953)
* fix for missing org items with single org & personal ownership enabled

* fix for ui issue with vault filter state change on pull to refresh
2022-06-15 10:45:01 -04:00
mp-bw
73890162bf Additional logic around filter display (#1951) 2022-06-14 13:05:28 -04:00
Joseph Flinn
ef14a8f850 Updating the release version check to use the new action (#1934)
* Updating the release version check to use the new action

* Update .github/workflows/release.yml

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>

Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
(cherry picked from commit 5579817f9f)
2022-06-13 12:59:24 -07:00
41 changed files with 6251 additions and 4555 deletions

View File

@@ -1,64 +0,0 @@
---
name: Automatic responses
on:
issues:
types:
- labeled
jobs:
close-issue:
name: 'Close issue with automatic response'
runs-on: ubuntu-20.04
permissions:
issues: write
steps:
# Feature request
- if: github.event.label.name == 'feature-request'
name: Feature request
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
comment: |
We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
Please [sign up on our forums](https://community.bitwarden.com/signup) and search to see if this request already exists. If so, you can vote for it and contribute to any discussions about it. If not, you can re-create the request there so that it can be properly tracked.
This issue will now be closed. Thanks!
# Intended behavior
- if: github.event.label.name == 'intended-behavior'
name: Intended behaviour
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
comment: |
Your issue appears to be describing the intended behavior of the software. If you want this to be changed, it would be a feature request.
We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
Please [sign up on our forums](https://community.bitwarden.com/signup) and search to see if this request already exists. If so, you can vote for it and contribute to any discussions about it. If not, you can re-create the request there so that it can be properly tracked.
This issue will now be closed. Thanks!
# Customer support request
- if: github.event.label.name == 'customer-support'
name: Customer Support request
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
comment: |
We use GitHub issues as a place to track bugs and other development related issues. Your issue appears to be a support request, or would otherwise be better handled by our dedicated Customer Success team.
Please contact us using our [Contact page](https://bitwarden.com/contact). You can include a link to this issue in the message content.
Alternatively, you can also search for an answer in our [help documentation](https://bitwarden.com/help/) or get help from other Bitwarden users on our [community forums](https://community.bitwarden.com/c/support/). The issue here will be closed.
# Resolved
- if: github.event.label.name == 'resolved'
name: Resolved
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
comment: |
Weve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
# Stale
- if: github.event.label.name == 'stale'
name: Stale
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
comment: |
As we havent heard from you about this problem in some time, this issue will now be closed.
If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.

View File

@@ -1,30 +0,0 @@
---
name: 'Close stale issues and PRs'
on:
workflow_dispatch:
schedule: # Run once a day at 5.23am (arbitrary but should avoid peak loads on the hour)
- cron: '23 5 * * *'
jobs:
stale:
name: 'Check for stale issues and PRs'
runs-on: ubuntu-20.04
steps:
- name: 'Run stale action'
uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0
with:
stale-issue-label: 'needs-reply'
stale-pr-label: 'needs-changes'
days-before-stale: -1 # Do not apply the stale labels automatically, this is a manual process
days-before-issue-close: 14 # Close issue if no further activity after X days
days-before-pr-close: 21 # Close PR if no further activity after X days
close-issue-message: |
We need more information before we can help you with your problem. As we havent heard from you recently, this issue will be closed.
If this happens again or continues to be an problem, please respond to this issue with the information weve requested and anything else relevant.
close-pr-message: |
We cant merge your pull request until you make the changes weve requested. As we havent heard from you recently, this pull request will be closed.
If youre still working on this, please respond here after youve made the changes weve requested and our team will re-open it for further review.
Please make sure to resolve any conflicts with the master branch before requesting another review.

View File

@@ -1,3 +1,40 @@
# How to Contribute
Our [Contributing Guidelines](https://contributing.bitwarden.com/contributing/) are located in our [Contributing Documentation](https://contributing.bitwarden.com/). The documentation also includes recommended tooling, code style tips, and lots of other great information to get you started.
Contributions of all kinds are welcome!
Please visit our [Community Forums](https://community.bitwarden.com/) for general community discussion and the development roadmap.
Here is how you can get involved:
* **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
* **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
* **Report a bug or submit a bugfix:** Use Github issues and pull requests
* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
* **Help other users:** Go to the [Ask the Bitwarden Community category](https://community.bitwarden.com/c/support/) on the Community Forums
* **Translate:** See the localization (i10n) section below
## Contributor Agreement
Please sign the [Contributor Agreement](https://cla-assistant.io/bitwarden/mobile) if you intend on contributing to any Github repository. Pull requests cannot be accepted and merged unless the author has signed the Contributor Agreement.
## Pull Request Guidelines
* commit any pull requests against the `master` branch
* include a link to your Community Forums post
# Localization (l10n)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-mobile/localized.svg)](https://crowdin.com/project/bitwarden-mobile)
We use a translation tool called [Crowdin](https://crowdin.com) to help manage our localization efforts across many different languages.
If you are interested in helping translate the Bitwarden mobile app into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-mobile
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/dwbit).
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/

View File

@@ -12,7 +12,16 @@ The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin
# 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.
**Requirements**
- [Visual Studio](https://visualstudio.microsoft.com/)
- [Xamarin](https://docs.microsoft.com/en-us/xamarin/get-started/installation/?pivots=windows)
**Run the app**
- Open the solution file in Visual Studio.
- Restore the nuget packages.
- Build and run the app.
# We're Hiring!
@@ -20,7 +29,8 @@ Interested in contributing in a big way? Consider joining our team! We're hiring
# Contribute
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
Code contributions are welcome! Visual Studio with Xamarin is required to work on this project. Please commit any pull requests against the `master` branch.
Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
@@ -35,3 +45,11 @@ We recently migrated to using dotnet-format as code formatter. All previous bran
5. Commit
6. Run `git merge -Xours 04539af2a66668b6e85476d5cf318c9150ec4357`
7. Push
#### Git blame
We also recommend that you configure git to ignore the prettier revision using:
```bash
git config blame.ignoreRevsFile .git-blame-ignore-revs
```

View File

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

View File

@@ -1,5 +1,5 @@
<?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.6.2" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<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)
{
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+
if ((int)Build.VERSION.SdkInt < 33)
{
await Clipboard.SetTextAsync(text);
}
else
{
CopyToClipboard(text, isSensitive);
}
await Clipboard.SetTextAsync(text);
}
else
{
CopyToClipboard(text, isSensitive);
}
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.
}
await ClearClipboardAlarmAsync(expiresInMs);
}
public bool IsCopyNotificationHandledByPlatform()

View File

@@ -59,10 +59,10 @@ namespace Bit.Droid.Utilities
{
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;
}

View File

@@ -10,7 +10,6 @@ using Bit.App.Utilities.AccountManagement;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@@ -57,93 +56,86 @@ namespace Bit.App
Bootstrap();
_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;
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
Device.BeginInvokeOnMainThread(async () =>
if (!string.IsNullOrWhiteSpace(details.CancelText))
{
if (!string.IsNullOrWhiteSpace(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();
confirmed = await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText,
details.CancelText);
}
}
else if (message.Command == "slept")
{
if (Device.RuntimePlatform == Device.iOS)
else
{
await SleptAsync();
await Current.MainPage.DisplayAlert(details.Title, details.Text, confirmText);
}
}
else if (message.Command == "migrated")
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
}
else if (message.Command == "resumed")
{
if (Device.RuntimePlatform == Device.iOS)
{
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()));
});
ResumedAsync().FireAndForget();
}
}
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()));
});
}
});
}

View File

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

View File

@@ -25,6 +25,8 @@ namespace Bit.App.Pages
_vm = BindingContext as LockPageViewModel;
_vm.Page = this;
_vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync());
MasterPasswordEntry = _masterPassword;
PinEntry = _pin;
if (Device.RuntimePlatform == Device.iOS)
{
@@ -36,17 +38,8 @@ namespace Bit.App.Pages
}
}
public Entry SecretEntry
{
get
{
if (_vm?.PinLock ?? false)
{
return _pin;
}
return _masterPassword;
}
}
public Entry MasterPasswordEntry { get; set; }
public Entry PinEntry { get; set; }
public async Task PromptBiometricAfterResumeAsync()
{
@@ -77,12 +70,16 @@ namespace Bit.App.Pages
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
await _vm.InitAsync();
_vm.FocusSecretEntry += PerformFocusSecretEntry;
if (!_vm.BiometricLock)
{
RequestFocus(SecretEntry);
if (_vm.PinLock)
{
RequestFocus(PinEntry);
}
else
{
RequestFocus(MasterPasswordEntry);
}
}
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()
{
if (_accountListOverlay.IsVisible)

View File

@@ -10,7 +10,6 @@ using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.Helpers;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -28,7 +27,6 @@ namespace Bit.App.Pages
private readonly IBiometricService _biometricService;
private readonly IKeyConnectorService _keyConnectorService;
private readonly ILogger _logger;
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
private string _email;
private bool _showPassword;
@@ -135,11 +133,6 @@ namespace Bit.App.Pages
public string MasterPassword { get; set; }
public string Pin { get; set; }
public Action UnlockedAction { get; set; }
public event Action<int?> FocusSecretEntry
{
add => _secretEntryFocusWeakEventManager.AddEventHandler(value);
remove => _secretEntryFocusWeakEventManager.RemoveEventHandler(value);
}
public async Task InitAsync()
{
@@ -353,8 +346,11 @@ namespace Bit.App.Pages
public void TogglePassword()
{
ShowPassword = !ShowPassword;
var secret = PinLock ? Pin : MasterPassword;
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
var page = (Page as LockPage);
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()
@@ -365,8 +361,18 @@ namespace Bit.App.Pages
return;
}
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
PinLock ? AppResources.PIN : AppResources.MasterPassword,
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
PinLock ? AppResources.PIN : AppResources.MasterPassword, () =>
{
var page = Page as LockPage;
if (PinLock)
{
page.PinEntry.Focus();
}
else
{
page.MasterPasswordEntry.Focus();
}
});
await _stateService.SetBiometricLockedAsync(!success);
if (success)
{

View File

@@ -219,7 +219,8 @@ namespace Bit.App.Pages
// Request
var resetRequest = new OrganizationUserResetPasswordEnrollmentRequest
{
ResetPasswordKey = encryptedKey.EncryptedString
ResetPasswordKey = encryptedKey.EncryptedString,
MasterPasswordHash = masterPasswordHash,
};
var userId = await _stateService.GetActiveUserIdAsync();
// Enroll user

View File

@@ -5,7 +5,6 @@ using Bit.App.Controls;
using Bit.App.Models;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.Forms;
@@ -69,28 +68,21 @@ namespace Bit.App.Pages
_broadcasterService.Subscribe(_pageName, async (message) =>
{
try
if (message.Command == "syncStarted")
{
if (message.Command == "syncStarted")
{
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();
}
});
}
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
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

@@ -33,23 +33,6 @@
StyleClass="box-footer-label"
Text="{u:I18n ThemeDescription}" />
</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-row, box-row-input, box-row-input-options-platform">
<Label

View File

@@ -19,7 +19,6 @@ namespace Bit.App.Pages
_vm = BindingContext as OptionsPageViewModel;
_vm.Page = this;
_themePicker.ItemDisplayBinding = new Binding("Value");
_autoDarkThemePicker.ItemDisplayBinding = new Binding("Value");
_uriMatchPicker.ItemDisplayBinding = new Binding("Value");
_clearClipboardPicker.ItemDisplayBinding = new Binding("Value");
if (Device.RuntimePlatform == Device.Android)
@@ -30,7 +29,6 @@ namespace Bit.App.Pages
else
{
_themePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_autoDarkThemePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_uriMatchPicker.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 int _clearClipboardSelectedIndex;
private int _themeSelectedIndex;
private int _autoDarkThemeSelectedIndex;
private int _uriMatchSelectedIndex;
private bool _inited;
private bool _updatingAutofill;
@@ -54,16 +53,10 @@ namespace Bit.App.Pages
ThemeOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(null, AppResources.ThemeDefault),
new KeyValuePair<string, string>(ThemeManager.Light, AppResources.Light),
new KeyValuePair<string, string>(ThemeManager.Dark, AppResources.Dark),
new KeyValuePair<string, string>(ThemeManager.Black, AppResources.Black),
new KeyValuePair<string, string>(ThemeManager.Nord, AppResources.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),
new KeyValuePair<string, string>("light", AppResources.Light),
new KeyValuePair<string, string>("dark", AppResources.Dark),
new KeyValuePair<string, string>("black", AppResources.Black),
new KeyValuePair<string, string>("nord", "Nord"),
};
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<string, string>> ThemeOptions { get; set; }
public List<KeyValuePair<string, string>> AutoDarkThemeOptions { get; set; }
public List<KeyValuePair<UriMatchType?, string>> UriMatchOptions { get; set; }
public int ClearClipboardSelectedIndex
@@ -88,7 +80,7 @@ namespace Bit.App.Pages
{
if (SetProperty(ref _clearClipboardSelectedIndex, value))
{
SaveClipboardChangedAsync().FireAndForget();
var task = SaveClipboardChangedAsync();
}
}
}
@@ -98,25 +90,9 @@ namespace Bit.App.Pages
get => _themeSelectedIndex;
set
{
if (SetProperty(ref _themeSelectedIndex, value,
additionalPropertyNames: new[] { nameof(ShowAutoDarkThemeOptions) })
)
if (SetProperty(ref _themeSelectedIndex, value))
{
SaveThemeAsync().FireAndForget();
}
}
}
public bool ShowAutoDarkThemeOptions => ThemeOptions[ThemeSelectedIndex].Key == null;
public int AutoDarkThemeSelectedIndex
{
get => _autoDarkThemeSelectedIndex;
set
{
if (SetProperty(ref _autoDarkThemeSelectedIndex, value))
{
SaveThemeAsync().FireAndForget();
var task = SaveThemeAsync();
}
}
}
@@ -128,7 +104,7 @@ namespace Bit.App.Pages
{
if (SetProperty(ref _uriMatchSelectedIndex, value))
{
SaveDefaultUriAsync().FireAndForget();
var task = SaveDefaultUriAsync();
}
}
}
@@ -140,7 +116,7 @@ namespace Bit.App.Pages
{
if (SetProperty(ref _disableFavicon, value))
{
UpdateDisableFaviconAsync().FireAndForget();
var task = UpdateDisableFaviconAsync();
}
}
}
@@ -152,7 +128,7 @@ namespace Bit.App.Pages
{
if (SetProperty(ref _disableAutoTotpCopy, value))
{
UpdateAutoTotpCopyAsync().FireAndForget();
var task = UpdateAutoTotpCopyAsync();
}
}
}
@@ -164,7 +140,7 @@ namespace Bit.App.Pages
{
if (SetProperty(ref _autofillDisableSavePrompt, value))
{
UpdateAutofillDisableSavePromptAsync().FireAndForget();
var task = UpdateAutofillDisableSavePromptAsync();
}
}
}
@@ -190,8 +166,6 @@ namespace Bit.App.Pages
DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
var theme = await _stateService.GetThemeAsync();
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();
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
@@ -228,8 +202,8 @@ namespace Bit.App.Pages
{
if (_inited && ThemeSelectedIndex > -1)
{
await _stateService.SetThemeAsync(ThemeOptions[ThemeSelectedIndex].Key);
await _stateService.SetAutoDarkThemeAsync(AutoDarkThemeOptions[AutoDarkThemeSelectedIndex].Key);
var theme = ThemeOptions[ThemeSelectedIndex].Key;
await _stateService.SetThemeAsync(theme);
ThemeManager.SetTheme(Application.Current.Resources);
_messagingService.Send("updatedTheme");
}

View File

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

@@ -7,7 +7,6 @@ using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.Forms;
@@ -96,28 +95,21 @@ namespace Bit.App.Pages
_broadcasterService.Subscribe(_pageName, async (message) =>
{
try
if (message.Command == "syncStarted")
{
if (message.Command == "syncStarted")
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
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,7 +3,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.Forms;
@@ -57,44 +56,37 @@ namespace Bit.App.Pages
_broadcasterService.Subscribe(nameof(ViewPage), async (message) =>
{
try
if (message.Command == "syncStarted")
{
if (message.Command == "syncStarted")
{
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);
});
}
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
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 () =>

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities;
@@ -12,7 +11,6 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -30,7 +28,6 @@ namespace Bit.App.Pages
private readonly IPasswordRepromptService _passwordRepromptService;
private readonly ILocalizeService _localizeService;
private readonly IClipboardService _clipboardService;
private readonly ILogger _logger;
private CipherView _cipher;
private List<ViewPageFieldViewModel> _fields;
@@ -61,11 +58,10 @@ namespace Bit.App.Pages
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
_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);
CopyUriCommand = new AsyncCommand<LoginUriView>(uriView => CopyAsync("LoginUri", uriView.Uri), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
CopyFieldCommand = new AsyncCommand<FieldView>(field => CopyAsync(field.Type == FieldType.Hidden ? "H_FieldValue" : "FieldValue", field.Value), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
CopyUriCommand = new Command<LoginUriView>(CopyUri);
CopyFieldCommand = new Command<FieldView>(CopyField);
LaunchUriCommand = new Command<LoginUriView>(LaunchUri);
TogglePasswordCommand = new Command(TogglePassword);
ToggleCardNumberCommand = new Command(ToggleCardNumber);
@@ -76,9 +72,9 @@ namespace Bit.App.Pages
PageTitle = AppResources.ViewItem;
}
public ICommand CopyCommand { get; set; }
public ICommand CopyUriCommand { get; set; }
public ICommand CopyFieldCommand { get; set; }
public Command CopyCommand { get; set; }
public Command CopyUriCommand { get; set; }
public Command CopyFieldCommand { get; set; }
public Command LaunchUriCommand { get; set; }
public Command TogglePasswordCommand { get; set; }
public Command ToggleCardNumberCommand { get; set; }
@@ -620,7 +616,7 @@ namespace Bit.App.Pages
_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())
{
@@ -684,6 +680,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)
{
if (uri.CanLaunch && (Page as BaseContentPage).DoOnce())

File diff suppressed because it is too large Load Diff

View File

@@ -1541,12 +1541,6 @@
<data name="ThemeDefault" xml:space="preserve">
<value>Default (System)</value>
</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">
<value>Copy Note</value>
</data>
@@ -1563,10 +1557,6 @@
<value>Black</value>
<comment>The color black</comment>
</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">
<value>Blacklisted URIs</value>
</data>

View File

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

View File

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

View File

@@ -110,8 +110,6 @@ namespace Bit.Core.Abstractions
Task SetRememberedOrgIdentifierAsync(string value);
Task<string> GetThemeAsync(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 SetAddSitePromptShownAsync(bool? value, string userId = null);
Task<bool?> GetPushInitialPromptShownAsync();

View File

@@ -73,7 +73,6 @@
public static string DisableFaviconKey(string userId) => $"disableFavicon_{userId}";
public static string DefaultUriMatchKey(string userId) => $"defaultUriMatch_{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 PreviousPageKey(string userId) => $"previousPage_{userId}";
public static string PasswordRepromptAutofillKey(string userId) => $"passwordRepromptAutofillKey_{userId}";

View File

@@ -2,6 +2,7 @@
{
public class OrganizationUserResetPasswordEnrollmentRequest
{
public string MasterPasswordHash { get; set; }
public string ResetPasswordKey { get; set; }
}
}

View File

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

View File

@@ -924,25 +924,6 @@ namespace Bit.Core.Services
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)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1433,7 +1414,6 @@ namespace Bit.Core.Services
await SetPasswordVerifiedAutofillAsync(null, userId);
await SetSyncOnRefreshAsync(null, userId);
await SetThemeAsync(null, userId);
await SetAutoDarkThemeAsync(null, userId);
await SetAddSitePromptShownAsync(null, userId);
await SetPasswordGenerationOptionsAsync(null, userId);
}
@@ -1443,7 +1423,6 @@ namespace Bit.Core.Services
{
await CheckStateAsync();
var currentTheme = await GetThemeAsync();
var currentAutoDarkTheme = await GetAutoDarkThemeAsync();
var currentDisableFavicons = await GetDisableFaviconAsync();
account.Settings.EnvironmentUrls = await GetPreAuthEnvironmentUrlsAsync();
@@ -1473,7 +1452,6 @@ namespace Bit.Core.Services
account.Settings.VaultTimeoutAction = VaultTimeoutAction.Lock;
}
await SetThemeAsync(currentTheme, account.Profile.UserId);
await SetAutoDarkThemeAsync(currentAutoDarkTheme, account.Profile.UserId);
await SetDisableFaviconAsync(currentDisableFavicons, account.Profile.UserId);
state.Accounts[account.Profile.UserId] = account;

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Services;
@@ -10,7 +8,7 @@ namespace Bit.Core.Utilities
{
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 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)
{
if (!RegisteredServices.TryAdd(serviceName, obj))
if (RegisteredServices.ContainsKey(serviceName))
{
throw new Exception($"Service {serviceName} has already been registered.");
}
RegisteredServices.Add(serviceName, obj);
}
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)
{
@@ -130,59 +129,6 @@ namespace Bit.Core.Utilities
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()
{
foreach (var service in RegisteredServices)
@@ -194,33 +140,7 @@ namespace Bit.Core.Utilities
}
Inited = false;
RegisteredServices.Clear();
RegisteredServices = new ConcurrentDictionary<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();
RegisteredServices = new Dictionary<string, object>();
}
}
}

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Bit.App.Controls;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
@@ -10,6 +11,8 @@ namespace Bit.iOS.Core.Utilities
{
public class AccountSwitchingOverlayHelper
{
const string DEFAULT_SYSTEM_AVATAR_IMAGE = "person.2";
IStateService _stateService;
IMessagingService _messagingService;
ILogger _logger;
@@ -23,9 +26,22 @@ namespace Bit.iOS.Core.Utilities
public async Task<UIImage> CreateAvatarImageAsync()
{
var avatarImageSource = new AvatarImageSource(await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
var avatarUIImage = await avatarImageSource.GetNativeImageAsync();
return avatarUIImage.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
try
{
if (_stateService is null)
{
throw new NullReferenceException(nameof(_stateService));
}
var avatarImageSource = new AvatarImageSource(await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
var avatarUIImage = await avatarImageSource.GetNativeImageAsync();
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
}
catch (Exception ex)
{
_logger.Exception(ex);
return UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
}
}
public AccountSwitchingOverlayView CreateAccountSwitchingOverlayView(UIView containerView)

View File

@@ -1,9 +1,9 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Bit.Core.Services;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.iOS;
namespace Bit.iOS.Core.Utilities
@@ -17,25 +17,29 @@ namespace Bit.iOS.Core.Utilities
public static async Task<UIImage> GetNativeImageAsync(this ImageSource source, CancellationToken cancellationToken = default(CancellationToken))
{
if (source == null || source.IsEmpty)
{
return null;
}
var handler = Xamarin.Forms.Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(source);
if (handler == null)
{
LoggerHelper.LogEvenIfCantBeResolved(new InvalidOperationException("GetNativeImageAsync failed cause IImageSourceHandler couldn't be found"));
return null;
}
try
{
float scale = (float)UIScreen.MainScreen.Scale;
return await handler.LoadImageAsync(source, scale: scale, cancelationToken: cancellationToken);
}
catch (OperationCanceledException)
{
Log.Warning("Image loading", "Image load cancelled");
LoggerHelper.LogEvenIfCantBeResolved(new OperationCanceledException("GetNativeImageAsync was cancelled"));
}
catch (Exception ex)
{
Log.Warning("Image loading", $"Image load failed: {ex}");
LoggerHelper.LogEvenIfCantBeResolved(new InvalidOperationException("GetNativeImageAsync failed", ex));
}
return null;

View File

@@ -83,10 +83,10 @@ namespace Bit.iOS.Core.Utilities
{
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;
}

View File

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

View File

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

View File

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

View File

@@ -59,121 +59,114 @@ namespace Bit.iOS
_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();
}
else if (message.Command == "stopEventTimer")
{
var task = StopEventTimerAsync();
}
else if (message.Command == "updatedTheme")
{
Device.BeginInvokeOnMainThread(() =>
{
iOSCoreHelpers.AppearanceAdjustments();
});
}
else if (message.Command == "listenYubiKeyOTP")
{
iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
}
else if (message.Command == "unlocked")
{
var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
Core.Constants.AutofillNeedsIdentityReplacementKey);
if (needsAutofillReplacement.GetValueOrDefault())
{
await ASHelpers.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)
iOSCoreHelpers.AppearanceAdjustments();
});
}
else if (message.Command == "listenYubiKeyOTP")
{
iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
}
else if (message.Command == "unlocked")
{
var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
Core.Constants.AutofillNeedsIdentityReplacementKey);
if (needsAutofillReplacement.GetValueOrDefault())
{
await ASHelpers.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();
if (timeoutAction == VaultTimeoutAction.Logout)
{
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
}
else
var success = data["successfully"] as bool?;
if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
{
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>
<string>com.8bit.bitwarden</string>
<key>CFBundleShortVersionString</key>
<string>2022.6.1</string>
<string>2022.6.2</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleIconName</key>