mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
1 Commits
fix-avatar
...
tech-debt/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
664d8d1f2c |
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -397,15 +397,6 @@ jobs:
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
|
||||
shell: bash
|
||||
|
||||
- name: Update Entitlements
|
||||
run: |
|
||||
echo "########################################"
|
||||
echo "##### Updating Entitlements"
|
||||
echo "########################################"
|
||||
|
||||
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./src/iOS/Entitlements.plist
|
||||
shell: bash
|
||||
|
||||
- name: Set up Keychain
|
||||
env:
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
||||
|
||||
12
.github/workflows/version-bump.yml
vendored
12
.github/workflows/version-bump.yml
vendored
@@ -27,25 +27,25 @@ jobs:
|
||||
ref: version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Bump Version - Android XML
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
uses: bitwarden/gh-actions/version-bump@0c263b3963211ccaf5804313c3b3a0bcc52d4b19
|
||||
with:
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/Android/Properties/AndroidManifest.xml"
|
||||
|
||||
- name: Bump Version - iOS.Autofill
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
uses: bitwarden/gh-actions/version-bump@0c263b3963211ccaf5804313c3b3a0bcc52d4b19
|
||||
with:
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS.Autofill/Info.plist"
|
||||
|
||||
- name: Bump Version - iOS.Extension
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
uses: bitwarden/gh-actions/version-bump@0c263b3963211ccaf5804313c3b3a0bcc52d4b19
|
||||
with:
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS.Extension/Info.plist"
|
||||
|
||||
- name: Bump Version - iOS
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
uses: bitwarden/gh-actions/version-bump@0c263b3963211ccaf5804313c3b3a0bcc52d4b19
|
||||
with:
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS/Info.plist"
|
||||
@@ -78,6 +78,6 @@ jobs:
|
||||
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||
- [ ] Build/deploy pipeline (DevOps)
|
||||
- [X] Other
|
||||
|
||||
|
||||
## Objective
|
||||
Automated version bump to ${{ github.event.inputs.version_number }}"
|
||||
Automated version bump to ${{ github.event.inputs.version_number }}"
|
||||
@@ -54,7 +54,6 @@ namespace Bit.Droid.Accessibility
|
||||
// Rem. for "com.google.android.captiveportallogin": URL displayed in ActionBar subtitle without viewId.
|
||||
new Browser("com.jamal2367.styx", "search"),
|
||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||
new Browser("com.kiwibrowser.browser.dev", "url_bar"),
|
||||
new Browser("com.microsoft.emmx", "url_bar"),
|
||||
new Browser("com.microsoft.emmx.beta", "url_bar"),
|
||||
new Browser("com.microsoft.emmx.canary", "url_bar"),
|
||||
|
||||
@@ -10,6 +10,7 @@ using Android.Views;
|
||||
using Android.Views.Accessibility;
|
||||
using Android.Widget;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -24,7 +25,7 @@ namespace Bit.Droid.Accessibility
|
||||
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
||||
private const string BitwardenWebsite = "vault.bitwarden.com";
|
||||
|
||||
private IStateService _stateService;
|
||||
private IStorageService _storageService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private DateTime? _lastSettingsReload = null;
|
||||
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
||||
@@ -443,9 +444,9 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private void LoadServices()
|
||||
{
|
||||
if (_stateService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
if (_broadcasterService == null)
|
||||
{
|
||||
@@ -459,12 +460,12 @@ namespace Bit.Droid.Accessibility
|
||||
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||
{
|
||||
_lastSettingsReload = now;
|
||||
var uris = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
if (uris != null)
|
||||
{
|
||||
_blacklistedUris = new HashSet<string>(uris);
|
||||
}
|
||||
var isAutoFillTileAdded = await _stateService.GetAutofillTileAddedAsync();
|
||||
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
|
||||
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,8 +171,7 @@
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
||||
<AndroidResource Include="Resources\drawable\card.xml" />
|
||||
<AndroidResource Include="Resources\drawable\cog_environment.xml" />
|
||||
<AndroidResource Include="Resources\drawable\cog_settings.xml" />
|
||||
<AndroidResource Include="Resources\drawable\cog.xml" />
|
||||
<AndroidResource Include="Resources\drawable\icon.xml" />
|
||||
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
||||
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
|
||||
|
||||
@@ -73,7 +73,6 @@ namespace Bit.Droid.Autofill
|
||||
"com.google.android.captiveportallogin",
|
||||
"com.jamal2367.styx",
|
||||
"com.kiwibrowser.browser",
|
||||
"com.kiwibrowser.browser.dev",
|
||||
"com.microsoft.emmx",
|
||||
"com.microsoft.emmx.beta",
|
||||
"com.microsoft.emmx.canary",
|
||||
|
||||
@@ -22,8 +22,9 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
private ICipherService _cipherService;
|
||||
private IVaultTimeoutService _vaultTimeoutService;
|
||||
private IStorageService _storageService;
|
||||
private IPolicyService _policyService;
|
||||
private IStateService _stateService;
|
||||
private IUserService _userService;
|
||||
|
||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||
FillCallback callback)
|
||||
@@ -37,18 +38,18 @@ namespace Bit.Droid.Autofill
|
||||
var parser = new Parser(structure, ApplicationContext);
|
||||
parser.Parse();
|
||||
|
||||
if (_stateService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
|
||||
var shouldAutofill = await parser.ShouldAutofillAsync(_stateService);
|
||||
var shouldAutofill = await parser.ShouldAutofillAsync(_storageService);
|
||||
if (!shouldAutofill)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inlineAutofillEnabled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
||||
|
||||
if (_vaultTimeoutService == null)
|
||||
{
|
||||
@@ -80,12 +81,12 @@ namespace Bit.Droid.Autofill
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stateService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
|
||||
var disableSavePrompt = await _stateService.GetAutofillDisableSavePromptAsync();
|
||||
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
|
||||
if (disableSavePrompt.GetValueOrDefault())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -80,13 +80,13 @@ namespace Bit.Droid.Autofill
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ShouldAutofillAsync(IStateService stateService)
|
||||
public async Task<bool> ShouldAutofillAsync(IStorageService storageService)
|
||||
{
|
||||
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
|
||||
FieldCollection != null && FieldCollection.Fillable;
|
||||
if (fillable)
|
||||
{
|
||||
var blacklistedUris = await stateService.GetAutofillBlacklistedUrisAsync();
|
||||
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
||||
{
|
||||
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Bit.Droid
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private IMessagingService _messagingService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private IStateService _stateService;
|
||||
private IUserService _userService;
|
||||
private IAppIdService _appIdService;
|
||||
private IEventService _eventService;
|
||||
private PendingIntent _eventUploadPendingIntent;
|
||||
@@ -54,7 +54,7 @@ namespace Bit.Droid
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Bit.Droid
|
||||
}
|
||||
|
||||
#if !FDROID
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
|
||||
var appCenterTask = appCenterHelper.InitAsync();
|
||||
#endif
|
||||
|
||||
@@ -373,7 +373,7 @@ namespace Bit.Droid
|
||||
{
|
||||
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
||||
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
|
||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
|
||||
}
|
||||
|
||||
private void ExitApp()
|
||||
|
||||
@@ -97,14 +97,13 @@ namespace Bit.Droid
|
||||
var secureStorageService = new SecureStorageService();
|
||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||
var deviceActionService = new DeviceActionService(stateService, messagingService,
|
||||
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
|
||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||
broadcasterService);
|
||||
var biometricService = new BiometricService();
|
||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService);
|
||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||
|
||||
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
||||
@@ -114,8 +113,7 @@ namespace Bit.Droid
|
||||
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
||||
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||
ServiceContainer.Register<IStateService>("stateService", stateService);
|
||||
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
|
||||
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(mobileStorageService));
|
||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||
@@ -134,7 +132,7 @@ namespace Bit.Droid
|
||||
ServiceContainer.Register<IPushNotificationListenerService>(
|
||||
"pushNotificationListenerService", notificationListenerService);
|
||||
var androidPushNotificationService = new AndroidPushNotificationService(
|
||||
stateService, notificationListenerService);
|
||||
mobileStorageService, notificationListenerService);
|
||||
ServiceContainer.Register<IPushNotificationService>(
|
||||
"pushNotificationService", androidPushNotificationService);
|
||||
#endif
|
||||
@@ -150,6 +148,10 @@ namespace Bit.Droid
|
||||
|
||||
private async Task BootstrapAsync()
|
||||
{
|
||||
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService")
|
||||
.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
|
||||
Constants.DisableFaviconKey, disableFavicon);
|
||||
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace Bit.Droid.Push
|
||||
{
|
||||
public async override void OnNewToken(string token)
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||
|
||||
await stateService.SetPushRegisteredTokenAsync(token);
|
||||
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
|
||||
await pushNotificationService.RegisterAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Android.App;
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Utilities;
|
||||
@@ -13,10 +14,9 @@ namespace Bit.Droid.Receivers
|
||||
{
|
||||
public override async void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
await AppHelpers.PerformUpdateTasksAsync(
|
||||
ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||
ServiceContainer.Resolve<IStateService>("stateService"));
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
await AppHelpers.PerformUpdateTasksAsync(ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"), storageService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="1792"
|
||||
android:viewportHeight="1792">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M1152,896q0,-106 -75,-181t-181,-75 -181,75 -75,181 75,181 181,75 181,-75 75,-181zM1664,787v222q0,12 -8,23t-20,13l-185,28q-19,54 -39,91 35,50 107,138 10,12 10,25t-9,23q-27,37 -99,108t-94,71q-12,0 -26,-9l-138,-108q-44,23 -91,38 -16,136 -29,186 -7,28 -36,28h-222q-14,0 -24.5,-8.5t-11.5,-21.5l-28,-184q-49,-16 -90,-37l-141,107q-10,9 -25,9 -14,0 -25,-11 -126,-114 -165,-168 -7,-10 -7,-23 0,-12 8,-23 15,-21 51,-66.5t54,-70.5q-27,-50 -41,-99l-183,-27q-13,-2 -21,-12.5t-8,-23.5v-222q0,-12 8,-23t19,-13l186,-28q14,-46 39,-92 -40,-57 -107,-138 -10,-12 -10,-24 0,-10 9,-23 26,-36 98.5,-107.5t94.5,-71.5q13,0 26,10l138,107q44,-23 91,-38 16,-136 29,-186 7,-28 36,-28h222q14,0 24.5,8.5t11.5,21.5l28,184q49,16 90,37l142,-107q9,-9 24,-9 13,0 25,10 129,119 165,170 7,8 7,22 0,12 -8,23 -15,21 -51,66.5t-54,70.5q26,50 41,98l183,28q13,2 21,12.5t8,23.5z" />
|
||||
</vector>
|
||||
@@ -77,9 +77,6 @@
|
||||
<compatibility-package
|
||||
android:name="com.kiwibrowser.browser"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.kiwibrowser.browser.dev"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.microsoft.emmx"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#if !FDROID
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using AndroidX.Core.App;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -10,27 +10,25 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class AndroidPushNotificationService : IPushNotificationService
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
||||
|
||||
public AndroidPushNotificationService(
|
||||
IStateService stateService,
|
||||
IStorageService storageService,
|
||||
IPushNotificationListenerService pushNotificationListenerService)
|
||||
{
|
||||
_stateService = stateService;
|
||||
_storageService = storageService;
|
||||
_pushNotificationListenerService = pushNotificationListenerService;
|
||||
}
|
||||
|
||||
public bool IsRegisteredForPush => NotificationManagerCompat.From(Android.App.Application.Context)?.AreNotificationsEnabled() ?? false;
|
||||
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
return await _stateService.GetPushCurrentTokenAsync();
|
||||
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey);
|
||||
}
|
||||
|
||||
public async Task RegisterAsync()
|
||||
{
|
||||
var registeredToken = await _stateService.GetPushRegisteredTokenAsync();
|
||||
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey);
|
||||
var currentToken = await GetTokenAsync();
|
||||
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
||||
{
|
||||
@@ -38,7 +36,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
|
||||
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class ClipboardService : IClipboardService
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
|
||||
|
||||
public ClipboardService(IStateService stateService)
|
||||
public ClipboardService(IStorageService storageService)
|
||||
{
|
||||
_stateService = stateService;
|
||||
_storageService = storageService;
|
||||
|
||||
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
||||
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
|
||||
@@ -39,7 +39,7 @@ namespace Bit.Droid.Services
|
||||
if (clearMs < 0)
|
||||
{
|
||||
// if not set then we need to check if the user set this config
|
||||
var clearSeconds = await _stateService.GetClearClipboardAsync();
|
||||
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
||||
if (clearSeconds != null)
|
||||
{
|
||||
clearMs = clearSeconds.Value * 1000;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class DeviceActionService : IDeviceActionService
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly Func<IEventService> _eventServiceFunc;
|
||||
@@ -43,12 +43,12 @@ namespace Bit.Droid.Services
|
||||
private string _userAgent;
|
||||
|
||||
public DeviceActionService(
|
||||
IStateService stateService,
|
||||
IStorageService storageService,
|
||||
IMessagingService messagingService,
|
||||
IBroadcasterService broadcasterService,
|
||||
Func<IEventService> eventServiceFunc)
|
||||
{
|
||||
_stateService = stateService;
|
||||
_storageService = storageService;
|
||||
_messagingService = messagingService;
|
||||
_broadcasterService = broadcasterService;
|
||||
_eventServiceFunc = eventServiceFunc;
|
||||
@@ -250,7 +250,7 @@ namespace Bit.Droid.Services
|
||||
try
|
||||
{
|
||||
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
||||
await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
|
||||
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
@@ -833,8 +833,9 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
||||
{
|
||||
var autoCopyDisabled = await _stateService.GetDisableAutoTotpCopyAsync();
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
|
||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
||||
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
||||
{
|
||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
|
||||
@@ -4,6 +4,7 @@ using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Service.QuickSettings;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Accessibility;
|
||||
@@ -17,7 +18,7 @@ namespace Bit.Droid.Tile
|
||||
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
||||
public class AutofillTileService : TileService
|
||||
{
|
||||
private IStateService _stateService;
|
||||
private IStorageService _storageService;
|
||||
|
||||
public override void OnTileAdded()
|
||||
{
|
||||
@@ -58,11 +59,11 @@ namespace Bit.Droid.Tile
|
||||
private void SetTileAdded(bool isAdded)
|
||||
{
|
||||
AccessibilityHelpers.IsAutofillTileAdded = isAdded;
|
||||
if (_stateService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
_stateService.SetAutofillTileAddedAsync(isAdded);
|
||||
_storageService.SaveAsync(Constants.AutofillTileAdded, isAdded);
|
||||
}
|
||||
|
||||
private void ScanAndFill()
|
||||
|
||||
@@ -12,22 +12,22 @@ namespace Bit.Droid.Utilities
|
||||
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
||||
|
||||
private readonly IAppIdService _appIdService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
private string _userId;
|
||||
private string _appId;
|
||||
|
||||
public AppCenterHelper(
|
||||
IAppIdService appIdService,
|
||||
IStateService stateService)
|
||||
IUserService userService)
|
||||
{
|
||||
_appIdService = appIdService;
|
||||
_stateService = stateService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_userId = await _stateService.GetActiveUserIdAsync();
|
||||
_userId = await _userService.GetUserIdAsync();
|
||||
_appId = await _appIdService.GetAppIdAsync();
|
||||
|
||||
AppCenter.Start(AppSecret, typeof(Crashes));
|
||||
|
||||
45
src/App/Abstractions/INavigationService.cs
Normal file
45
src/App/Abstractions/INavigationService.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Bit.App.Pages;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
public interface INavigationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the viewmodel to be the main page of the application
|
||||
/// </summary>
|
||||
void PresentAsMainPage(BaseViewModel viewModel);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the viewmodel as the main page of the application, and wraps its page within a Navigation page
|
||||
/// </summary>
|
||||
void PresentAsNavigatableMainPage(BaseViewModel viewModel);
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to the given page on top of the current navigation stack
|
||||
/// </summary>
|
||||
Task NavigateTo(BaseViewModel viewModel);
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to the previous item in the navigation stack
|
||||
/// </summary>
|
||||
Task NavigateBack();
|
||||
|
||||
/// <summary>
|
||||
/// Navigate back to the element at the root of the navigation stack
|
||||
/// </summary>
|
||||
Task NavigateBackToRoot();
|
||||
}
|
||||
|
||||
public interface IViewLocator
|
||||
{
|
||||
Page CreateAndBindPageFor<TViewModel>(TViewModel viewModel) where TViewModel : BaseViewModel;
|
||||
}
|
||||
|
||||
public interface IMainPage
|
||||
{
|
||||
Page MainPage { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ namespace Bit.App.Abstractions
|
||||
{
|
||||
public interface IPushNotificationService
|
||||
{
|
||||
bool IsRegisteredForPush { get; }
|
||||
Task<string> GetTokenAsync();
|
||||
Task RegisterAsync();
|
||||
Task UnregisterAsync();
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit" Version="1.3.2" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
||||
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2291" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2125" />
|
||||
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
||||
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
@@ -122,7 +120,6 @@
|
||||
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -134,7 +131,6 @@
|
||||
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,8 +4,8 @@ using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
@@ -15,8 +15,9 @@ using Xamarin.Forms.Xaml;
|
||||
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
namespace Bit.App
|
||||
{
|
||||
public partial class App : Application
|
||||
public partial class App : Application, IMainPage
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
@@ -24,6 +25,7 @@ namespace Bit.App
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
|
||||
@@ -37,6 +39,7 @@ namespace Bit.App
|
||||
Current = this;
|
||||
return;
|
||||
}
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
@@ -44,9 +47,12 @@ namespace Bit.App
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
|
||||
var navigator = new NavigationService(this, new ViewLocator());
|
||||
|
||||
Bootstrap();
|
||||
_broadcasterService.Subscribe(nameof(App), async (message) =>
|
||||
{
|
||||
@@ -80,12 +86,8 @@ namespace Bit.App
|
||||
}
|
||||
else if (message.Command == "logout")
|
||||
{
|
||||
var extras = message.Data as Tuple<string, bool, bool>;
|
||||
var userId = extras?.Item1;
|
||||
var userInitiated = extras?.Item2;
|
||||
var expired = extras?.Item3;
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
await LogOutAsync(userId, userInitiated, expired));
|
||||
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
|
||||
}
|
||||
else if (message.Command == "loggedOut")
|
||||
{
|
||||
@@ -106,18 +108,6 @@ namespace Bit.App
|
||||
await SleptAsync();
|
||||
}
|
||||
}
|
||||
else if (message.Command == "addAccount")
|
||||
{
|
||||
await AddAccount();
|
||||
}
|
||||
else if (message.Command == "accountAdded")
|
||||
{
|
||||
UpdateTheme();
|
||||
}
|
||||
else if (message.Command == "switchedAccount")
|
||||
{
|
||||
await SwitchedAccountAsync();
|
||||
}
|
||||
else if (message.Command == "migrated")
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
@@ -179,7 +169,7 @@ namespace Bit.App
|
||||
if (string.IsNullOrWhiteSpace(Options.Uri))
|
||||
{
|
||||
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
||||
_stateService);
|
||||
_storageService);
|
||||
if (!updated)
|
||||
{
|
||||
SyncIfNeeded();
|
||||
@@ -188,8 +178,6 @@ namespace Bit.App
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
// Reset delay on every start
|
||||
_vaultTimeoutService.DelayLockAndLogoutMs = null;
|
||||
}
|
||||
_messagingService.Send("startEventTimer");
|
||||
}
|
||||
@@ -203,7 +191,7 @@ namespace Bit.App
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
if (!isLocked)
|
||||
{
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
||||
}
|
||||
SetTabsPageFromAutofill(isLocked);
|
||||
await SleptAsync();
|
||||
@@ -222,7 +210,7 @@ namespace Bit.App
|
||||
|
||||
private async Task SleptAsync()
|
||||
{
|
||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
await HandleVaultTimeoutAsync();
|
||||
_messagingService.Send("stopEventTimer");
|
||||
}
|
||||
|
||||
@@ -248,65 +236,27 @@ namespace Bit.App
|
||||
new System.Globalization.UmAlQuraCalendar();
|
||||
}
|
||||
|
||||
private async Task LogOutAsync(string userId, bool? userInitiated, bool? expired)
|
||||
private async Task LogOutAsync(bool expired)
|
||||
{
|
||||
await AppHelpers.LogOutAsync(userId, userInitiated.GetValueOrDefault(true));
|
||||
await SetMainPageAsync();
|
||||
await AppHelpers.LogOutAsync();
|
||||
_authService.LogOut(() =>
|
||||
{
|
||||
if (expired.GetValueOrDefault())
|
||||
Current.MainPage = new HomePage();
|
||||
if (expired)
|
||||
{
|
||||
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task AddAccount()
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SwitchedAccountAsync()
|
||||
{
|
||||
await AppHelpers.OnAccountSwitchAsync();
|
||||
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
if (shouldTimeout)
|
||||
{
|
||||
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await SetMainPageAsync();
|
||||
}
|
||||
await Task.Delay(50);
|
||||
UpdateTheme();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SetMainPageAsync()
|
||||
{
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
if (authed)
|
||||
{
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
|
||||
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
||||
if (isLocked || shouldTimeout)
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
if (vaultTimeoutAction == "logOut")
|
||||
{
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||
}
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||
}
|
||||
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
||||
{
|
||||
@@ -327,13 +277,40 @@ namespace Bit.App
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
Current.MainPage = new HomePage(Options);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleVaultTimeoutAsync()
|
||||
{
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
|
||||
if (vaultTimeout == 0)
|
||||
{
|
||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
|
||||
if (action == "logOut")
|
||||
{
|
||||
await _vaultTimeoutService.LogOutAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await _vaultTimeoutService.LockAsync(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ClearCacheIfNeededAsync()
|
||||
{
|
||||
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
||||
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey);
|
||||
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
|
||||
{
|
||||
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
||||
@@ -376,12 +353,12 @@ namespace Bit.App
|
||||
{
|
||||
InitializeComponent();
|
||||
SetCulture();
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
Current.RequestedThemeChanged += (s, a) =>
|
||||
{
|
||||
UpdateTheme();
|
||||
};
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
Current.MainPage = new HomePage();
|
||||
var mainPageTask = SetMainPageAsync();
|
||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||
}
|
||||
@@ -407,16 +384,17 @@ namespace Bit.App
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
_messagingService.Send("updatedTheme");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LockedAsync(bool autoPromptBiometric)
|
||||
{
|
||||
await _stateService.PurgeAsync();
|
||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
if (vaultTimeout == 0)
|
||||
{
|
||||
autoPromptBiometric = false;
|
||||
@@ -446,7 +424,7 @@ namespace Bit.App
|
||||
}
|
||||
}
|
||||
}
|
||||
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
|
||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
x:Class="Bit.App.Controls.AccountViewCell"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="controls:AccountViewCellViewModel">
|
||||
<Grid RowSpacing="0"
|
||||
ColumnSpacing="0">
|
||||
|
||||
<Grid.Resources>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid
|
||||
IsVisible="{Binding IsAccount}"
|
||||
VerticalOptions="CenterAndExpand">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
RowSpacing="3"
|
||||
Margin="12,0,0,0"
|
||||
VerticalOptions="Center">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Text="{Binding AccountView.Email}"
|
||||
StyleClass="list-title"
|
||||
LineBreakMode="TailTruncation" />
|
||||
<Label
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Text="{Binding AccountView.Hostname}"
|
||||
StyleClass="list-sub"
|
||||
LineBreakMode="TailTruncation" />
|
||||
</Grid>
|
||||
|
||||
<Label
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Text="{Binding AuthStatusText}"
|
||||
FontSize="Small"
|
||||
FontAttributes="Italic"
|
||||
LineBreakMode="NoWrap"
|
||||
HorizontalOptions="End"
|
||||
Margin="10,0,20,0"
|
||||
VerticalOptions="Center" />
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
IsVisible="{Binding IsAccount, Converter={StaticResource inverseBool}}"
|
||||
VerticalOptions="CenterAndExpand">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
VerticalOptions="Center"
|
||||
Margin="12,0,0,0"
|
||||
WidthRequest="{OnPlatform 14, iOS=14, Android=26}"
|
||||
HeightRequest="{OnPlatform 14, iOS=14, Android=26}"
|
||||
Source="plus.png"
|
||||
xct:IconTintColorEffect.TintColor="{DynamicResource TextColor}"
|
||||
AutomationProperties.IsInAccessibleTree="False" />
|
||||
<Label
|
||||
Text="Add Account"
|
||||
StyleClass="list-title"
|
||||
LineBreakMode="TailTruncation"
|
||||
VerticalOptions="Center"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ViewCell>
|
||||
@@ -1,35 +0,0 @@
|
||||
using Bit.Core.Models.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public partial class AccountViewCell : ViewCell
|
||||
{
|
||||
public static readonly BindableProperty AccountProperty = BindableProperty.Create(
|
||||
nameof(Account), typeof(AccountView), typeof(AccountViewCell), default(AccountView), BindingMode.OneWay);
|
||||
|
||||
public AccountViewCell()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public AccountView Account
|
||||
{
|
||||
get => GetValue(AccountProperty) as AccountView;
|
||||
set => SetValue(AccountProperty, value);
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(string propertyName = null)
|
||||
{
|
||||
base.OnPropertyChanged(propertyName);
|
||||
if (propertyName == AccountProperty.PropertyName)
|
||||
{
|
||||
if (Account == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BindingContext = new AccountViewCellViewModel(Account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class AccountViewCellViewModel : ExtendedViewModel
|
||||
{
|
||||
private AccountView _accountView;
|
||||
|
||||
public AccountViewCellViewModel(AccountView accountView)
|
||||
{
|
||||
AccountView = accountView;
|
||||
}
|
||||
|
||||
public AccountView AccountView
|
||||
{
|
||||
get => _accountView;
|
||||
set => SetProperty(ref _accountView, value);
|
||||
}
|
||||
|
||||
public bool IsAccount
|
||||
{
|
||||
get => AccountView.IsAccount;
|
||||
}
|
||||
|
||||
public string AuthStatusText
|
||||
{
|
||||
get => AccountView.AuthStatus.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SkiaSharp;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class AvatarImageSource : StreamImageSource
|
||||
{
|
||||
private string _data;
|
||||
|
||||
public AvatarImageSource(string data = null)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
|
||||
|
||||
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
|
||||
{
|
||||
OnLoadingStarted();
|
||||
userToken.Register(CancellationTokenSource.Cancel);
|
||||
var result = Draw();
|
||||
OnLoadingCompleted(CancellationTokenSource.IsCancellationRequested);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private Stream Draw()
|
||||
{
|
||||
string chars = null;
|
||||
string upperData = null;
|
||||
|
||||
if (string.IsNullOrEmpty(_data))
|
||||
{
|
||||
chars = "..";
|
||||
}
|
||||
else if (_data?.Length > 2)
|
||||
{
|
||||
upperData = _data.ToUpper();
|
||||
chars = upperData.Substring(0, 2).ToUpper();
|
||||
}
|
||||
|
||||
var bgColor = StringToColor(upperData);
|
||||
var textColor = Color.White;
|
||||
var size = 50;
|
||||
|
||||
var bitmap = new SKBitmap(
|
||||
size * 2,
|
||||
size * 2,
|
||||
SKImageInfo.PlatformColorType,
|
||||
SKAlphaType.Premul);
|
||||
var canvas = new SKCanvas(bitmap);
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
|
||||
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
||||
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
|
||||
var radius = midX - midX / 5;
|
||||
|
||||
var circlePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
StrokeJoin = SKStrokeJoin.Miter,
|
||||
Color = SKColor.Parse(bgColor.ToHex())
|
||||
};
|
||||
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
||||
|
||||
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
|
||||
var textSize = midX / 1.3f;
|
||||
var textPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
Color = SKColor.Parse(textColor.ToHex()),
|
||||
TextSize = textSize,
|
||||
TextAlign = SKTextAlign.Center,
|
||||
Typeface = typeface
|
||||
};
|
||||
var rect = new SKRect();
|
||||
textPaint.MeasureText(chars, ref rect);
|
||||
canvas.DrawText(chars, midX, midY + rect.Height / 2, textPaint);
|
||||
|
||||
return SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100).AsStream();
|
||||
}
|
||||
|
||||
private Color StringToColor(string str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return Color.FromHex("#33ffffff");
|
||||
}
|
||||
var hash = 0;
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
hash = str[i] + ((hash << 5) - hash);
|
||||
}
|
||||
var color = "#FF";
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var value = (hash >> (i * 8)) & 0xff;
|
||||
var base16 = "00" + Convert.ToString(value, 16);
|
||||
color += base16.Substring(base16.Length - 2);
|
||||
}
|
||||
//if (Device.RuntimePlatform == Device.iOS)
|
||||
//{
|
||||
// // TODO remove this once iOS ToolbarItem tint issue is solved
|
||||
// return Color.FromHex("#33ffffff");
|
||||
//}
|
||||
return Color.FromHex(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class ExtendedToolbarItem : ToolbarItem
|
||||
{
|
||||
public bool UseOriginalImage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Bit.Core.Models.Data
|
||||
namespace Bit.App.Models
|
||||
{
|
||||
public class PreviousPageInfo
|
||||
{
|
||||
@@ -14,7 +14,7 @@ namespace Bit.App.Pages
|
||||
public class BaseChangePasswordViewModel : BaseViewModel
|
||||
{
|
||||
protected readonly IPlatformUtilsService _platformUtilsService;
|
||||
protected readonly IStateService _stateService;
|
||||
protected readonly IUserService _userService;
|
||||
protected readonly IPolicyService _policyService;
|
||||
protected readonly IPasswordGenerationService _passwordGenerationService;
|
||||
protected readonly II18nService _i18nService;
|
||||
@@ -31,7 +31,7 @@ namespace Bit.App.Pages
|
||||
protected BaseChangePasswordViewModel()
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
@@ -172,7 +172,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||
{
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
List<string> userInput = null;
|
||||
var atPosition = email.IndexOf('@');
|
||||
if (atPosition > -1)
|
||||
|
||||
@@ -10,11 +10,14 @@ namespace Bit.App.Pages
|
||||
public partial class EnvironmentPage : BaseContentPage
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly EnvironmentPageViewModel _vm;
|
||||
|
||||
public EnvironmentPage()
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as EnvironmentPageViewModel;
|
||||
_vm.Page = this;
|
||||
@@ -32,6 +35,7 @@ namespace Bit.App.Pages
|
||||
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,107 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.HomePage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:HomeViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
<ContentPage.BindingContext>
|
||||
<pages:HomeViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<controls:ExtendedToolbarItem
|
||||
x:Name="_accountAvatar"
|
||||
x:Key="accountAvatar"
|
||||
IconImageSource="{Binding AvatarImageSource}"
|
||||
Clicked="AccountSwitch_Clicked"
|
||||
Order="Primary"
|
||||
Priority="-1"
|
||||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Account}" />
|
||||
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
|
||||
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
<ToolbarItem
|
||||
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Options}" />
|
||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="0" Padding="10, 5">
|
||||
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
Source="logo.png"
|
||||
VerticalOptions="Center" />
|
||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||
StyleClass="text-lg"
|
||||
HorizontalTextAlignment="Center">
|
||||
</Label>
|
||||
<StackLayout Spacing="5">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
<Button Text="{u:I18n CreateAccount}"
|
||||
Clicked="Register_Clicked" />
|
||||
<Button Text="{u:I18n LogInSso}"
|
||||
Clicked="LogInSso_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout Spacing="0" Padding="10, 5">
|
||||
<controls:FaButton Text=""
|
||||
StyleClass="btn-muted, btn-icon, btn-icon-platform"
|
||||
HorizontalOptions="Start"
|
||||
Clicked="Environment_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Options}">
|
||||
<controls:FaButton.Margin>
|
||||
<OnPlatform x:TypeArguments="Thickness">
|
||||
<On Platform="iOS" Value="0, 10, 0, 0" />
|
||||
<On Platform="Android" Value="0" />
|
||||
</OnPlatform>
|
||||
</controls:FaButton.Margin>
|
||||
</controls:FaButton>
|
||||
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
Source="logo.png"
|
||||
VerticalOptions="Center" />
|
||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||
StyleClass="text-lg"
|
||||
HorizontalTextAlignment="Center"></Label>
|
||||
<StackLayout Spacing="5">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
<Button Text="{u:I18n CreateAccount}"
|
||||
Clicked="Register_Clicked" />
|
||||
<Button Text="{u:I18n LogInSso}"
|
||||
Clicked="LogInSso_Clicked" />
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<AbsoluteLayout
|
||||
x:Name="_absLayout"
|
||||
VerticalOptions="FillAndExpand"
|
||||
HorizontalOptions="FillAndExpand">
|
||||
<ContentView
|
||||
x:Name="_mainContent"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<!-- Account Switching Overlay -->
|
||||
<ContentView
|
||||
x:Name="_accountListOverlay"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
IsVisible="False"
|
||||
BackgroundColor="#22000000"
|
||||
Padding="0">
|
||||
|
||||
<StackLayout
|
||||
x:Name="_accountListContainer"
|
||||
VerticalOptions="StartAndExpand"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||
xct:ShadowEffect.Color="Black"
|
||||
xct:ShadowEffect.Radius="10"
|
||||
xct:ShadowEffect.OffsetY="3">
|
||||
<ListView
|
||||
x:Name="_accountListView"
|
||||
ItemsSource="{Binding AccountViews}"
|
||||
ItemSelected="AccountRow_Selected"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="FillAndExpand"
|
||||
RowHeight="60">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="view:AccountView">
|
||||
<controls:AccountViewCell
|
||||
Account="{Binding .}" />
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -12,10 +12,13 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly HomeViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
private IMessagingService _messagingService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
|
||||
public HomePage(AppOptions appOptions = null)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
@@ -26,11 +29,6 @@ namespace Bit.App.Pages
|
||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
||||
UpdateLogo();
|
||||
|
||||
if (!_appOptions?.IosExtension ?? false)
|
||||
{
|
||||
ToolbarItems.Remove(_closeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DismissRegisterPageAndLogInAsync(string email)
|
||||
@@ -39,18 +37,10 @@ namespace Bit.App.Pages
|
||||
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
|
||||
}
|
||||
|
||||
protected override async void OnAppearing()
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
_mainContent.Content = _mainLayout;
|
||||
if (await ShowAccountSwitcherAsync())
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
|
||||
{
|
||||
if (message.Command == "updatedTheme")
|
||||
@@ -137,23 +127,5 @@ namespace Bit.App.Pages
|
||||
var page = new EnvironmentPage();
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (_accountListOverlay.IsVisible)
|
||||
{
|
||||
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RefreshAccountViewsAsync(_accountListView, false);
|
||||
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,15 @@
|
||||
using System;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class HomeViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
public HomeViewModel()
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
|
||||
PageTitle = AppResources.Bitwarden;
|
||||
}
|
||||
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
|
||||
public Action StartLoginAction { get; set; }
|
||||
public Action StartRegisterAction { get; set; }
|
||||
public Action StartSsoLoginAction { get; set; }
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.LockPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:LockPageViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
@@ -15,19 +13,6 @@
|
||||
<pages:LockPageViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<controls:ExtendedToolbarItem
|
||||
x:Name="_accountAvatar"
|
||||
x:Key="accountAvatar"
|
||||
IconImageSource="{Binding AvatarImageSource}"
|
||||
Clicked="AccountSwitch_Clicked"
|
||||
Order="Primary"
|
||||
Priority="-1"
|
||||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Account}" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
@@ -40,162 +25,119 @@
|
||||
x:Name="_logOut"
|
||||
Clicked="LogOut_Clicked"
|
||||
Order="Secondary"/>
|
||||
|
||||
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
|
||||
<StackLayout Spacing="20">
|
||||
<StackLayout StyleClass="box">
|
||||
<Grid
|
||||
StyleClass="box-row"
|
||||
IsVisible="{Binding PinLock}"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n PIN}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_pin"
|
||||
Text="{Binding Pin}"
|
||||
StyleClass="box-value"
|
||||
Keyboard="Numeric"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding SubmitCommand}" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="_passwordGrid"
|
||||
StyleClass="box-row"
|
||||
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n MasterPassword}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_masterPassword"
|
||||
Text="{Binding MasterPassword}"
|
||||
StyleClass="box-value"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding SubmitCommand}" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<StackLayout
|
||||
StyleClass="box-row"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Label
|
||||
Text="{Binding LockedVerifyText}"
|
||||
StyleClass="box-footer-label" />
|
||||
<Label
|
||||
Text="{Binding LoggedInAsText}"
|
||||
StyleClass="box-footer-label"
|
||||
Margin="0, 10, 0, 0" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout Padding="10, 0">
|
||||
<Label
|
||||
Text="{u:I18n BiometricInvalidated}"
|
||||
StyleClass="box-footer-label,text-danger,text-bold"
|
||||
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
|
||||
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
|
||||
IsVisible="{Binding BiometricButtonVisible}">
|
||||
</Button>
|
||||
<Button
|
||||
x:Name="_unlockButton"
|
||||
Text="{u:I18n Unlock}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="Unlock_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<AbsoluteLayout
|
||||
x:Name="_absLayout"
|
||||
VerticalOptions="FillAndExpand"
|
||||
HorizontalOptions="FillAndExpand">
|
||||
<ContentView
|
||||
x:Name="_mainContent"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
<ContentPage.ToolbarItems>
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<!-- Account Switching Overlay -->
|
||||
<ContentView
|
||||
x:Name="_accountListOverlay"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
IsVisible="False"
|
||||
BackgroundColor="#22000000"
|
||||
Padding="0">
|
||||
|
||||
<StackLayout
|
||||
x:Name="_accountListContainer"
|
||||
VerticalOptions="StartAndExpand"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||
xct:ShadowEffect.Color="Black"
|
||||
xct:ShadowEffect.Radius="10"
|
||||
xct:ShadowEffect.OffsetY="3">
|
||||
<ListView
|
||||
x:Name="_accountListView"
|
||||
ItemsSource="{Binding AccountViews}"
|
||||
ItemSelected="AccountRow_Selected"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="FillAndExpand"
|
||||
RowHeight="60">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="view:AccountView">
|
||||
<controls:AccountViewCell
|
||||
Account="{Binding .}" />
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<ScrollView>
|
||||
<StackLayout Spacing="20">
|
||||
<StackLayout StyleClass="box">
|
||||
<Grid
|
||||
StyleClass="box-row"
|
||||
IsVisible="{Binding PinLock}"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n PIN}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_pin"
|
||||
Text="{Binding Pin}"
|
||||
StyleClass="box-value"
|
||||
Keyboard="Numeric"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding SubmitCommand}" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="_passwordGrid"
|
||||
StyleClass="box-row"
|
||||
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n MasterPassword}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_masterPassword"
|
||||
Text="{Binding MasterPassword}"
|
||||
StyleClass="box-value"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding SubmitCommand}" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<StackLayout
|
||||
StyleClass="box-row"
|
||||
Padding="0, 10, 0, 0">
|
||||
<Label
|
||||
Text="{Binding LockedVerifyText}"
|
||||
StyleClass="box-footer-label" />
|
||||
<Label
|
||||
Text="{Binding LoggedInAsText}"
|
||||
StyleClass="box-footer-label"
|
||||
Margin="0, 10, 0, 0" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
<StackLayout Padding="10, 0">
|
||||
<Label
|
||||
Text="{u:I18n BiometricInvalidated}"
|
||||
StyleClass="box-footer-label,text-danger,text-bold"
|
||||
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
|
||||
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
|
||||
IsVisible="{Binding BiometricButtonVisible}"></Button>
|
||||
<Button
|
||||
x:Name="_unlockButton"
|
||||
Text="{u:I18n Unlock}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="Unlock_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
@@ -61,15 +63,6 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
_appeared = true;
|
||||
_mainContent.Content = _mainLayout;
|
||||
if (await ShowAccountSwitcherAsync())
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
await _vm.InitAsync();
|
||||
if (!_vm.BiometricLock)
|
||||
{
|
||||
@@ -154,23 +147,5 @@ namespace Bit.App.Pages
|
||||
|
||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
|
||||
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (_accountListOverlay.IsVisible)
|
||||
{
|
||||
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RefreshAccountViewsAsync(_accountListView, false);
|
||||
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
@@ -8,7 +10,6 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Models.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -20,7 +21,10 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IBiometricService _biometricService;
|
||||
@@ -45,7 +49,10 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||
@@ -113,11 +120,6 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _lockedVerifyText, value);
|
||||
}
|
||||
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
public Command SubmitCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||
@@ -128,16 +130,16 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
PinLock = (_pinSet.Item1 && _stateService.GetPinProtectedAsync() != null) || _pinSet.Item2;
|
||||
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
||||
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
||||
|
||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||
if (_usingKeyConnector && !(BiometricLock || PinLock))
|
||||
if ( _usingKeyConnector && !(BiometricLock || PinLock))
|
||||
{
|
||||
await _vaultTimeoutService.LogOutAsync();
|
||||
}
|
||||
_email = await _stateService.GetEmailAsync();
|
||||
_email = await _userService.GetEmailAsync();
|
||||
var webVault = _environmentService.GetWebVaultUrl();
|
||||
if (string.IsNullOrWhiteSpace(webVault))
|
||||
{
|
||||
@@ -202,8 +204,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
ShowPassword = false;
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
|
||||
if (PinLock)
|
||||
{
|
||||
@@ -214,9 +216,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
||||
await _stateService.GetPinProtectedCachedAsync());
|
||||
_vaultTimeoutService.PinProtectedKey);
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
failed = decPin != Pin;
|
||||
if (!failed)
|
||||
@@ -285,12 +287,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_pinSet.Item1)
|
||||
{
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||
await _stateService.SetPinProtectedCachedAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||
}
|
||||
MasterPassword = string.Empty;
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
@@ -356,7 +358,7 @@ namespace Bit.App.Pages
|
||||
page.MasterPasswordEntry.Focus();
|
||||
}
|
||||
});
|
||||
_stateService.BiometricLocked = !success;
|
||||
_vaultTimeoutService.BiometricLocked = !success;
|
||||
if (success)
|
||||
{
|
||||
await DoContinueAsync();
|
||||
@@ -375,7 +377,9 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task DoContinueAsync()
|
||||
{
|
||||
_stateService.BiometricLocked = false;
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
_messagingService.Send("unlocked");
|
||||
UnlockedAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.LoginPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:LoginPageViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
@@ -15,21 +13,6 @@
|
||||
<pages:LoginPageViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<controls:ExtendedToolbarItem
|
||||
x:Name="_accountAvatar"
|
||||
x:Key="accountAvatar"
|
||||
IconImageSource="{Binding AvatarImageSource}"
|
||||
Clicked="AccountSwitch_Clicked"
|
||||
Order="Primary"
|
||||
Priority="-1"
|
||||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Account}" />
|
||||
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
|
||||
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
@@ -42,109 +25,68 @@
|
||||
x:Name="_getPasswordHint"
|
||||
Clicked="Hint_Clicked"
|
||||
Order="Secondary"/>
|
||||
|
||||
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
|
||||
<StackLayout Spacing="20">
|
||||
<StackLayout StyleClass="box">
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
Text="{u:I18n EmailAddress}"
|
||||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_email"
|
||||
Text="{Binding Email}"
|
||||
Keyboard="Email"
|
||||
StyleClass="box-value" />
|
||||
</StackLayout>
|
||||
<Grid StyleClass="box-row">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n MasterPassword}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_masterPassword"
|
||||
Text="{Binding MasterPassword}"
|
||||
StyleClass="box-value"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding LogInCommand}" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
<StackLayout Padding="10, 0">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<AbsoluteLayout
|
||||
x:Name="_absLayout"
|
||||
VerticalOptions="FillAndExpand"
|
||||
HorizontalOptions="FillAndExpand">
|
||||
<ContentView
|
||||
x:Name="_mainContent"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<!-- Account Switching Overlay -->
|
||||
<ContentView
|
||||
x:Name="_accountListOverlay"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
IsVisible="False"
|
||||
BackgroundColor="#22000000"
|
||||
Padding="0">
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<StackLayout
|
||||
x:Name="_accountListContainer"
|
||||
VerticalOptions="StartAndExpand"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||
xct:ShadowEffect.Color="Black"
|
||||
xct:ShadowEffect.Radius="10"
|
||||
xct:ShadowEffect.OffsetY="3">
|
||||
<ListView
|
||||
x:Name="_accountListView"
|
||||
ItemsSource="{Binding AccountViews}"
|
||||
ItemSelected="AccountRow_Selected"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="FillAndExpand"
|
||||
RowHeight="60">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="view:AccountView">
|
||||
<controls:AccountViewCell
|
||||
Account="{Binding .}" />
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<ScrollView>
|
||||
<StackLayout Spacing="20">
|
||||
<StackLayout StyleClass="box">
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
Text="{u:I18n EmailAddress}"
|
||||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_email"
|
||||
Text="{Binding Email}"
|
||||
Keyboard="Email"
|
||||
StyleClass="box-value" />
|
||||
</StackLayout>
|
||||
<Grid StyleClass="box-row">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Text="{u:I18n MasterPassword}"
|
||||
StyleClass="box-label"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
x:Name="_masterPassword"
|
||||
Text="{Binding MasterPassword}"
|
||||
StyleClass="box-value"
|
||||
IsSpellCheckEnabled="False"
|
||||
IsTextPredictionEnabled="False"
|
||||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding LogInCommand}" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
<StackLayout Padding="10, 0">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
@@ -9,6 +11,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class LoginPage : BaseContentPage
|
||||
{
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly LoginPageViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
@@ -16,6 +20,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public LoginPage(string email = null, AppOptions appOptions = null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as LoginPageViewModel;
|
||||
@@ -26,6 +33,7 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
_vm.Email = email;
|
||||
@@ -46,11 +54,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
ToolbarItems.Add(_getPasswordHint);
|
||||
}
|
||||
|
||||
if (!_appOptions?.IosExtension ?? false)
|
||||
{
|
||||
ToolbarItems.Remove(_closeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public Entry MasterPasswordEntry { get; set; }
|
||||
@@ -58,15 +61,6 @@ namespace Bit.App.Pages
|
||||
protected override async void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
_mainContent.Content = _mainLayout;
|
||||
if (await ShowAccountSwitcherAsync())
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
await _vm.InitAsync();
|
||||
if (!_inputFocused)
|
||||
{
|
||||
@@ -136,23 +130,5 @@ namespace Bit.App.Pages
|
||||
var page = new UpdateTempPasswordPage();
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (_accountListOverlay.IsVisible)
|
||||
{
|
||||
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RefreshAccountViewsAsync(_accountListView, false);
|
||||
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Models.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class LoginPageViewModel : CaptchaProtectedViewModel
|
||||
{
|
||||
private const string Keys_RememberedEmail = "rememberedEmail";
|
||||
private const string Keys_RememberEmail = "rememberEmail";
|
||||
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
@@ -30,6 +34,7 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
@@ -62,11 +67,6 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _masterPassword, value);
|
||||
}
|
||||
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
public Command LogInCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||
@@ -85,9 +85,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Email))
|
||||
{
|
||||
Email = await _stateService.GetRememberedEmailAsync();
|
||||
Email = await _storageService.GetAsync<string>(Keys_RememberedEmail);
|
||||
}
|
||||
var rememberEmail = await _stateService.GetRememberEmailAsync();
|
||||
var rememberEmail = await _storageService.GetAsync<bool?>(Keys_RememberEmail);
|
||||
RememberEmail = rememberEmail.GetValueOrDefault(true);
|
||||
}
|
||||
|
||||
@@ -131,11 +131,11 @@ namespace Bit.App.Pages
|
||||
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
||||
if (RememberEmail)
|
||||
{
|
||||
await _stateService.SetRememberedEmailAsync(Email);
|
||||
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _stateService.SetRememberedEmailAsync(null);
|
||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
||||
}
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
|
||||
@@ -163,6 +163,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||
LogInSuccessAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class LoginSsoPage : BaseContentPage
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly LoginSsoPageViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
@@ -18,7 +20,10 @@ namespace Bit.App.Pages
|
||||
|
||||
public LoginSsoPage(AppOptions appOptions = null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as LoginSsoPageViewModel;
|
||||
@@ -31,6 +36,7 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
@@ -15,12 +16,16 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class LoginSsoPageViewModel : BaseViewModel
|
||||
{
|
||||
private const string Keys_RememberedOrgIdentifier = "rememberedOrgIdentifier";
|
||||
private const string Keys_RememberOrgIdentifier = "rememberOrgIdentifier";
|
||||
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
@@ -35,6 +40,7 @@ namespace Bit.App.Pages
|
||||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
|
||||
@@ -60,9 +66,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(OrgIdentifier))
|
||||
{
|
||||
OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
|
||||
OrgIdentifier = await _storageService.GetAsync<string>(Keys_RememberedOrgIdentifier);
|
||||
}
|
||||
var rememberOrgIdentifier = await _stateService.GetRememberOrgIdentifierAsync();
|
||||
var rememberOrgIdentifier = await _storageService.GetAsync<bool?>(Keys_RememberOrgIdentifier);
|
||||
RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true);
|
||||
}
|
||||
|
||||
@@ -166,11 +172,11 @@ namespace Bit.App.Pages
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
if (RememberOrgIdentifier)
|
||||
{
|
||||
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
||||
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _stateService.SetRememberedOrgIdentifierAsync(null);
|
||||
await _storageService.RemoveAsync(Keys_RememberedOrgIdentifier);
|
||||
}
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
if (response.TwoFactor)
|
||||
@@ -187,6 +193,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -6,18 +8,22 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class RegisterPage : BaseContentPage
|
||||
{
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly RegisterPageViewModel _vm;
|
||||
|
||||
private bool _inputFocused;
|
||||
|
||||
public RegisterPage(HomePage homePage)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as RegisterPageViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
MasterPasswordEntry = _masterPassword;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
@@ -8,11 +10,14 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class SetPasswordPage : BaseContentPage
|
||||
{
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly SetPasswordPageViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as SetPasswordPageViewModel;
|
||||
@@ -21,6 +26,7 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
_vm.OrgIdentifier = orgIdentifier;
|
||||
|
||||
@@ -7,6 +7,7 @@ using Bit.Core.Models.Request;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -22,7 +23,7 @@ namespace Bit.App.Pages
|
||||
private readonly IApiService _apiService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly II18nService _i18nService;
|
||||
@@ -39,7 +40,7 @@ namespace Bit.App.Pages
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
@@ -158,7 +159,7 @@ namespace Bit.App.Pages
|
||||
|
||||
var kdf = KdfType.PBKDF2_SHA256;
|
||||
var kdfIterations = 100000;
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||
@@ -195,8 +196,8 @@ namespace Bit.App.Pages
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||
// Set Password and relevant information
|
||||
await _apiService.SetPasswordAsync(request);
|
||||
await _stateService.SetKdfTypeAsync(kdf);
|
||||
await _stateService.SetKdfIterationsAsync(kdfIterations);
|
||||
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
||||
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||
@@ -215,7 +216,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
ResetPasswordKey = encryptedKey.EncryptedString
|
||||
};
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
// Enroll user
|
||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||
}
|
||||
@@ -288,7 +289,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||
{
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
List<string> userInput = null;
|
||||
var atPosition = email.IndexOf('@');
|
||||
if (atPosition > -1)
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
private TwoFactorPageViewModel _vm;
|
||||
@@ -28,8 +30,10 @@ namespace Bit.App.Pages
|
||||
_authingWithSso = authingWithSso ?? false;
|
||||
_appOptions = appOptions;
|
||||
_orgIdentifier = orgIdentifier;
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_vm = BindingContext as TwoFactorPageViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.StartSetPasswordAction = () =>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
@@ -22,6 +23,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
@@ -41,6 +43,7 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
@@ -300,6 +303,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
TwoFactorAuthSuccessAction?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ namespace Bit.App.Pages
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
|
||||
// Service Use
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
|
||||
// Binding
|
||||
InitializeComponent();
|
||||
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
||||
|
||||
@@ -43,9 +43,9 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
// Retrieve details for key generation
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
|
||||
// Create new key and hash new password
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Enums;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.PlatformConfiguration;
|
||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||
@@ -14,9 +13,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class BaseContentPage : ContentPage
|
||||
{
|
||||
private IStateService _stateService;
|
||||
private IStorageService _storageService;
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private IMessagingService _messagingService;
|
||||
|
||||
protected int ShowModalAnimationDelay = 400;
|
||||
protected int ShowPageAnimationDelay = 100;
|
||||
@@ -32,10 +30,10 @@ namespace Bit.App.Pages
|
||||
|
||||
public DateTime? LastPageAction { get; set; }
|
||||
|
||||
protected async override void OnAppearing()
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
await SaveActivity();
|
||||
SaveActivity();
|
||||
}
|
||||
|
||||
public bool DoOnce(Action action = null, int milliseconds = 1000)
|
||||
@@ -108,121 +106,22 @@ namespace Bit.App.Pages
|
||||
});
|
||||
}
|
||||
|
||||
protected async Task<bool> ShowAccountSwitcherAsync()
|
||||
{
|
||||
return await _stateService.HasMultipleAccountsAsync();
|
||||
}
|
||||
|
||||
protected async Task RefreshAccountViewsAsync(Xamarin.Forms.ListView accountListView, bool allowAddAccountRow)
|
||||
{
|
||||
await _stateService.RefreshAccountViewsAsync(allowAddAccountRow);
|
||||
// Property change trigger on account listview is yielding inconsistent results, using a hammer instead
|
||||
accountListView.ItemsSource = null;
|
||||
accountListView.ItemsSource = _stateService.AccountViews;
|
||||
}
|
||||
protected async Task<AvatarImageSource> GetAvatarImageSourceAsync(bool useCurrentActiveAccount = true)
|
||||
{
|
||||
return new AvatarImageSource(useCurrentActiveAccount ? await _stateService.GetEmailAsync() : null);
|
||||
}
|
||||
|
||||
protected async Task ShowAccountListAsync(bool isVisible, View listContainer, View overlay, View fab = null)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
// Not all animations are awaited. This is intentional to allow multiple simultaneous animations.
|
||||
if (isVisible)
|
||||
{
|
||||
// start listView in default (off-screen) position
|
||||
await listContainer.TranslateTo(0, listContainer.Height * -1, 0);
|
||||
|
||||
// set overlay opacity to zero before making visible and start fade-in
|
||||
overlay.Opacity = 0;
|
||||
overlay.IsVisible = true;
|
||||
overlay.FadeTo(1, 100);
|
||||
|
||||
if (Device.RuntimePlatform == Device.Android && fab != null)
|
||||
{
|
||||
// start fab fade-out
|
||||
fab.FadeTo(0, 200);
|
||||
}
|
||||
|
||||
// slide account list into view
|
||||
await listContainer.TranslateTo(0, 0, 200, Easing.SinOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
// start overlay fade-out
|
||||
overlay.FadeTo(0, 200);
|
||||
|
||||
if (Device.RuntimePlatform == Device.Android && fab != null)
|
||||
{
|
||||
// start fab fade-in
|
||||
fab.FadeTo(1, 200);
|
||||
}
|
||||
|
||||
// slide account list out of view
|
||||
await listContainer.TranslateTo(0, listContainer.Height * -1, 200, Easing.SinIn);
|
||||
|
||||
// remove overlay
|
||||
overlay.IsVisible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected async Task AccountRowSelectedAsync(object sender, SelectedItemChangedEventArgs e, View listContainer,
|
||||
View overlay, View fab = null, bool? allowActiveAccountSelection = false)
|
||||
{
|
||||
if (!DoOnce())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!(e.SelectedItem is AccountViewCellViewModel item))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
((Xamarin.Forms.ListView)sender).SelectedItem = null;
|
||||
await Task.Delay(100);
|
||||
await ShowAccountListAsync(false, listContainer, overlay, fab);
|
||||
|
||||
if (item.AccountView.IsAccount)
|
||||
{
|
||||
if (item.AccountView.AuthStatus != AuthenticationStatus.Active)
|
||||
{
|
||||
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||
_messagingService.Send("switchedAccount");
|
||||
}
|
||||
else if (allowActiveAccountSelection ?? false)
|
||||
{
|
||||
_messagingService.Send("switchedAccount");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_messagingService.Send("addAccount");
|
||||
}
|
||||
}
|
||||
|
||||
private void SetServices()
|
||||
{
|
||||
if (_stateService == null)
|
||||
if (_storageService == null)
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
}
|
||||
if (_deviceActionService == null)
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
}
|
||||
if (_messagingService == null)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveActivity()
|
||||
private void SaveActivity()
|
||||
{
|
||||
SetServices();
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
_storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -7,7 +6,6 @@ namespace Bit.App.Pages
|
||||
public abstract class BaseViewModel : ExtendedViewModel
|
||||
{
|
||||
private string _pageTitle = string.Empty;
|
||||
private AvatarImageSource _avatar;
|
||||
|
||||
public string PageTitle
|
||||
{
|
||||
@@ -15,12 +13,6 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _pageTitle, value);
|
||||
}
|
||||
|
||||
public AvatarImageSource AvatarImageSource
|
||||
{
|
||||
get => _avatar ?? new AvatarImageSource();
|
||||
set => SetProperty(ref _avatar, value);
|
||||
}
|
||||
|
||||
public ContentPage Page { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendService _sendService;
|
||||
private bool _sendEnabled;
|
||||
private bool _canAccessPremium;
|
||||
@@ -51,7 +51,7 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
|
||||
@@ -220,8 +220,8 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
||||
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_emailVerified = await _stateService.GetEmailVerifiedAsync();
|
||||
_canAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
_emailVerified = await _userService.GetEmailVerifiedAsync();
|
||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
||||
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
||||
|
||||
@@ -32,19 +32,21 @@ namespace Bit.App.Pages
|
||||
|
||||
private readonly ISendService _sendService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStorageService _storageService;
|
||||
|
||||
public SendGroupingsPageViewModel()
|
||||
{
|
||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
|
||||
Loading = true;
|
||||
PageTitle = AppResources.Send;
|
||||
@@ -114,7 +116,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
@@ -123,7 +125,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
||||
{
|
||||
SyncRefreshing = true;
|
||||
await _syncService.FullSyncAsync(false);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -10,7 +11,7 @@ namespace Bit.App.Pages
|
||||
public class AutofillServicesPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly MobileI18nService _i18nService;
|
||||
|
||||
private bool _autofillServiceToggled;
|
||||
@@ -22,7 +23,7 @@ namespace Bit.App.Pages
|
||||
public AutofillServicesPageViewModel()
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
||||
PageTitle = AppResources.AutofillServices;
|
||||
}
|
||||
@@ -151,7 +152,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||
InlineAutofillToggled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
@@ -201,7 +202,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
|
||||
await _storageService.SaveAsync(Constants.InlineAutofillEnabledKey, InlineAutofillToggled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class ExtensionPageViewModel : BaseViewModel
|
||||
{
|
||||
private const string StartedKey = "appExtensionStarted";
|
||||
private const string ActivatedKey = "appExtensionActivated";
|
||||
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
|
||||
private bool _started;
|
||||
private bool _activated;
|
||||
@@ -16,7 +20,8 @@ namespace Bit.App.Pages
|
||||
public ExtensionPageViewModel()
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
PageTitle = AppResources.AppExtension;
|
||||
}
|
||||
|
||||
@@ -47,8 +52,8 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
var started = await _stateService.GetAppExtensionStartedAsync();
|
||||
var activated = await _stateService.GetAppExtensionActivatedAsync();
|
||||
var started = await _storageService.GetAsync<bool?>(StartedKey);
|
||||
var activated = await _storageService.GetAsync<bool?>(ActivatedKey);
|
||||
Started = started.GetValueOrDefault();
|
||||
Activated = activated.GetValueOrDefault();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
@@ -12,6 +13,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class OptionsPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ITotpService _totpService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
@@ -30,6 +34,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public OptionsPageViewModel()
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
@@ -159,17 +166,19 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
AutofillDisableSavePrompt = (await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
|
||||
var blacklistedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
AutofillDisableSavePrompt = (await _storageService.GetAsync<bool?>(
|
||||
Constants.AutofillDisableSavePromptKey)).GetValueOrDefault();
|
||||
var blacklistedUrisList = await _storageService.GetAsync<List<string>>(
|
||||
Constants.AutofillBlacklistedUrisKey);
|
||||
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
|
||||
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
|
||||
DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
var theme = await _stateService.GetThemeAsync();
|
||||
DisableFavicon = (await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey)).GetValueOrDefault();
|
||||
var theme = await _storageService.GetAsync<string>(Constants.ThemeKey);
|
||||
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
|
||||
var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
|
||||
var defaultUriMatch = await _storageService.GetAsync<int?>(Constants.DefaultUriMatch);
|
||||
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
|
||||
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
|
||||
var clearClipboard = await _stateService.GetClearClipboardAsync();
|
||||
var clearClipboard = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
||||
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
|
||||
_inited = true;
|
||||
}
|
||||
@@ -178,7 +187,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _stateService.SetDisableAutoTotpCopyAsync(DisableAutoTotpCopy);
|
||||
await _storageService.SaveAsync(Constants.DisableAutoTotpCopyKey, DisableAutoTotpCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +195,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _stateService.SetDisableFaviconAsync(DisableFavicon);
|
||||
await _storageService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +204,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited && ClearClipboardSelectedIndex > -1)
|
||||
{
|
||||
await _stateService.SetClearClipboardAsync(ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||
await _storageService.SaveAsync(Constants.ClearClipboardKey,
|
||||
ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,8 +214,8 @@ namespace Bit.App.Pages
|
||||
if (_inited && ThemeSelectedIndex > -1)
|
||||
{
|
||||
var theme = ThemeOptions[ThemeSelectedIndex].Key;
|
||||
await _stateService.SetThemeAsync(theme);
|
||||
ThemeManager.SetTheme(Application.Current.Resources);
|
||||
await _storageService.SaveAsync(Constants.ThemeKey, theme);
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Application.Current.Resources);
|
||||
_messagingService.Send("updatedTheme");
|
||||
}
|
||||
}
|
||||
@@ -213,7 +224,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited && UriMatchSelectedIndex > -1)
|
||||
{
|
||||
await _stateService.SetDefaultUriMatchAsync((int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||
await _storageService.SaveAsync(Constants.DefaultUriMatch,
|
||||
(int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +233,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _stateService.SetAutofillDisableSavePromptAsync(AutofillDisableSavePrompt);
|
||||
await _storageService.SaveAsync(Constants.AutofillDisableSavePromptKey, AutofillDisableSavePrompt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +243,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
|
||||
{
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(null);
|
||||
await _storageService.RemoveAsync(Constants.AutofillBlacklistedUrisKey);
|
||||
AutofillBlacklistedUris = null;
|
||||
return;
|
||||
}
|
||||
@@ -253,7 +265,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
urisList.Add(cleanedUri);
|
||||
}
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
|
||||
await _storageService.SaveAsync(Constants.AutofillBlacklistedUrisKey, urisList);
|
||||
AutofillBlacklistedUris = string.Join(", ", urisList);
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
@@ -16,11 +17,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IBiometricService _biometricService;
|
||||
private readonly IPolicyService _policyService;
|
||||
@@ -66,11 +68,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
@@ -108,7 +111,7 @@ namespace Bit.App.Pages
|
||||
|
||||
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
|
||||
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
|
||||
var action = await _stateService.GetVaultTimeoutActionAsync() ?? "lock";
|
||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey) ?? "lock";
|
||||
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
|
||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
_pin = pinSet.Item1 || pinSet.Item2;
|
||||
@@ -148,7 +151,7 @@ namespace Bit.App.Pages
|
||||
List<string> fingerprint;
|
||||
try
|
||||
{
|
||||
fingerprint = await _cryptoService.GetFingerprintAsync(await _stateService.GetActiveUserIdAsync());
|
||||
fingerprint = await _cryptoService.GetFingerprintAsync(await _userService.GetUserIdAsync());
|
||||
}
|
||||
catch (Exception e) when (e.Message == "No public key available.")
|
||||
{
|
||||
@@ -326,9 +329,9 @@ namespace Bit.App.Pages
|
||||
AppResources.Yes, AppResources.No);
|
||||
}
|
||||
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
||||
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
||||
kdfIterations.GetValueOrDefault(5000));
|
||||
@@ -338,12 +341,12 @@ namespace Bit.App.Pages
|
||||
if (masterPassOnRestart)
|
||||
{
|
||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||
await _stateService.SetPinProtectedCachedAsync(pinProtectedKey);
|
||||
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString);
|
||||
_vaultTimeoutService.PinProtectedKey = pinProtectedKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
|
||||
await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -378,13 +381,13 @@ namespace Bit.App.Pages
|
||||
if (_biometric)
|
||||
{
|
||||
await _biometricService.SetupBiometricAsync();
|
||||
await _stateService.SetBiometricUnlockAsync(true);
|
||||
await _storageService.SaveAsync(Constants.BiometricUnlockKey, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _stateService.SetBiometricUnlockAsync(null);
|
||||
await _storageService.RemoveAsync(Constants.BiometricUnlockKey);
|
||||
}
|
||||
_stateService.BiometricLocked = false;
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
await _cryptoService.ToggleKeyAsync();
|
||||
BuildList();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -11,7 +12,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly ILocalizeService _localizeService;
|
||||
|
||||
@@ -23,7 +24,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||
|
||||
@@ -51,7 +52,7 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await SetLastSyncAsync();
|
||||
EnableSyncOnRefresh = await _stateService.GetSyncOnRefreshAsync();
|
||||
EnableSyncOnRefresh = await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey);
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _stateService.SetSyncOnRefreshAsync(_syncOnRefresh);
|
||||
await _storageService.SaveAsync(Constants.SyncOnRefreshKey, _syncOnRefresh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -46,7 +45,7 @@ namespace Bit.App.Pages
|
||||
var settingsPage = new NavigationPage(new SettingsPage(this))
|
||||
{
|
||||
Title = AppResources.Settings,
|
||||
IconImageSource = "cog_settings.png"
|
||||
IconImageSource = "cog.png"
|
||||
};
|
||||
Children.Add(settingsPage);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -17,7 +18,7 @@ namespace Bit.App.Pages
|
||||
public partial class AddEditPage : BaseContentPage
|
||||
{
|
||||
private readonly AppOptions _appOptions;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
@@ -37,7 +38,7 @@ namespace Bit.App.Pages
|
||||
bool cloneMode = false,
|
||||
ViewPage viewPage = null)
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
@@ -332,10 +333,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var addLoginShown = await _stateService.GetAddSitePromptShownAsync();
|
||||
var addLoginShown = await _storageService.GetAsync<bool?>(Constants.AddSitePromptShownKey);
|
||||
if (_vm.Cipher.Type == CipherType.Login && !_fromAutofill && !addLoginShown.GetValueOrDefault())
|
||||
{
|
||||
await _stateService.SetAddSitePromptShownAsync(true);
|
||||
await _storageService.SaveAsync(Constants.AddSitePromptShownKey, true);
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
if (_deviceActionService.SystemMajorVersion() < 12)
|
||||
|
||||
@@ -22,8 +22,7 @@ namespace Bit.App.Pages
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
@@ -71,8 +70,7 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
@@ -304,9 +302,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task<bool> LoadAsync(AppOptions appOptions = null)
|
||||
{
|
||||
var myEmail = await _stateService.GetEmailAsync();
|
||||
var myEmail = await _userService.GetEmailAsync();
|
||||
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
|
||||
var orgs = await _organizationService.GetAllAsync();
|
||||
var orgs = await _userService.GetAllOrganizationAsync();
|
||||
foreach (var org in orgs.OrderBy(o => o.Name))
|
||||
{
|
||||
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private CipherView _cipher;
|
||||
private Cipher _cipherDomain;
|
||||
@@ -33,8 +32,7 @@ namespace Bit.App.Pages
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
Attachments = new ExtendedObservableCollection<AttachmentView>();
|
||||
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
|
||||
PageTitle = AppResources.Attachments;
|
||||
@@ -66,7 +64,7 @@ namespace Bit.App.Pages
|
||||
Cipher = await _cipherDomain.DecryptAsync();
|
||||
LoadAttachments();
|
||||
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
var canAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
||||
if (!_canAccessAttachments)
|
||||
{
|
||||
@@ -137,11 +135,6 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task ChooseFileAsync()
|
||||
{
|
||||
// Prevent Android from locking if vault timeout set to "immediate"
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
_vaultTimeoutService.DelayLockAndLogoutMs = 60000;
|
||||
}
|
||||
await _deviceActionService.SelectFileAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -86,7 +88,8 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
ShowList = false;
|
||||
var groupedItems = new List<GroupingsPageListGroup>();
|
||||
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
|
||||
|
||||
@@ -76,7 +76,8 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
||||
{
|
||||
Search((Page as CiphersPage).SearchBar.Text, 200);
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
x:Class="Bit.App.Pages.GroupingsPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
xmlns:effects="clr-namespace:Bit.App.Effects"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
x:DataType="pages:GroupingsPageViewModel"
|
||||
Title="{Binding PageTitle}"
|
||||
x:Name="_page">
|
||||
@@ -17,15 +15,6 @@
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<controls:ExtendedToolbarItem
|
||||
x:Name="_accountAvatar"
|
||||
IconImageSource="{Binding AvatarImageSource}"
|
||||
Clicked="AccountSwitch_Clicked"
|
||||
Order="Primary"
|
||||
Priority="-1"
|
||||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Account}" />
|
||||
<ToolbarItem Icon="search.png" Clicked="Search_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Search}" />
|
||||
@@ -146,8 +135,6 @@
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<!-- Android FAB -->
|
||||
<Button
|
||||
x:Name="_fab"
|
||||
Image="plus.png"
|
||||
@@ -161,40 +148,6 @@
|
||||
<effects:FabShadowEffect />
|
||||
</Button.Effects>
|
||||
</Button>
|
||||
|
||||
<!-- Account Switching Overlay -->
|
||||
<ContentView
|
||||
x:Name="_accountListOverlay"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
IsVisible="False"
|
||||
BackgroundColor="#22000000"
|
||||
Padding="0">
|
||||
|
||||
<StackLayout
|
||||
x:Name="_accountListContainer"
|
||||
VerticalOptions="StartAndExpand"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||
xct:ShadowEffect.Color="Black"
|
||||
xct:ShadowEffect.Radius="10"
|
||||
xct:ShadowEffect.OffsetY="3">
|
||||
<ListView
|
||||
x:Name="_accountListView"
|
||||
ItemsSource="{Binding AccountViews}"
|
||||
ItemSelected="AccountRow_Selected"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="FillAndExpand"
|
||||
RowHeight="60">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="view:AccountView">
|
||||
<controls:AccountViewCell
|
||||
Account="{Binding .}" />
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -7,7 +9,6 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Models.Data;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -17,7 +18,7 @@ namespace Bit.App.Pages
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
@@ -36,7 +37,7 @@ namespace Bit.App.Pages
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
@@ -69,10 +70,6 @@ namespace Bit.App.Pages
|
||||
_absLayout.Children.Remove(_fab);
|
||||
ToolbarItems.Remove(_addItem);
|
||||
}
|
||||
if (!mainPage)
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
protected async override void OnAppearing()
|
||||
@@ -83,11 +80,6 @@ namespace Bit.App.Pages
|
||||
IsBusy = true;
|
||||
}
|
||||
|
||||
if (_vm.MainPage)
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
|
||||
_broadcasterService.Subscribe(_pageName, async (message) =>
|
||||
{
|
||||
if (message.Command == "syncStarted")
|
||||
@@ -108,7 +100,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
});
|
||||
|
||||
var migratedFromV1 = await _stateService.GetMigratedFromV1Async();
|
||||
var migratedFromV1 = await _storageService.GetAsync<bool?>(Constants.MigratedFromV1);
|
||||
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
||||
{
|
||||
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
||||
@@ -136,10 +128,10 @@ namespace Bit.App.Pages
|
||||
!_vm.HasCiphers &&
|
||||
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
|
||||
{
|
||||
var triedV1ReSync = await _stateService.GetTriedV1ResyncAsync();
|
||||
var triedV1ReSync = await _storageService.GetAsync<bool?>(Constants.TriedV1Resync);
|
||||
if (!triedV1ReSync.GetValueOrDefault())
|
||||
{
|
||||
await _stateService.SetTriedV1ResyncAsync(true);
|
||||
await _storageService.SaveAsync(Constants.TriedV1Resync, true);
|
||||
await _syncService.FullSyncAsync(true);
|
||||
}
|
||||
}
|
||||
@@ -153,14 +145,14 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
// Push registration
|
||||
var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
|
||||
var lastPushRegistration = await _storageService.GetAsync<DateTime?>(Constants.PushLastRegistrationDateKey);
|
||||
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
|
||||
var pushPromptShow = await _storageService.GetAsync<bool?>(Constants.PushInitialPromptShownKey);
|
||||
if (!pushPromptShow.GetValueOrDefault(false))
|
||||
{
|
||||
await _stateService.SetPushInitialPromptShownAsync(true);
|
||||
await _storageService.SaveAsync(Constants.PushInitialPromptShownKey, true);
|
||||
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
|
||||
AppResources.OkGotIt);
|
||||
}
|
||||
@@ -181,8 +173,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (migratedFromV1.GetValueOrDefault())
|
||||
{
|
||||
var migratedFromV1AutofillPromptShown =
|
||||
await _stateService.GetMigratedFromV1AutofillPromptShownAsync();
|
||||
var migratedFromV1AutofillPromptShown = await _storageService.GetAsync<bool?>(
|
||||
Constants.MigratedFromV1AutofillPromptShown);
|
||||
if (!migratedFromV1AutofillPromptShown.GetValueOrDefault())
|
||||
{
|
||||
await DisplayAlert(AppResources.Autofill,
|
||||
@@ -190,7 +182,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
}
|
||||
await _stateService.SetMigratedFromV1AutofillPromptShownAsync(true);
|
||||
await _storageService.SaveAsync(Constants.MigratedFromV1AutofillPromptShown, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,24 +284,5 @@ namespace Bit.App.Pages
|
||||
_addItem.IsEnabled = !_vm.Deleted;
|
||||
_addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
|
||||
}
|
||||
|
||||
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
if (_accountListOverlay.IsVisible)
|
||||
{
|
||||
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay, _fab);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RefreshAccountViewsAsync(_accountListView, true);
|
||||
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay, _fab);
|
||||
}
|
||||
}
|
||||
|
||||
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, _fab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
@@ -10,7 +11,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -39,11 +39,13 @@ namespace Bit.App.Pages
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||
|
||||
public GroupingsPageViewModel()
|
||||
@@ -52,11 +54,13 @@ namespace Bit.App.Pages
|
||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||
|
||||
Loading = true;
|
||||
@@ -76,6 +80,7 @@ namespace Bit.App.Pages
|
||||
public string CollectionId { get; set; }
|
||||
public Func<CipherView, bool> Filter { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
public bool HasCiphers { get; set; }
|
||||
public bool HasFolders { get; set; }
|
||||
public bool HasCollections { get; set; }
|
||||
@@ -134,11 +139,6 @@ namespace Bit.App.Pages
|
||||
get => _websiteIconsEnabled;
|
||||
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||
}
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
|
||||
public Command RefreshCommand { get; set; }
|
||||
public Command<CipherView> CipherOptionsCommand { get; set; }
|
||||
@@ -150,7 +150,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
@@ -159,7 +159,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
||||
{
|
||||
SyncRefreshing = true;
|
||||
await _syncService.FullSyncAsync(false);
|
||||
@@ -175,7 +175,8 @@ namespace Bit.App.Pages
|
||||
var groupedItems = new List<GroupingsPageListGroup>();
|
||||
var page = Page as GroupingsPage;
|
||||
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
try
|
||||
{
|
||||
await LoadDataAsync();
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private CipherView _cipher;
|
||||
private int _organizationSelectedIndex;
|
||||
@@ -28,7 +28,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
||||
@@ -67,7 +67,7 @@ namespace Bit.App.Pages
|
||||
var allCollections = await _collectionService.GetAllDecryptedAsync();
|
||||
_writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList();
|
||||
|
||||
var orgs = await _organizationService.GetAllAsync();
|
||||
var orgs = await _userService.GetAllOrganizationAsync();
|
||||
OrganizationOptions = orgs.OrderBy(o => o.Name)
|
||||
.Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed)
|
||||
.Select(o => new KeyValuePair<string, string>(o.Name, o.Id)).ToList();
|
||||
@@ -110,7 +110,7 @@ namespace Bit.App.Pages
|
||||
await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
|
||||
(await _organizationService.GetAsync(OrganizationId)).Name);
|
||||
(await _userService.GetOrganizationAsync(OrganizationId)).Name);
|
||||
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
return true;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ITotpService _totpService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuditService _auditService;
|
||||
@@ -48,7 +48,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||
@@ -248,7 +248,7 @@ namespace Bit.App.Pages
|
||||
return false;
|
||||
}
|
||||
Cipher = await cipher.DecryptAsync();
|
||||
CanAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
CanAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
||||
|
||||
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
||||
|
||||
@@ -251,7 +251,7 @@
|
||||
<comment>Title for the alert when internet connection is required to continue.</comment>
|
||||
</data>
|
||||
<data name="InvalidMasterPassword" xml:space="preserve">
|
||||
<value>Glavna lozinka neispravna. Pokušajte ponovo.</value>
|
||||
<value>Nevažeća glavna lozinka. Pokušajte ponovo.</value>
|
||||
</data>
|
||||
<data name="InvalidPIN" xml:space="preserve">
|
||||
<value>Nevažeći PIN. Pokušajte ponovo.</value>
|
||||
@@ -378,10 +378,10 @@
|
||||
<value>Prikaz</value>
|
||||
</data>
|
||||
<data name="VisitOurWebsite" xml:space="preserve">
|
||||
<value>Posjetite našu Web Stranicu </value>
|
||||
<value>Posjeti naš web</value>
|
||||
</data>
|
||||
<data name="VisitOurWebsiteDescription" xml:space="preserve">
|
||||
<value>Posjeti našu Web stranicu kako bi pronašli pomoć, novosti, poslali nam e-poštu i/ili saznali više o tome kako koristiti Bitwarden.</value>
|
||||
<value>Posjeti naš web kako bi pronašli pomoć, vijesti, poslali nam e-poštu i/ili saznali više o tome kako koristiti Bitwarden.</value>
|
||||
</data>
|
||||
<data name="Website" xml:space="preserve">
|
||||
<value>Web stranica</value>
|
||||
@@ -409,13 +409,13 @@
|
||||
<value>Usluga auto-ispune</value>
|
||||
</data>
|
||||
<data name="AvoidAmbiguousCharacters" xml:space="preserve">
|
||||
<value>Izbjegavaj dvosmislene znakove</value>
|
||||
<value>Izbegavanja nesigurnih slova</value>
|
||||
</data>
|
||||
<data name="BitwardenAppExtension" xml:space="preserve">
|
||||
<value>Dodatak Bitwarden aplikacije</value>
|
||||
</data>
|
||||
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
|
||||
<value>Najlakši način za dodavanje novih prijava u Vaš trezor je iz dodatka Bitwarden aplikacije. Saznajte više o korištenju dodatka Bitwarden aplikacije tako što ćete otići u Postavke aplikacije.</value>
|
||||
<value>Najlakši način za dodavanje novih prijava u Vaš trezor je iz dodatka Bitwarden aplikacije. Saznajte više o korišćenju dodatka Bitwarden aplikacije u „Podešavanja“.</value>
|
||||
</data>
|
||||
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
|
||||
<value>Koristite Bitwarden u Safari-ju i drugim aplikacijama za automatsko popunjavanje Vaših prijava.</value>
|
||||
@@ -430,35 +430,35 @@
|
||||
<value>Promijenite e-poštu</value>
|
||||
</data>
|
||||
<data name="ChangeEmailConfirmation" xml:space="preserve">
|
||||
<value>Možete da promenite svoju adresu e-pošte na bitwarden.com web trezoru. Da li želite da posjetite Web Stranicu sada?</value>
|
||||
<value>Možete da promenite svoju adresu e-pošte na bitwarden.com veb trezoru. Da li želite da posetite veb stranicu sada?</value>
|
||||
</data>
|
||||
<data name="ChangeMasterPassword" xml:space="preserve">
|
||||
<value>Promijenite glavnu lozinku</value>
|
||||
</data>
|
||||
<data name="ChangePasswordConfirmation" xml:space="preserve">
|
||||
<value>Možete da promjenite svoju glavnu lozinku na bitwarden.com web trezoru. Da li želite da posjetite web stranicu sada?</value>
|
||||
<value>Možete da promenite svoju glavnu lozinku na bitwarden.com veb trezoru. Da li želite da posetite veb stranicu sada?</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Zatvori</value>
|
||||
<value>Zatvorite</value>
|
||||
</data>
|
||||
<data name="Continue" xml:space="preserve">
|
||||
<value>Nastavi</value>
|
||||
<value>Nastavite</value>
|
||||
</data>
|
||||
<data name="CreateAccount" xml:space="preserve">
|
||||
<value>Napravi račun</value>
|
||||
<value>Napravite nalog</value>
|
||||
</data>
|
||||
<data name="CreatingAccount" xml:space="preserve">
|
||||
<value>Pravljenje računa...</value>
|
||||
<value>Pravljenje naloga...</value>
|
||||
<comment>Message shown when interacting with the server</comment>
|
||||
</data>
|
||||
<data name="EditItem" xml:space="preserve">
|
||||
<value>Uredi stavku</value>
|
||||
<value>Uredite stavku</value>
|
||||
</data>
|
||||
<data name="EnableAutomaticSyncing" xml:space="preserve">
|
||||
<value>Omogući automatsku sinhronizaciju</value>
|
||||
<value>Omogućite automatsku sinhronizaciju</value>
|
||||
</data>
|
||||
<data name="EnterEmailForHint" xml:space="preserve">
|
||||
<value>Unesite E-Mail adresu Vašeg računa da biste dobili nagovještaj o mogućoj glavnoj lozinki.</value>
|
||||
<value>Unesite adresu e-pošte svog naloga da biste dobili nagoveštaj glavne lozinke.</value>
|
||||
</data>
|
||||
<data name="ExntesionReenable" xml:space="preserve">
|
||||
<value>Ponovo omogućite dodatak aplikacije</value>
|
||||
@@ -470,11 +470,11 @@
|
||||
<value>Omogućite dodatak aplikacije</value>
|
||||
</data>
|
||||
<data name="ExtensionInSafari" xml:space="preserve">
|
||||
<value>U Safari-ju pronađite Bitwarden pomoću ikone za dijeljenje (savjet: desni donji red menija).</value>
|
||||
<value>U Safari-ju pronađite Bitwarden pomoću ikone za deljenje (savet: desni donji red menija).</value>
|
||||
<comment>Safari is the name of apple's web browser</comment>
|
||||
</data>
|
||||
<data name="ExtensionInstantAccess" xml:space="preserve">
|
||||
<value>Dobijte trenutni pristup Vašim lozinkama!</value>
|
||||
<value>Dobijte instant pristup Vašim lozinkama!</value>
|
||||
</data>
|
||||
<data name="ExtensionReady" xml:space="preserve">
|
||||
<value>Spremni ste za prijavljivanje!</value>
|
||||
@@ -507,10 +507,10 @@
|
||||
<value>Uvezite stavke</value>
|
||||
</data>
|
||||
<data name="ImportItemsConfirmation" xml:space="preserve">
|
||||
<value>Možete da uvezete sve vaše lozinke preko bitwarden.com web trezora. Da li želite da posjetite web stranicu sada?</value>
|
||||
<value>Možete da uvezete stavke na veliko na bitwarden.com veb trezoru. Da li želite da posetite veb stranicu sada?</value>
|
||||
</data>
|
||||
<data name="ImportItemsDescription" xml:space="preserve">
|
||||
<value>Uvezite sve lozinke i datoteke iz drugih aplikacija za upravljanje lozinkom.</value>
|
||||
<value>Uvezite stavke na veliko iz drugih aplikacija za upravljanje lozinkom.</value>
|
||||
</data>
|
||||
<data name="LastSync" xml:space="preserve">
|
||||
<value>Poslednja sinhronizacija:</value>
|
||||
@@ -1456,7 +1456,7 @@
|
||||
<value>Otključaj</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Otključaj trezor</value>
|
||||
<value>Unlock Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minuta</value>
|
||||
@@ -1475,7 +1475,7 @@
|
||||
<value>Vaš trezor je zaključan. Potvrdite vaš PIN da nastavite.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Vaš trezor je zaključan. Potvrdite glavnu lozinku da nastavite.</value>
|
||||
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Tamno</value>
|
||||
@@ -1618,13 +1618,13 @@
|
||||
<value>Unesi glavnu lozinku za izvoz podataka iz trezora.</value>
|
||||
</data>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Pošalji verifikacijski kod na E-Mail</value>
|
||||
<value>Send a verification code to your email</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Kod poslan!</value>
|
||||
<value>Code Sent!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Potvrdite lozinku za nastavak.</value>
|
||||
<value>Confirm your identity to continue.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Ovaj izvoz sadrži podatke trezora u nešifriranom obliku! Izvezenu datoteku se ne bi smjelo pohranjivati ili slati putem nesigurnih kanala (npr. e-poštom). Izbrišite je odmah nakon završetka korištenja.</value>
|
||||
@@ -1814,64 +1814,64 @@
|
||||
<value>Koristi preklapanje</value>
|
||||
</data>
|
||||
<data name="DrawOverDescription" xml:space="preserve">
|
||||
<value>Kada je uključeno, omogućuje usluzi Bitwarden prikaz iskočnog okvira prilikom odabira polja za prijavu.</value>
|
||||
<value>When enabled, allows the Bitwarden Accessibility Service to display a popup when login fields are selected.</value>
|
||||
</data>
|
||||
<data name="DrawOverDescription2" xml:space="preserve">
|
||||
<value>Ako je uključena, usluga Bitwarden prikazati će iskočni okvir prilikom odabira polja za prijavu kao pomoć pri auto-ispuni.</value>
|
||||
<value>If enabled, the Bitwarden Accessibility Service will display a popup when login fields are selected to assist with auto-filling your logins.</value>
|
||||
</data>
|
||||
<data name="DrawOverDescription3" xml:space="preserve">
|
||||
<value>Ako je uključeno, pristupačnost će prikazati iskočni okvir kao pomoć usluzi automatskog popunjavanja za starije aplikacije koje ne podržavaju Android strukturu auotmatskog popunjavanja.</value>
|
||||
<value>If enabled, accessibility will show a popup to augment the Autofill Service for older apps that don't support the Android Autofill Framework.</value>
|
||||
</data>
|
||||
<data name="PersonalOwnershipSubmitError" xml:space="preserve">
|
||||
<value>Zbog poslovnih smjernica, zabranjeno vam je pohranjivanje predmeta u svoj lični trezor. Promijenite opciju vlasništva u organizaciji i odaberite neku od dostupnih kolekcija.</value>
|
||||
<value>Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections.</value>
|
||||
</data>
|
||||
<data name="PersonalOwnershipPolicyInEffect" xml:space="preserve">
|
||||
<value>Pravila organizacije utječu na tvoje mogućnosti vlasništva.</value>
|
||||
<value>An organization policy is affecting your ownership options.</value>
|
||||
</data>
|
||||
<data name="Send" xml:space="preserve">
|
||||
<value>Send</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="AllSends" xml:space="preserve">
|
||||
<value>Svi Send-ovi</value>
|
||||
<value>All Sends</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Sends" xml:space="preserve">
|
||||
<value>Send-ovi</value>
|
||||
<value>Sends</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="NameInfo" xml:space="preserve">
|
||||
<value>Nadimak za ovaj Send</value>
|
||||
<value>A friendly name to describe this Send.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="TypeText" xml:space="preserve">
|
||||
<value>Tekst</value>
|
||||
<value>Text</value>
|
||||
</data>
|
||||
<data name="TypeTextInfo" xml:space="preserve">
|
||||
<value>Tekst kojeg želiš poslati.</value>
|
||||
<value>The text you want to send.</value>
|
||||
</data>
|
||||
<data name="HideTextByDefault" xml:space="preserve">
|
||||
<value>Zadano sakrij tekst pri pristupanju Send-u</value>
|
||||
<value>When accessing the Send, hide the text by default</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="TypeFile" xml:space="preserve">
|
||||
<value>Datoteka</value>
|
||||
<value>File</value>
|
||||
</data>
|
||||
<data name="TypeFileInfo" xml:space="preserve">
|
||||
<value>Datoteka koju želiš poslati</value>
|
||||
<value>The file you want to send.</value>
|
||||
</data>
|
||||
<data name="DeletionDate" xml:space="preserve">
|
||||
<value>Datum brisanja</value>
|
||||
<value>Deletion Date</value>
|
||||
</data>
|
||||
<data name="DeletionTime" xml:space="preserve">
|
||||
<value>Vrijeme brisanja</value>
|
||||
<value>Deletion Time</value>
|
||||
</data>
|
||||
<data name="DeletionDateInfo" xml:space="preserve">
|
||||
<value>Ovaj Send će biti trajno izbrisan na odabrani datum i vrijeme.</value>
|
||||
<value>The Send will be permanently deleted on the specified date and time.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="PendingDelete" xml:space="preserve">
|
||||
<value>Brisanje na čekanju </value>
|
||||
<value>Pending deletion</value>
|
||||
</data>
|
||||
<data name="ExpirationDate" xml:space="preserve">
|
||||
<value>Rok upotrebe</value>
|
||||
@@ -1880,115 +1880,115 @@
|
||||
<value>Vrijeme roka upotrebe</value>
|
||||
</data>
|
||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||
<value>Ako je postavljeno, pristup ovom Sendu ističe na navedeni datum i vrijeme.</value>
|
||||
<value>If set, access to this Send will expire on the specified date and time.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Expired" xml:space="preserve">
|
||||
<value>Isteklo</value>
|
||||
<value>Expired</value>
|
||||
</data>
|
||||
<data name="MaximumAccessCount" xml:space="preserve">
|
||||
<value>Maksimalan broj pristupa</value>
|
||||
<value>Maximum Access Count</value>
|
||||
</data>
|
||||
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
||||
<value>Ako je određeno, ovom Send-u će se moći pristupiti samo ograničeni broj puta.</value>
|
||||
<value>If set, users will no longer be able to access this Send once the maximum access count is reached.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="MaximumAccessCountReached" xml:space="preserve">
|
||||
<value>Dostignut najveći broj pristupanja</value>
|
||||
<value>Max access count reached</value>
|
||||
</data>
|
||||
<data name="CurrentAccessCount" xml:space="preserve">
|
||||
<value>Trenutni broj pristupa</value>
|
||||
<value>Current Access Count</value>
|
||||
</data>
|
||||
<data name="NewPassword" xml:space="preserve">
|
||||
<value>Nova lozinka</value>
|
||||
<value>New Password</value>
|
||||
</data>
|
||||
<data name="PasswordInfo" xml:space="preserve">
|
||||
<value>Neobavezno zahtijevaj lozinku od korisnika za pristup ovom Send-u.</value>
|
||||
<value>Optionally require a password for users to access this Send.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="RemovePassword" xml:space="preserve">
|
||||
<value>Ukloni lozinku</value>
|
||||
<value>Remove Password</value>
|
||||
</data>
|
||||
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
||||
<value>Sigurno želiš ukloniti lozinku?</value>
|
||||
<value>Are you sure you want to remove the password?</value>
|
||||
</data>
|
||||
<data name="RemovingSendPassword" xml:space="preserve">
|
||||
<value>Uklanjanje lozinke </value>
|
||||
<value>Removing password</value>
|
||||
</data>
|
||||
<data name="SendPasswordRemoved" xml:space="preserve">
|
||||
<value>Lozinka je uklonjena.</value>
|
||||
<value>Password has been removed.</value>
|
||||
</data>
|
||||
<data name="NotesInfo" xml:space="preserve">
|
||||
<value>Privatne bilješke o ovom Send-u.</value>
|
||||
<value>Private notes about this Send.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="DisableSend" xml:space="preserve">
|
||||
<value>Onemogući ovaj Send da mu niko drugi ne može pristupiti.</value>
|
||||
<value>Disable this Send so that no one can access it.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="NoSends" xml:space="preserve">
|
||||
<value>Nema Send-ova na tvom računu.</value>
|
||||
<value>There are no Sends in your account.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="AddASend" xml:space="preserve">
|
||||
<value>Dodaj Send</value>
|
||||
<value>Add a Send</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="CopyLink" xml:space="preserve">
|
||||
<value>Kopiraj link</value>
|
||||
<value>Copy Link</value>
|
||||
</data>
|
||||
<data name="ShareLink" xml:space="preserve">
|
||||
<value>Podjeli link</value>
|
||||
<value>Share Link</value>
|
||||
</data>
|
||||
<data name="SendLink" xml:space="preserve">
|
||||
<value>Link od Send-a</value>
|
||||
<value>Send link</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="SearchSends" xml:space="preserve">
|
||||
<value>Pretraži Send-ove</value>
|
||||
<value>Search Sends</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="EditSend" xml:space="preserve">
|
||||
<value>Uredi Send</value>
|
||||
<value>Edit Send</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="AddSend" xml:space="preserve">
|
||||
<value>Dodaj Send</value>
|
||||
<value>Add Send</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
||||
<value>Sigurno želiš izbrisati ovaj Send?</value>
|
||||
<value>Are you sure you want to delete this Send?</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="SendDeleted" xml:space="preserve">
|
||||
<value>Send izbrisan.</value>
|
||||
<value>Send has been deleted.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="SendUpdated" xml:space="preserve">
|
||||
<value>Send ažuriran.</value>
|
||||
<value>Send updated.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="NewSendCreated" xml:space="preserve">
|
||||
<value>Novi Send kreiran.</value>
|
||||
<value>New send created.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="OneDay" xml:space="preserve">
|
||||
<value>1 dan</value>
|
||||
<value>1 day</value>
|
||||
</data>
|
||||
<data name="TwoDays" xml:space="preserve">
|
||||
<value>2 dana</value>
|
||||
<value>2 days</value>
|
||||
</data>
|
||||
<data name="ThreeDays" xml:space="preserve">
|
||||
<value>3 dana</value>
|
||||
<value>3 days</value>
|
||||
</data>
|
||||
<data name="SevenDays" xml:space="preserve">
|
||||
<value>7 dana</value>
|
||||
<value>7 days</value>
|
||||
</data>
|
||||
<data name="ThirtyDays" xml:space="preserve">
|
||||
<value>30 dana</value>
|
||||
<value>30 days</value>
|
||||
</data>
|
||||
<data name="Custom" xml:space="preserve">
|
||||
<value>Prilagođeno</value>
|
||||
<value>Custom</value>
|
||||
</data>
|
||||
<data name="ShareOnSave" xml:space="preserve">
|
||||
<value>Podijeli Send nakon spremanja.</value>
|
||||
@@ -2093,27 +2093,27 @@
|
||||
<value>Jedno ili više pravila organizacija onemogućuje izvoz osobnog trezora. </value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Obriši račun</value>
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Brisanje tvog računa je nepovratno</value>
|
||||
<value>Deleting your account is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Tvoj račun i svi povezani podaci biti će nepovratno obrisani. Sigurno želiš nastaviti?</value>
|
||||
<value>Your account and all associated data will be erased and unrecoverable. Are you sure you want to continue?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Brisanje vašeg računa</value>
|
||||
<value>Deleting your account</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Vaš račun je trajno obrisan.</value>
|
||||
<value>Your account has been permanently deleted</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Neispravan verifikacijski kod</value>
|
||||
</data>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Pošalji kod</value>
|
||||
<value>Send Code</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Slanje</value>
|
||||
<value>Sending</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1060,7 +1060,7 @@
|
||||
<value>Cognoms</value>
|
||||
</data>
|
||||
<data name="FullName" xml:space="preserve">
|
||||
<value>Nom complet</value>
|
||||
<value>Full Name</value>
|
||||
</data>
|
||||
<data name="LicenseNumber" xml:space="preserve">
|
||||
<value>Número de llicència</value>
|
||||
@@ -1187,7 +1187,7 @@
|
||||
<value>Amagat</value>
|
||||
</data>
|
||||
<data name="FieldTypeLinked" xml:space="preserve">
|
||||
<value>Enllaçat</value>
|
||||
<value>Linked</value>
|
||||
</data>
|
||||
<data name="FieldTypeText" xml:space="preserve">
|
||||
<value>Text</value>
|
||||
@@ -1456,7 +1456,7 @@
|
||||
<value>Desbloqueja</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Desbloqueja la caixa forta</value>
|
||||
<value>Unlock Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minuts</value>
|
||||
@@ -1475,7 +1475,7 @@
|
||||
<value>La caixa forta està bloquejada. Verifiqueu El codi PIN per continuar.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>La caixa forta està bloquejada. Comproveu la vostra identitat per continuar.</value>
|
||||
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Fosc</value>
|
||||
@@ -1618,13 +1618,13 @@
|
||||
<value>Introduïu la contrasenya mestra per exportar les dades de la caixa forta.</value>
|
||||
</data>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Envia un codi de verificació al correu electrònic</value>
|
||||
<value>Send a verification code to your email</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Codi enviat!</value>
|
||||
<value>Code Sent!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirmeu la vostra identitat per continuar.</value>
|
||||
<value>Confirm your identity to continue.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Aquesta exportació conté les dades de la vostra caixa forta en un format no xifrat. No hauríeu d'emmagatzemar o enviar el fitxer exportat a través de canals no segurs (com ara el correu electrònic). Elimineu-lo immediatament després d'haver acabat d'usar-lo.</value>
|
||||
@@ -2048,19 +2048,19 @@
|
||||
<value>Actualment no es pot actualitzar la contrasenya</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||
<value>Suprimiu la contrasenya mestra</value>
|
||||
<value>Remove Master Password</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||
<value>{0} utilitza SSO amb el xifratge gestionat pel client. Continuar suprimirà la vostra contrasenya mestra del vostre compte i requerirà SSO per iniciar la sessió.</value>
|
||||
<value>{0} is using SSO with customer-managed encryption. Continuing will remove your Master Password from your account and require SSO to login.</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||
<value>Si no voleu suprimir la vostra contrasenya mestra, podeu abandonar aquesta organització.</value>
|
||||
<value>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||
</data>
|
||||
<data name="LeaveOrganization" xml:space="preserve">
|
||||
<value>Abandona l'organització</value>
|
||||
<value>Leave Organization</value>
|
||||
</data>
|
||||
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||
<value>Abandona {0}</value>
|
||||
<value>Leave {0}?</value>
|
||||
</data>
|
||||
<data name="Fido2Title" xml:space="preserve">
|
||||
<value>FIDO2 WebAuthn</value>
|
||||
@@ -2093,27 +2093,27 @@
|
||||
<value>Una o més polítiques d'organització us impedeixen exportar la vostra caixa forta.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Suprimeix el compte</value>
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>La supressió del vostre compte és permanent</value>
|
||||
<value>Deleting your account is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>El vostre compte i totes les dades associades seran suprimides i irrecuperables. Esteu segur que voleu continuar?</value>
|
||||
<value>Your account and all associated data will be erased and unrecoverable. Are you sure you want to continue?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>S'està suprimint el vostre compte</value>
|
||||
<value>Deleting your account</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>El vostre compte ha sigut suprimit de forma permanent.</value>
|
||||
<value>Your account has been permanently deleted</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Codi de verificació no vàlid</value>
|
||||
<value>Invalid Verification Code.</value>
|
||||
</data>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Envia codi</value>
|
||||
<value>Send Code</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>S'està enviant...</value>
|
||||
<value>Sending</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1410,7 +1410,7 @@
|
||||
<value>Aucune organisation à afficher.</value>
|
||||
</data>
|
||||
<data name="MoveToOrgDesc" xml:space="preserve">
|
||||
<value>Choisissez une organisation vers laquelle vous souhaitez déplacer cet élément. Déplacer un élément vers une organisation transfère la propriété de l'élément à cette organisation. Vous ne serez plus le propriétaire direct de cet élément une fois qu'il aura été déplacé.</value>
|
||||
<value>Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved.</value>
|
||||
</data>
|
||||
<data name="NumberOfWords" xml:space="preserve">
|
||||
<value>Nombre de mots</value>
|
||||
@@ -1456,7 +1456,7 @@
|
||||
<value>Déverrouiller</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Déverrouiller le coffre</value>
|
||||
<value>Unlock Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minutes</value>
|
||||
@@ -1475,7 +1475,7 @@
|
||||
<value>Votre coffre est verrouillé. Saisissez votre code PIN pour continuer.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Votre coffre est verrouillé. Vérifiez votre identité pour continuer.</value>
|
||||
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Sombre</value>
|
||||
@@ -1618,13 +1618,13 @@
|
||||
<value>Saisissez votre mot de passe maître pour exporter les données de votre coffre.</value>
|
||||
</data>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Envoyer un code de vérification à votre adresse email</value>
|
||||
<value>Send a verification code to your email</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Code Envoyé !</value>
|
||||
<value>Code Sent!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirmez votre identité pour continuer.</value>
|
||||
<value>Confirm your identity to continue.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Cet export contient les données de votre coffre dans un format non chiffré. Vous ne devriez ni le stocker ni l'envoyer via des canaux non sécurisés (tel que par e-mail). Supprimez-le immédiatement après l'avoir utilisé.</value>
|
||||
@@ -2039,7 +2039,7 @@
|
||||
<value>Mettre à jour le mot de passe principal</value>
|
||||
</data>
|
||||
<data name="UpdateMasterPasswordWarning" xml:space="preserve">
|
||||
<value>Votre mot de passe maître a récemment été modifié par un administrateur de votre organisation. Pour accéder au coffre, vous devez mettre à jour votre mot de passe maître dès maintenant. Cette action va vous déconnecter et vous obligera à vous reconnecter. Les sessions actives sur d'autres appareils peuvent rester actives jusqu'à une heure.</value>
|
||||
<value>Your Master Password was recently changed by an administrator in your organization. In order to access the vault, you must update your Master Password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour.</value>
|
||||
</data>
|
||||
<data name="UpdatingPassword" xml:space="preserve">
|
||||
<value>Mise à jour du mot de passe</value>
|
||||
@@ -2054,7 +2054,7 @@
|
||||
<value>{0} is using SSO with customer-managed encryption. Continuing will remove your Master Password from your account and require SSO to login.</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||
<value>Si vous ne souhaitez pas supprimer votre mot de passe maître actuel, vous pouvez quitter cette organisation. </value>
|
||||
<value>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||
</data>
|
||||
<data name="LeaveOrganization" xml:space="preserve">
|
||||
<value>Quitter l’organisation</value>
|
||||
@@ -2063,16 +2063,16 @@
|
||||
<value>Quitter {0} ?</value>
|
||||
</data>
|
||||
<data name="Fido2Title" xml:space="preserve">
|
||||
<value>WebAuthn FIDO2</value>
|
||||
<value>FIDO2 WebAuthn</value>
|
||||
</data>
|
||||
<data name="Fido2Instruction" xml:space="preserve">
|
||||
<value>Pour continuer, préparez votre clé de sécurité compatible FIDO2 WebAuthn, puis suivez les instructions après avoir cliqué sur "Authentifier WebAuthn" à l'écran suivant.</value>
|
||||
<value>To continue, have your FIDO2 WebAuthn enabled security key ready, then follow the instructions after clicking 'Authenticate WebAuthn' on the next screen.</value>
|
||||
</data>
|
||||
<data name="Fido2Desc" xml:space="preserve">
|
||||
<value>Authentication using FIDO2 WebAuthn, you can authenticate using an external security key.</value>
|
||||
</data>
|
||||
<data name="Fido2AuthenticateWebAuthn" xml:space="preserve">
|
||||
<value>Authentifier WebAuthn</value>
|
||||
<value>Authenticate WebAuthn</value>
|
||||
</data>
|
||||
<data name="Fido2ReturnToApp" xml:space="preserve">
|
||||
<value>Retourner à l’application</value>
|
||||
@@ -2084,13 +2084,13 @@
|
||||
<value>This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password.</value>
|
||||
</data>
|
||||
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
|
||||
<value>Les politiques de votre organisation affectent le délai d'expiration de votre coffre-fort. Le délai d'expiration maximal autorisé est de {0} heure(s) et {1} minute(s)</value>
|
||||
<value>Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is {0} hour(s) and {1} minute(s)</value>
|
||||
</data>
|
||||
<data name="VaultTimeoutToLarge" xml:space="preserve">
|
||||
<value>Le délai d'expiration de votre coffre-fort dépasse les restrictions définies par votre organisation.</value>
|
||||
<value>Your vault timeout exceeds the restrictions set by your organization.</value>
|
||||
</data>
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Une ou plusieurs politiques d'organisation vous empêchent d'exporter votre coffre-fort personnel.</value>
|
||||
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Supprimer le compte</value>
|
||||
@@ -2111,9 +2111,9 @@
|
||||
<value>Code de vérification invalide.</value>
|
||||
</data>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Envoyer le code</value>
|
||||
<value>Send Code</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Envoi en cours</value>
|
||||
<value>Sending</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -13,10 +13,15 @@ namespace Bit.App.Services
|
||||
|
||||
private readonly HashSet<string> _preferenceStorageKeys = new HashSet<string>
|
||||
{
|
||||
Constants.AppIdKey,
|
||||
Constants.PreAuthEnvironmentUrlsKey,
|
||||
Constants.AutofillTileAdded,
|
||||
Constants.AddSitePromptShownKey,
|
||||
Constants.VaultTimeoutKey,
|
||||
Constants.VaultTimeoutActionKey,
|
||||
Constants.ThemeKey,
|
||||
Constants.DefaultUriMatch,
|
||||
Constants.DisableAutoTotpCopyKey,
|
||||
Constants.DisableFaviconKey,
|
||||
Constants.ClearClipboardKey,
|
||||
Constants.AutofillDisableSavePromptKey,
|
||||
Constants.LastActiveTimeKey,
|
||||
Constants.PushInitialPromptShownKey,
|
||||
Constants.LastFileCacheClearKey,
|
||||
Constants.PushLastRegistrationDateKey,
|
||||
@@ -32,17 +37,14 @@ namespace Bit.App.Services
|
||||
Constants.iOSAutoFillBiometricIntegrityKey,
|
||||
Constants.iOSExtensionClearCiphersCacheKey,
|
||||
Constants.iOSExtensionBiometricIntegrityKey,
|
||||
Constants.RememberEmailKey,
|
||||
Constants.RememberedEmailKey,
|
||||
Constants.RememberOrgIdentifierKey,
|
||||
Constants.RememberedOrgIdentifierKey,
|
||||
Constants.AppExtensionStartedKey,
|
||||
Constants.AppExtensionActivatedKey,
|
||||
Constants.EnvironmentUrlsKey,
|
||||
Constants.InlineAutofillEnabledKey,
|
||||
Constants.InvalidUnlockAttempts,
|
||||
};
|
||||
|
||||
private readonly HashSet<string> _migrateToPreferences = new HashSet<string>
|
||||
{
|
||||
"environmentUrls",
|
||||
Constants.EnvironmentUrlsKey,
|
||||
};
|
||||
private readonly HashSet<string> _haveMigratedToPreferences = new HashSet<string>();
|
||||
|
||||
|
||||
164
src/App/Services/NavigationService.cs
Normal file
164
src/App/Services/NavigationService.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Pages;
|
||||
|
||||
namespace Bit.App.Services
|
||||
{
|
||||
public class NavigationService : INavigationService
|
||||
{
|
||||
private readonly IMainPage _presentationRoot;
|
||||
private readonly IViewLocator _viewLocator;
|
||||
|
||||
public NavigationService(IMainPage presentationRoot, IViewLocator viewLocator)
|
||||
{
|
||||
_presentationRoot = presentationRoot;
|
||||
_viewLocator = viewLocator;
|
||||
}
|
||||
|
||||
private Xamarin.Forms.INavigation Navigator => _presentationRoot.MainPage.Navigation;
|
||||
|
||||
public void PresentAsMainPage(BaseViewModel viewModel)
|
||||
{
|
||||
var page = _viewLocator.CreateAndBindPageFor(viewModel);
|
||||
|
||||
IEnumerable<BaseViewModel> viewModelsToDismiss = FindViewModelsToDismiss(_presentationRoot.MainPage);
|
||||
|
||||
if (_presentationRoot.MainPage is NavigationPage navPage)
|
||||
{
|
||||
// If we're replacing a navigation page, unsub from events
|
||||
navPage.PopRequested -= NavPagePopRequested;
|
||||
}
|
||||
|
||||
// viewModel.BeforeFirstShown();
|
||||
|
||||
_presentationRoot.MainPage = page;
|
||||
|
||||
foreach (BaseViewModel toDismiss in viewModelsToDismiss)
|
||||
{
|
||||
// toDismiss.AfterDismissed();
|
||||
}
|
||||
}
|
||||
|
||||
public void PresentAsNavigatableMainPage(BaseViewModel viewModel)
|
||||
{
|
||||
var page = _viewLocator.CreateAndBindPageFor(viewModel);
|
||||
|
||||
NavigationPage newNavigationPage = new NavigationPage(page);
|
||||
|
||||
IEnumerable<BaseViewModel> viewModelsToDismiss = FindViewModelsToDismiss(_presentationRoot.MainPage);
|
||||
|
||||
if (_presentationRoot.MainPage is NavigationPage navPage)
|
||||
{
|
||||
navPage.PopRequested -= NavPagePopRequested;
|
||||
}
|
||||
|
||||
// viewModel.BeforeFirstShown();
|
||||
|
||||
// Listen for back button presses on the new navigation bar
|
||||
newNavigationPage.PopRequested += NavPagePopRequested;
|
||||
_presentationRoot.MainPage = newNavigationPage;
|
||||
|
||||
foreach (BaseViewModel toDismiss in viewModelsToDismiss)
|
||||
{
|
||||
// toDismiss.AfterDismissed();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<BaseViewModel> FindViewModelsToDismiss(Page dismissingPage)
|
||||
{
|
||||
var viewmodels = new List<BaseViewModel>();
|
||||
|
||||
if (dismissingPage is NavigationPage)
|
||||
{
|
||||
viewmodels.AddRange(
|
||||
Navigator
|
||||
.NavigationStack
|
||||
.Select(p => p.BindingContext)
|
||||
.OfType<BaseViewModel>()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var viewmodel = dismissingPage?.BindingContext as BaseViewModel;
|
||||
if (viewmodel != null) viewmodels.Add(viewmodel);
|
||||
}
|
||||
|
||||
return viewmodels;
|
||||
}
|
||||
|
||||
private void NavPagePopRequested(object sender, NavigationRequestedEventArgs e)
|
||||
{
|
||||
if (Navigator.NavigationStack.LastOrDefault()?.BindingContext is BaseViewModel poppingPage)
|
||||
{
|
||||
// poppingPage.AfterDismissed();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task NavigateTo(BaseViewModel viewModel)
|
||||
{
|
||||
var page = _viewLocator.CreateAndBindPageFor(viewModel);
|
||||
|
||||
// await viewModel.BeforeFirstShown();
|
||||
|
||||
await Navigator.PushAsync(page);
|
||||
}
|
||||
|
||||
public async Task NavigateBack()
|
||||
{
|
||||
var dismissing = Navigator.NavigationStack.Last().BindingContext as BaseViewModel;
|
||||
|
||||
await Navigator.PopAsync();
|
||||
|
||||
// dismissing?.AfterDismissed();
|
||||
}
|
||||
|
||||
public async Task NavigateBackToRoot()
|
||||
{
|
||||
var toDismiss = Navigator
|
||||
.NavigationStack
|
||||
.Skip(1)
|
||||
.Select(vw => vw.BindingContext)
|
||||
.OfType<BaseViewModel>()
|
||||
.ToArray();
|
||||
|
||||
await Navigator.PopToRootAsync();
|
||||
|
||||
foreach (BaseViewModel viewModel in toDismiss)
|
||||
{
|
||||
// viewModel.AfterDismissed().FireAndForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ViewLocator : IViewLocator
|
||||
{
|
||||
public Page CreateAndBindPageFor<TViewModel>(TViewModel viewModel) where TViewModel : BaseViewModel
|
||||
{
|
||||
var pageType = FindPageForViewModel(viewModel.GetType());
|
||||
|
||||
var page = (Page)Activator.CreateInstance(pageType);
|
||||
|
||||
page.BindingContext = viewModel;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
protected virtual Type FindPageForViewModel(Type viewModelType)
|
||||
{
|
||||
var pageTypeName = viewModelType
|
||||
.AssemblyQualifiedName
|
||||
.Replace("ViewModel", "");
|
||||
|
||||
var pageType = Type.GetType(pageTypeName);
|
||||
if (pageType == null)
|
||||
throw new ArgumentException(pageTypeName + " type does not exist");
|
||||
|
||||
return pageType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ namespace Bit.App.Services
|
||||
{
|
||||
public class NoopPushNotificationService : IPushNotificationService
|
||||
{
|
||||
public bool IsRegisteredForPush => false;
|
||||
|
||||
public Task<string> GetTokenAsync()
|
||||
{
|
||||
return Task.FromResult(null as string);
|
||||
|
||||
@@ -19,8 +19,9 @@ namespace Bit.App.Services
|
||||
{
|
||||
private bool _showNotification;
|
||||
private bool _resolved;
|
||||
private IStorageService _storageService;
|
||||
private ISyncService _syncService;
|
||||
private IStateService _stateService;
|
||||
private IUserService _userService;
|
||||
private IAppIdService _appIdService;
|
||||
private IApiService _apiService;
|
||||
private IMessagingService _messagingService;
|
||||
@@ -57,8 +58,8 @@ namespace Bit.App.Services
|
||||
return;
|
||||
}
|
||||
|
||||
var myUserId = await _stateService.GetActiveUserIdAsync();
|
||||
var isAuthenticated = await _stateService.IsAuthenticatedAsync();
|
||||
var myUserId = await _userService.GetUserIdAsync();
|
||||
var isAuthenticated = await _userService.IsAuthenticatedAsync();
|
||||
switch (notification.Type)
|
||||
{
|
||||
case NotificationType.SyncCipherUpdate:
|
||||
@@ -128,7 +129,7 @@ namespace Bit.App.Services
|
||||
{
|
||||
Resolve();
|
||||
Debug.WriteLine(string.Format("Push Notification - Device Registered - Token : {0}", token));
|
||||
var isAuthenticated = await _stateService.IsAuthenticatedAsync();
|
||||
var isAuthenticated = await _userService.IsAuthenticatedAsync();
|
||||
if (!isAuthenticated)
|
||||
{
|
||||
return;
|
||||
@@ -140,10 +141,10 @@ namespace Bit.App.Services
|
||||
await _apiService.PutDeviceTokenAsync(appId,
|
||||
new Core.Models.Request.DeviceTokenRequest { PushToken = token });
|
||||
Debug.WriteLine("Registered device with server.");
|
||||
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
|
||||
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
|
||||
if (deviceType == Device.Android)
|
||||
{
|
||||
await _stateService.SetPushCurrentTokenAsync(token);
|
||||
await _storageService.SaveAsync(Constants.PushCurrentTokenKey, token);
|
||||
}
|
||||
}
|
||||
catch (ApiException)
|
||||
@@ -173,8 +174,9 @@ namespace Bit.App.Services
|
||||
{
|
||||
return;
|
||||
}
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -13,7 +14,6 @@ using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Newtonsoft.Json;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
@@ -46,8 +46,8 @@ namespace Bit.App.Utilities
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(cipher.Login.Totp))
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var canAccessPremium = await stateService.CanAccessPremiumAsync();
|
||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
||||
if (canAccessPremium || cipher.OrganizationUseTotp)
|
||||
{
|
||||
options.Add(AppResources.CopyTotp);
|
||||
@@ -330,15 +330,33 @@ namespace Bit.App.Utilities
|
||||
}
|
||||
|
||||
public static async Task<bool> PerformUpdateTasksAsync(ISyncService syncService,
|
||||
IDeviceActionService deviceActionService, IStateService stateService)
|
||||
IDeviceActionService deviceActionService, IStorageService storageService)
|
||||
{
|
||||
var currentBuild = deviceActionService.GetBuildNumber();
|
||||
var lastBuild = await stateService.GetLastBuildAsync();
|
||||
if (lastBuild == null || lastBuild != currentBuild)
|
||||
var lastBuild = await storageService.GetAsync<string>(Constants.LastBuildKey);
|
||||
if (lastBuild == null)
|
||||
{
|
||||
// Installed
|
||||
var currentTimeout = await storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
if (currentTimeout == null)
|
||||
{
|
||||
await storageService.SaveAsync(Constants.VaultTimeoutKey, 15);
|
||||
}
|
||||
|
||||
var currentAction = await storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
|
||||
if (currentAction == null)
|
||||
{
|
||||
await storageService.SaveAsync(Constants.VaultTimeoutActionKey, "lock");
|
||||
}
|
||||
}
|
||||
else if (lastBuild != currentBuild)
|
||||
{
|
||||
// Updated
|
||||
var tasks = Task.Run(() => syncService.FullSyncAsync(true));
|
||||
await stateService.SetLastBuildAsync(currentBuild);
|
||||
}
|
||||
if (lastBuild != currentBuild)
|
||||
{
|
||||
await storageService.SaveAsync(Constants.LastBuildKey, currentBuild);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -400,34 +418,35 @@ namespace Bit.App.Utilities
|
||||
|
||||
public static async Task<PreviousPageInfo> ClearPreviousPage()
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var previousPage = await stateService.GetPreviousPageInfoAsync();
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
var previousPage = await storageService.GetAsync<PreviousPageInfo>(Constants.PreviousPageKey);
|
||||
if (previousPage != null)
|
||||
{
|
||||
await stateService.SetPreviousPageInfoAsync(null);
|
||||
await storageService.RemoveAsync(Constants.PreviousPageKey);
|
||||
}
|
||||
return previousPage;
|
||||
}
|
||||
|
||||
public static async Task<int> IncrementInvalidUnlockAttemptsAsync()
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var invalidUnlockAttempts = await stateService.GetInvalidUnlockAttemptsAsync();
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
var invalidUnlockAttempts = await storageService.GetAsync<int>(Constants.InvalidUnlockAttempts);
|
||||
invalidUnlockAttempts++;
|
||||
await stateService.SetInvalidUnlockAttemptsAsync(invalidUnlockAttempts);
|
||||
await storageService.SaveAsync(Constants.InvalidUnlockAttempts, invalidUnlockAttempts);
|
||||
return invalidUnlockAttempts;
|
||||
}
|
||||
|
||||
public static async Task ResetInvalidUnlockAttemptsAsync()
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
await stateService.SetInvalidUnlockAttemptsAsync(null);
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
await storageService.RemoveAsync(Constants.InvalidUnlockAttempts);
|
||||
}
|
||||
|
||||
public static async Task<bool> IsVaultTimeoutImmediateAsync()
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var vaultTimeoutMinutes = await stateService.GetVaultTimeoutAsync();
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
|
||||
var vaultTimeoutMinutes = await storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
if (vaultTimeoutMinutes.GetValueOrDefault(-1) == 0)
|
||||
{
|
||||
return true;
|
||||
@@ -447,8 +466,9 @@ namespace Bit.App.Utilities
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(multiByteEscaped));
|
||||
}
|
||||
|
||||
public static async Task LogOutAsync(string userId, bool userInitiated = false)
|
||||
public static async Task LogOutAsync()
|
||||
{
|
||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
var syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
var tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
|
||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
@@ -463,61 +483,21 @@ namespace Bit.App.Utilities
|
||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
var searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
||||
|
||||
if (userId == null)
|
||||
{
|
||||
userId = await stateService.GetActiveUserIdAsync();
|
||||
}
|
||||
|
||||
var userId = await userService.GetUserIdAsync();
|
||||
await Task.WhenAll(
|
||||
syncService.SetLastSyncAsync(DateTime.MinValue),
|
||||
tokenService.ClearTokenAsync(),
|
||||
cryptoService.ClearKeysAsync(),
|
||||
userService.ClearAsync(),
|
||||
settingsService.ClearAsync(userId),
|
||||
cipherService.ClearAsync(userId),
|
||||
folderService.ClearAsync(userId),
|
||||
collectionService.ClearAsync(userId),
|
||||
passwordGenerationService.ClearAsync(userId),
|
||||
passwordGenerationService.ClearAsync(),
|
||||
vaultTimeoutService.ClearAsync(),
|
||||
stateService.PurgeAsync(),
|
||||
deviceActionService.ClearCacheAsync());
|
||||
if (userInitiated)
|
||||
{
|
||||
await Task.WhenAll(
|
||||
syncService.SetLastSyncAsync(DateTime.MinValue),
|
||||
tokenService.ClearTokenAsync(userId),
|
||||
cryptoService.ClearKeysAsync(userId),
|
||||
settingsService.ClearAsync(userId),
|
||||
vaultTimeoutService.ClearAsync(userId),
|
||||
stateService.ClearAsync(userId));
|
||||
if (await stateService.GetActiveUserIdAsync() != null)
|
||||
{
|
||||
var messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
messagingService.Send("switchedAccount");
|
||||
}
|
||||
}
|
||||
stateService.BiometricLocked = true;
|
||||
searchService.ClearIndex();
|
||||
}
|
||||
|
||||
public static async Task OnAccountSwitchAsync()
|
||||
{
|
||||
var environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
var tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
|
||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
var settingsService = ServiceContainer.Resolve<ISettingsService>("settingsService");
|
||||
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
var folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||
var collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||
var passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||
"passwordGenerationService");
|
||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
var searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
||||
|
||||
await environmentService.SetUrlsFromStorageAsync();
|
||||
|
||||
await Task.WhenAll(
|
||||
cipherService.ClearCacheAsync(),
|
||||
deviceActionService.ClearCacheAsync());
|
||||
tokenService.ClearCache();
|
||||
cryptoService.ClearCache();
|
||||
settingsService.ClearCache();
|
||||
folderService.ClearCache();
|
||||
collectionService.ClearCache();
|
||||
passwordGenerationService.ClearCache();
|
||||
vaultTimeoutService.BiometricLocked = true;
|
||||
searchService.ClearIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Services;
|
||||
using Bit.App.Styles;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core;
|
||||
using Xamarin.Forms;
|
||||
#if !FDROID
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
@@ -93,15 +93,16 @@ namespace Bit.App.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetTheme(ResourceDictionary resources)
|
||||
public static void SetTheme(bool android, ResourceDictionary resources)
|
||||
{
|
||||
SetThemeStyle(GetTheme(), resources);
|
||||
SetThemeStyle(GetTheme(android), resources);
|
||||
}
|
||||
|
||||
public static string GetTheme()
|
||||
public static string GetTheme(bool android)
|
||||
{
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
return stateService.GetThemeAsync().GetAwaiter().GetResult();
|
||||
return Xamarin.Essentials.Preferences.Get(
|
||||
string.Format(PreferencesStorageService.KeyFormat, Constants.ThemeKey), default(string),
|
||||
!android ? "group.com.8bit.bitwarden" : default(string));
|
||||
}
|
||||
|
||||
public static bool OsDarkModeEnabled()
|
||||
|
||||
@@ -5,5 +5,6 @@ namespace Bit.Core.Abstractions
|
||||
public interface IAppIdService
|
||||
{
|
||||
Task<string> GetAppIdAsync();
|
||||
Task<string> GetAnonymousAppIdAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,4 @@ namespace Bit.Core.Abstractions
|
||||
Task UpsertAsync(CollectionData collection);
|
||||
Task UpsertAsync(List<CollectionData> collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,13 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface ICryptoService
|
||||
{
|
||||
Task ClearEncKeyAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearKeyAsync(string userId = null);
|
||||
Task ClearKeyHashAsync(string userId = null);
|
||||
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearKeysAsync(string userId = null);
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearPinProtectedKeyAsync(string userId = null);
|
||||
void ClearCache();
|
||||
Task ClearEncKeyAsync(bool memoryOnly = false);
|
||||
Task ClearKeyAsync();
|
||||
Task ClearKeyHashAsync();
|
||||
Task ClearKeyPairAsync(bool memoryOnly = false);
|
||||
Task ClearKeysAsync();
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false);
|
||||
Task ClearPinProtectedKeyAsync();
|
||||
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
|
||||
Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null);
|
||||
Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null);
|
||||
@@ -25,7 +24,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
|
||||
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
||||
Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
|
||||
Task<SymmetricCryptoKey> GetKeyAsync();
|
||||
Task<string> GetKeyHashAsync();
|
||||
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
|
||||
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
|
||||
@@ -34,7 +33,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, SymmetricCryptoKey key);
|
||||
Task<bool> HasEncKeyAsync();
|
||||
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||
Task<bool> HasKeyAsync(string userId = null);
|
||||
Task<bool> HasKeyAsync();
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
|
||||
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations);
|
||||
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations,
|
||||
|
||||
@@ -9,4 +9,4 @@ namespace Bit.Core.Abstractions
|
||||
Task CollectAsync(EventType eventType, string cipherId = null, bool uploadImmediately = false);
|
||||
Task UploadEventsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,4 +23,4 @@ namespace Bit.Core.Abstractions
|
||||
Task UpsertAsync(FolderData folder);
|
||||
Task UpsertAsync(List<FolderData> folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IOrganizationService
|
||||
{
|
||||
Task<Organization> GetAsync(string id);
|
||||
Task<Organization> GetByIdentifierAsync(string identifier);
|
||||
Task<List<Organization>> GetAllAsync(string userId = null);
|
||||
Task ReplaceAsync(Dictionary<string, OrganizationData> organizations);
|
||||
Task ClearAllAsync(string userId);
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,7 @@ namespace Bit.Core.Abstractions
|
||||
public interface IPasswordGenerationService
|
||||
{
|
||||
Task AddHistoryAsync(string password, CancellationToken token = default(CancellationToken));
|
||||
Task ClearAsync(string userId = null);
|
||||
void ClearCache();
|
||||
Task ClearAsync();
|
||||
Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options);
|
||||
Task<string> GeneratePasswordAsync(PasswordGenerationOptions options);
|
||||
Task<List<GeneratedPasswordHistory>> GetHistoryAsync();
|
||||
|
||||
@@ -10,4 +10,4 @@ namespace Bit.Core.Abstractions
|
||||
Task<List<List<string>>> GetEquivalentDomainsAsync();
|
||||
Task SetEquivalentDomainsAsync(List<List<string>> equivalentDomains);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IStateService
|
||||
{
|
||||
bool BiometricLocked { get; set; }
|
||||
ExtendedObservableCollection<AccountView> AccountViews { get; set; }
|
||||
Task<List<string>> GetUserIdsAsync();
|
||||
Task<string> GetActiveUserIdAsync();
|
||||
Task SetActiveUserAsync(string userId);
|
||||
Task<bool> IsAuthenticatedAsync(string userId = null);
|
||||
Task<bool> HasMultipleAccountsAsync();
|
||||
Task RefreshAccountViewsAsync(bool allowAddAccountRow);
|
||||
Task AddAccountAsync(Account account);
|
||||
Task ClearAsync(string userId);
|
||||
Task<EnvironmentUrlData> GetPreAuthEnvironmentUrlsAsync();
|
||||
Task SetPreAuthEnvironmentUrlsAsync(EnvironmentUrlData value);
|
||||
Task<EnvironmentUrlData> GetEnvironmentUrlsAsync(string userId = null);
|
||||
Task<bool?> GetBiometricUnlockAsync(string userId = null);
|
||||
Task SetBiometricUnlockAsync(bool? value, string userId = null);
|
||||
Task<bool> CanAccessPremiumAsync(string userId = null);
|
||||
Task<string> GetProtectedPinAsync(string userId = null);
|
||||
Task SetProtectedPinAsync(string value, string userId = null);
|
||||
Task<string> GetPinProtectedAsync(string userId = null);
|
||||
Task SetPinProtectedAsync(string value, string userId = null);
|
||||
Task<EncString> GetPinProtectedCachedAsync(string userId = null);
|
||||
Task SetPinProtectedCachedAsync(EncString value, string userId = null);
|
||||
Task<KdfType?> GetKdfTypeAsync(string userId = null);
|
||||
Task SetKdfTypeAsync(KdfType? value, string userId = null);
|
||||
Task<int?> GetKdfIterationsAsync(string userId = null);
|
||||
Task SetKdfIterationsAsync(int? value, string userId = null);
|
||||
Task<string> GetKeyEncryptedAsync(string userId = null);
|
||||
Task SetKeyEncryptedAsync(string value, string userId = null);
|
||||
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
||||
Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null);
|
||||
Task<string> GetKeyHashAsync(string userId = null);
|
||||
Task SetKeyHashAsync(string value, string userId = null);
|
||||
Task<string> GetEncKeyEncryptedAsync(string userId = null);
|
||||
Task SetEncKeyEncryptedAsync(string value, string userId = null);
|
||||
Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null);
|
||||
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
|
||||
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
|
||||
Task SetPrivateKeyEncryptedAsync(string value, string userId = null);
|
||||
Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null);
|
||||
Task SetAutofillBlacklistedUrisAsync(List<string> value, string userId = null);
|
||||
Task<bool?> GetAutofillTileAddedAsync(string userId = null);
|
||||
Task SetAutofillTileAddedAsync(bool? value, string userId = null);
|
||||
Task<string> GetEmailAsync(string userId = null);
|
||||
// Task SetEmailAsync(string value, string userId = null);
|
||||
Task<long?> GetLastActiveTimeAsync(string userId = null);
|
||||
Task SetLastActiveTimeAsync(long? value, string userId = null);
|
||||
Task<int?> GetVaultTimeoutAsync(string userId = null);
|
||||
Task SetVaultTimeoutAsync(int? value, string userId = null);
|
||||
Task<string> GetVaultTimeoutActionAsync(string userId = null);
|
||||
Task SetVaultTimeoutActionAsync(string value, string userId = null);
|
||||
Task<DateTime?> GetLastFileCacheClearAsync(string userId = null);
|
||||
Task SetLastFileCacheClearAsync(DateTime? value, string userId = null);
|
||||
Task<PreviousPageInfo> GetPreviousPageInfoAsync(string userId = null);
|
||||
Task SetPreviousPageInfoAsync(PreviousPageInfo value, string userId = null);
|
||||
Task<int> GetInvalidUnlockAttemptsAsync(string userId = null);
|
||||
Task SetInvalidUnlockAttemptsAsync(int? value, string userId = null);
|
||||
Task<string> GetLastBuildAsync(string userId = null);
|
||||
Task SetLastBuildAsync(string value, string userId = null);
|
||||
Task<bool?> GetDisableFaviconAsync(string userId = null);
|
||||
Task SetDisableFaviconAsync(bool? value, string userId = null);
|
||||
Task<bool?> GetDisableAutoTotpCopyAsync(string userId = null);
|
||||
Task SetDisableAutoTotpCopyAsync(bool? value, string userId = null);
|
||||
Task<bool?> GetInlineAutofillEnabledAsync(string userId = null);
|
||||
Task SetInlineAutofillEnabledAsync(bool? value, string userId = null);
|
||||
Task<bool?> GetAutofillDisableSavePromptAsync(string userId = null);
|
||||
Task SetAutofillDisableSavePromptAsync(bool? value, string userId = null);
|
||||
Task<Dictionary<string, Dictionary<string, object>>> GetLocalDataAsync(string userId = null);
|
||||
Task SetLocalDataAsync(Dictionary<string, Dictionary<string, object>> value, string userId = null);
|
||||
Task<Dictionary<string, CipherData>> GetEncryptedCiphersAsync(string userId = null);
|
||||
Task SetEncryptedCiphersAsync(Dictionary<string, CipherData> value, string userId = null);
|
||||
Task<int?> GetDefaultUriMatchAsync(string userId = null);
|
||||
Task SetDefaultUriMatchAsync(int? value, string userId = null);
|
||||
Task<HashSet<string>> GetNeverDomainsAsync(string userId = null);
|
||||
Task SetNeverDomainsAsync(HashSet<string> value, string userId = null);
|
||||
Task<int?> GetClearClipboardAsync(string userId = null);
|
||||
Task SetClearClipboardAsync(int? value, string userId = null);
|
||||
Task<Dictionary<string, CollectionData>> GetEncryptedCollectionsAsync(string userId = null);
|
||||
Task SetEncryptedCollectionsAsync(Dictionary<string, CollectionData> value, string userId = null);
|
||||
Task<bool> GetPasswordRepromptAutofillAsync(string userId = null);
|
||||
Task SetPasswordRepromptAutofillAsync(bool? value, string userId = null);
|
||||
Task<bool> GetPasswordVerifiedAutofillAsync(string userId = null);
|
||||
Task SetPasswordVerifiedAutofillAsync(bool? value, string userId = null);
|
||||
Task<DateTime?> GetLastSyncAsync(string userId = null);
|
||||
Task SetLastSyncAsync(DateTime? value, string userId = null);
|
||||
Task<string> GetSecurityStampAsync(string userId = null);
|
||||
Task SetSecurityStampAsync(string value, string userId = null);
|
||||
Task<bool> GetEmailVerifiedAsync(string userId = null);
|
||||
Task SetEmailVerifiedAsync(bool? value, string userId = null);
|
||||
Task<bool> GetForcePasswordReset(string userId = null);
|
||||
Task SetForcePasswordResetAsync(bool? value, string userId = null);
|
||||
Task<bool> GetSyncOnRefreshAsync(string userId = null);
|
||||
Task SetSyncOnRefreshAsync(bool? value, string userId = null);
|
||||
Task<string> GetRememberedEmailAsync(string userId = null);
|
||||
Task SetRememberedEmailAsync(string value, string userId = null);
|
||||
Task<bool?> GetRememberEmailAsync(string userId = null);
|
||||
Task SetRememberEmailAsync(bool? value, string userId = null);
|
||||
Task<string> GetRememberedOrgIdentifierAsync(string userId = null);
|
||||
Task SetRememberedOrgIdentifierAsync(string value, string userId = null);
|
||||
Task<bool?> GetRememberOrgIdentifierAsync(string userId = null);
|
||||
Task SetRememberOrgIdentifierAsync(bool? value, string userId = null);
|
||||
Task<string> GetThemeAsync(string userId = null);
|
||||
Task SetThemeAsync(string value, string userId = null);
|
||||
Task<bool?> GetAddSitePromptShownAsync(string userId = null);
|
||||
Task SetAddSitePromptShownAsync(bool? value, string userId = null);
|
||||
Task<bool?> GetMigratedFromV1Async(string userId = null);
|
||||
Task SetMigratedFromV1Async(bool? value, string userId = null);
|
||||
Task<bool?> GetMigratedFromV1AutofillPromptShownAsync(string userId = null);
|
||||
Task SetMigratedFromV1AutofillPromptShownAsync(bool? value, string userId = null);
|
||||
Task<bool?> GetTriedV1ResyncAsync(string userId = null);
|
||||
Task SetTriedV1ResyncAsync(bool? value, string userId = null);
|
||||
Task<bool?> GetPushInitialPromptShownAsync(string userId = null);
|
||||
Task SetPushInitialPromptShownAsync(bool? value, string userId = null);
|
||||
Task<DateTime?> GetPushLastRegistrationDateAsync(string userId = null);
|
||||
Task SetPushLastRegistrationDateAsync(DateTime? value, string userId = null);
|
||||
Task<string> GetPushCurrentTokenAsync(string userId = null);
|
||||
Task SetPushCurrentTokenAsync(string value, string userId = null);
|
||||
Task<List<EventData>> GetEventCollectionAsync(string userId = null);
|
||||
Task SetEventCollectionAsync(List<EventData> value, string userId = null);
|
||||
Task<Dictionary<string, FolderData>> GetEncryptedFoldersAsync(string userId = null);
|
||||
Task SetEncryptedFoldersAsync(Dictionary<string, FolderData> value, string userId = null);
|
||||
Task<Dictionary<string, PolicyData>> GetEncryptedPoliciesAsync(string userId = null);
|
||||
Task SetEncryptedPoliciesAsync(Dictionary<string, PolicyData> value, string userId = null);
|
||||
Task<string> GetPushRegisteredTokenAsync();
|
||||
Task SetPushRegisteredTokenAsync(string value);
|
||||
Task<bool?> GetAppExtensionStartedAsync(string userId = null);
|
||||
Task SetAppExtensionStartedAsync(bool? value, string userId = null);
|
||||
Task<bool?> GetAppExtensionActivatedAsync(string userId = null);
|
||||
Task SetAppExtensionActivatedAsync(bool? value, string userId = null);
|
||||
Task<string> GetAppIdAsync(string userId = null);
|
||||
Task SetAppIdAsync(string value, string userId = null);
|
||||
Task<bool> GetUsesKeyConnectorAsync(string userId = null);
|
||||
Task SetUsesKeyConnectorAsync(bool? value, string userId = null);
|
||||
Task<Dictionary<string, OrganizationData>> GetOrganizationsAsync(string userId = null);
|
||||
Task SetOrganizationsAsync(Dictionary<string, OrganizationData> organizations, string userId = null);
|
||||
Task<PasswordGenerationOptions> GetPasswordGenerationOptionsAsync(string userId = null);
|
||||
Task SetPasswordGenerationOptionsAsync(PasswordGenerationOptions value, string userId = null);
|
||||
Task<List<GeneratedPasswordHistory>> GetEncryptedPasswordGenerationHistory(string userId = null);
|
||||
Task SetEncryptedPasswordGenerationHistoryAsync(List<GeneratedPasswordHistory> value, string userId = null);
|
||||
Task<Dictionary<string, SendData>> GetEncryptedSendsAsync(string userId = null);
|
||||
Task SetEncryptedSendsAsync(Dictionary<string, SendData> value, string userId = null);
|
||||
Task<Dictionary<string, object>> GetSettingsAsync(string userId = null);
|
||||
Task SetSettingsAsync(Dictionary<string, object> value, string userId = null);
|
||||
Task<string> GetAccessTokenAsync(string userId = null);
|
||||
Task SetAccessTokenAsync(string value, bool skipTokenStorage, string userId = null);
|
||||
Task<string> GetRefreshTokenAsync(string userId = null);
|
||||
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
|
||||
Task<string> GetTwoFactorTokenAsync(string email = null);
|
||||
Task SetTwoFactorTokenAsync(string value, string email = null);
|
||||
Task<T> GetAsync<T>(string key);
|
||||
Task RemoveAsync(string key);
|
||||
Task SaveAsync<T>(string key, T obj);
|
||||
Task PurgeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,15 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface ITokenService
|
||||
{
|
||||
Task ClearTokenAsync(string userId = null);
|
||||
Task ClearTokenAsync();
|
||||
Task ClearTwoFactorTokenAsync(string email);
|
||||
void ClearCache();
|
||||
JObject DecodeToken();
|
||||
string GetEmail();
|
||||
bool GetEmailVerified();
|
||||
string GetIssuer();
|
||||
string GetName();
|
||||
bool GetPremium();
|
||||
Task<bool> GetIsExternal();
|
||||
bool GetIsExternal();
|
||||
Task<string> GetRefreshTokenAsync();
|
||||
Task<string> GetTokenAsync();
|
||||
Task ToggleTokensAsync();
|
||||
@@ -23,7 +22,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GetTwoFactorTokenAsync(string email);
|
||||
string GetUserId();
|
||||
Task SetRefreshTokenAsync(string refreshToken);
|
||||
Task SetAccessTokenAsync(string token);
|
||||
Task SetTokenAsync(string token);
|
||||
Task SetTokensAsync(string accessToken, string refreshToken);
|
||||
Task SetTwoFactorTokenAsync(string token, string email);
|
||||
bool TokenNeedsRefresh(int minutes = 5);
|
||||
|
||||
31
src/Core/Abstractions/IUserService.cs
Normal file
31
src/Core/Abstractions/IUserService.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
Task<bool> CanAccessPremiumAsync();
|
||||
Task ClearAsync();
|
||||
Task ClearOrganizationsAsync(string userId);
|
||||
Task<List<Organization>> GetAllOrganizationAsync();
|
||||
Task<string> GetEmailAsync();
|
||||
Task<KdfType?> GetKdfAsync();
|
||||
Task<int?> GetKdfIterationsAsync();
|
||||
Task<Organization> GetOrganizationAsync(string id);
|
||||
Task<Organization> GetOrganizationByIdentifierAsync(string identifier);
|
||||
Task<string> GetSecurityStampAsync();
|
||||
Task<bool> GetEmailVerifiedAsync();
|
||||
Task<bool> GetForcePasswordReset();
|
||||
Task<string> GetUserIdAsync();
|
||||
Task<bool> IsAuthenticatedAsync();
|
||||
Task ReplaceOrganizationsAsync(Dictionary<string, OrganizationData> organizations);
|
||||
Task SetInformationAsync(string userId, string email, KdfType kdf, int? kdfIterations);
|
||||
Task SetSecurityStampAsync(string stamp);
|
||||
Task SetEmailVerifiedAsync(bool emailVerified);
|
||||
Task SetForcePasswordReset(bool forcePasswordReset);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Domain;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IVaultTimeoutService
|
||||
{
|
||||
long? DelayLockAndLogoutMs { get; set; }
|
||||
EncString PinProtectedKey { get; set; }
|
||||
bool BiometricLocked { get; set; }
|
||||
|
||||
Task CheckVaultTimeoutAsync();
|
||||
Task<bool> ShouldTimeoutAsync(string userId = null);
|
||||
Task ExecuteTimeoutActionAsync(string userId = null);
|
||||
Task ClearAsync(string userId = null);
|
||||
Task<bool> IsLockedAsync(string userId = null);
|
||||
Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null);
|
||||
Task<bool> IsBiometricLockSetAsync(string userId = null);
|
||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
|
||||
Task LogOutAsync(bool userInitiated = true, string userId = null);
|
||||
Task ClearAsync();
|
||||
Task<bool> IsLockedAsync();
|
||||
Task<Tuple<bool, bool>> IsPinLockSetAsync();
|
||||
Task<bool> IsBiometricLockSetAsync();
|
||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false);
|
||||
Task LogOutAsync();
|
||||
Task SetVaultTimeoutOptionsAsync(int? timeout, string action);
|
||||
Task<int?> GetVaultTimeout(string userId = null);
|
||||
Task<int?> GetVaultTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,31 @@
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const int MaxAccounts = 5;
|
||||
public const string AndroidAppProtocol = "androidapp://";
|
||||
public const string iOSAppProtocol = "iosapp://";
|
||||
public static string StateKey = "state";
|
||||
public static string AppIdKey = "appId";
|
||||
public static string PreAuthEnvironmentUrlsKey = "preAuthEnvironmentUrls";
|
||||
public static string SyncOnRefreshKey = "syncOnRefresh";
|
||||
public static string VaultTimeoutKey = "lockOption";
|
||||
public static string VaultTimeoutActionKey = "vaultTimeoutAction";
|
||||
public static string LastActiveTimeKey = "lastActiveTime";
|
||||
public static string BiometricUnlockKey = "fingerprintUnlock";
|
||||
public static string ProtectedPin = "protectedPin";
|
||||
public static string PinProtectedKey = "pinProtectedKey";
|
||||
public static string DefaultUriMatch = "defaultUriMatch";
|
||||
public static string DisableAutoTotpCopyKey = "disableAutoTotpCopy";
|
||||
public static string EnvironmentUrlsKey = "environmentUrls";
|
||||
public static string LastFileCacheClearKey = "lastFileCacheClear";
|
||||
public static string AutofillDisableSavePromptKey = "autofillDisableSavePrompt";
|
||||
public static string AutofillBlacklistedUrisKey = "autofillBlacklistedUris";
|
||||
public static string AutofillTileAdded = "autofillTileAdded";
|
||||
public static string DisableFaviconKey = "disableFavicon";
|
||||
public static string PushRegisteredTokenKey = "pushRegisteredToken";
|
||||
public static string PushCurrentTokenKey = "pushCurrentToken";
|
||||
public static string PushLastRegistrationDateKey = "pushLastRegistrationDate";
|
||||
public static string PushInitialPromptShownKey = "pushInitialPromptShown";
|
||||
public static string ThemeKey = "theme";
|
||||
public static string ClearClipboardKey = "clearClipboard";
|
||||
public static string LastBuildKey = "lastBuild";
|
||||
public static string OldUserIdKey = "userId";
|
||||
public static string AddSitePromptShownKey = "addSitePromptShown";
|
||||
public static string ClearCiphersCacheKey = "clearCiphersCache";
|
||||
public static string BiometricIntegrityKey = "biometricIntegrityState";
|
||||
@@ -26,12 +38,11 @@
|
||||
public static string MigratedFromV1AutofillPromptShown = "migratedV1AutofillPromptShown";
|
||||
public static string TriedV1Resync = "triedV1Resync";
|
||||
public static string EventCollectionKey = "eventCollection";
|
||||
public static string RememberEmailKey = "rememberEmail";
|
||||
public static string RememberedEmailKey = "rememberedEmail";
|
||||
public static string RememberOrgIdentifierKey = "rememberOrgIdentifier";
|
||||
public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier";
|
||||
public static string AppExtensionStartedKey = "appExtensionStarted";
|
||||
public static string AppExtensionActivatedKey = "appExtensionActivated";
|
||||
public static string PreviousPageKey = "previousPage";
|
||||
public static string InlineAutofillEnabledKey = "inlineAutofillEnabled";
|
||||
public static string InvalidUnlockAttempts = "invalidUnlockAttempts";
|
||||
public static string PasswordRepromptAutofillKey = "passwordRepromptAutofillKey";
|
||||
public static string PasswordVerifiedAutofillKey = "passwordVerifiedAutofillKey";
|
||||
public const int SelectFileRequestCode = 42;
|
||||
public const int SelectFilePermissionRequestCode = 43;
|
||||
public const int SaveFileRequestCode = 44;
|
||||
@@ -47,43 +58,5 @@
|
||||
iOSAutoFillClearCiphersCacheKey,
|
||||
iOSExtensionClearCiphersCacheKey
|
||||
};
|
||||
|
||||
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
||||
public static string FoldersKey(string userId) => $"folders_{userId}";
|
||||
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
||||
public static string OrganizationsKey(string userId) => $"organizations_{userId}";
|
||||
public static string LocalDataKey(string userId) => $"ciphersLocalData_{userId}";
|
||||
public static string NeverDomainsKey(string userId) => $"neverDomains_{userId}";
|
||||
public static string SendsKey(string userId) => $"sends_{userId}";
|
||||
public static string PoliciesKey(string userId) => $"policies_{userId}";
|
||||
public static string KeyKey(string userId) => $"key_{userId}";
|
||||
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
|
||||
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
|
||||
public static string EncKeyKey(string userId) => $"encKey_{userId}";
|
||||
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
|
||||
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
|
||||
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";
|
||||
public static string PassGenHistoryKey(string userId) => $"generatedPasswordHistory_{userId}";
|
||||
public static string TwoFactorTokenKey(string email) => $"twoFactorToken_{email}";
|
||||
public static string LastActiveTimeKey(string userId) => $"lastActiveTime_{userId}";
|
||||
public static string InvalidUnlockAttemptsKey(string userId) => $"invalidUnlockAttempts_{userId}";
|
||||
public static string InlineAutofillEnabledKey(string userId) => $"inlineAutofillEnabled_{userId}";
|
||||
public static string AutofillDisableSavePromptKey(string userId) => $"autofillDisableSavePrompt_{userId}";
|
||||
public static string AutofillBlacklistedUrisKey(string userId) => $"autofillBlacklistedUris_{userId}";
|
||||
public static string ClearClipboardKey(string userId) => $"clearClipboard_{userId}";
|
||||
public static string SyncOnRefreshKey(string userId) => $"syncOnRefresh_{userId}";
|
||||
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 DisableAutoTotpCopyKey(string userId) => $"disableAutoTotpCopy_{userId}";
|
||||
public static string PreviousPageKey(string userId) => $"previousPage_{userId}";
|
||||
public static string PasswordRepromptAutofillKey(string userId) => $"passwordRepromptAutofillKey_{userId}";
|
||||
public static string PasswordVerifiedAutofillKey(string userId) => $"passwordVerifiedAutofillKey_{userId}";
|
||||
public static string SettingsKey(string userId) => $"settings_{userId}";
|
||||
public static string UsesKeyConnectorKey(string userId) => $"usesKeyConnector_{userId}";
|
||||
public static string ProtectedPinKey(string userId) => $"protectedPin_{userId}";
|
||||
public static string ForcePasswordResetKey(string userId) => $"forcePasswordReset_{userId}";
|
||||
public static string LastSyncKey(string userId) => $"lastSync_{userId}";
|
||||
public static string BiometricUnlockKey(string userId) => $"biometricUnlock_{userId}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum AuthenticationStatus : byte
|
||||
{
|
||||
LoggedOut = 0,
|
||||
Locked = 1,
|
||||
Unlocked = 2,
|
||||
Active = 3,
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum StorageLocation
|
||||
{
|
||||
Both = 0,
|
||||
Disk = 1,
|
||||
Memory = 2
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class Account : Domain
|
||||
{
|
||||
public AccountProfile Profile;
|
||||
public AccountTokens Tokens;
|
||||
public AccountSettings Settings;
|
||||
public AccountKeys Keys;
|
||||
|
||||
public Account() { }
|
||||
|
||||
public Account(AccountProfile profile, AccountTokens tokens)
|
||||
{
|
||||
Profile = profile;
|
||||
Tokens = tokens;
|
||||
Settings = new AccountSettings();
|
||||
Keys = new AccountKeys();
|
||||
}
|
||||
|
||||
public Account(Account account)
|
||||
{
|
||||
// Copy constructor excludes Keys (for storage)
|
||||
Profile = new AccountProfile(account.Profile);
|
||||
Tokens = new AccountTokens(account.Tokens);
|
||||
Settings = new AccountSettings(account.Settings);
|
||||
}
|
||||
|
||||
public class AccountProfile
|
||||
{
|
||||
public AccountProfile() { }
|
||||
|
||||
public AccountProfile(AccountProfile copy)
|
||||
{
|
||||
if (copy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UserId = copy.UserId;
|
||||
Email = copy.Email;
|
||||
Stamp = copy.Stamp;
|
||||
KdfType = copy.KdfType;
|
||||
KdfIterations = copy.KdfIterations;
|
||||
EmailVerified = copy.EmailVerified;
|
||||
HasPremiumPersonally = copy.HasPremiumPersonally;
|
||||
}
|
||||
|
||||
public string UserId;
|
||||
public string Email;
|
||||
public string Stamp;
|
||||
public KdfType? KdfType;
|
||||
public int? KdfIterations;
|
||||
public bool? EmailVerified;
|
||||
public bool? HasPremiumPersonally;
|
||||
}
|
||||
|
||||
public class AccountTokens
|
||||
{
|
||||
public AccountTokens() { }
|
||||
|
||||
public AccountTokens(AccountTokens copy)
|
||||
{
|
||||
if (copy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AccessToken = copy.AccessToken;
|
||||
RefreshToken = copy.RefreshToken;
|
||||
}
|
||||
|
||||
public string AccessToken;
|
||||
public string RefreshToken;
|
||||
}
|
||||
|
||||
public class AccountSettings
|
||||
{
|
||||
public AccountSettings() { }
|
||||
|
||||
public AccountSettings(AccountSettings copy)
|
||||
{
|
||||
if (copy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnvironmentUrls = copy.EnvironmentUrls;
|
||||
PinProtected = copy.PinProtected;
|
||||
VaultTimeout = copy.VaultTimeout;
|
||||
VaultTimeoutAction = copy.VaultTimeoutAction;
|
||||
}
|
||||
|
||||
public EnvironmentUrlData EnvironmentUrls;
|
||||
public EncString PinProtected;
|
||||
public int? VaultTimeout;
|
||||
public string VaultTimeoutAction;
|
||||
}
|
||||
|
||||
public class AccountKeys
|
||||
{
|
||||
public SymmetricCryptoKey Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class State : Domain
|
||||
{
|
||||
public Dictionary<string, Account> Accounts { get; set; }
|
||||
public string ActiveUserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class StorageOptions : Domain
|
||||
{
|
||||
public StorageLocation? StorageLocation { get; set; }
|
||||
public bool? UseSecureStorage { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string Email { get; set; }
|
||||
public bool? SkipTokenStorage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
|
||||
namespace Bit.Core.Models.View
|
||||
{
|
||||
public class AccountView : View
|
||||
{
|
||||
public AccountView() { }
|
||||
|
||||
public AccountView(Account a = null)
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
// null will render as "Add Account" row
|
||||
return;
|
||||
}
|
||||
IsAccount = true;
|
||||
UserId = a.Profile?.UserId;
|
||||
Email = a.Profile?.Email;
|
||||
Hostname = a.Settings?.EnvironmentUrls?.Base;
|
||||
}
|
||||
|
||||
public bool IsAccount { get; set; }
|
||||
public AuthenticationStatus? AuthStatus { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Hostname { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,12 @@ namespace Bit.Core.Services
|
||||
private readonly HttpClient _httpClient = new HttpClient();
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly Func<Tuple<string, bool, bool>, Task> _logoutCallbackAsync;
|
||||
private readonly Func<bool, Task> _logoutCallbackAsync;
|
||||
|
||||
public ApiService(
|
||||
ITokenService tokenService,
|
||||
IPlatformUtilsService platformUtilsService,
|
||||
Func<Tuple<string, bool, bool>, Task> logoutCallbackAsync,
|
||||
Func<bool, Task> logoutCallbackAsync,
|
||||
string customUserAgent = null)
|
||||
{
|
||||
_tokenService = tokenService;
|
||||
@@ -698,7 +698,7 @@ namespace Bit.Core.Services
|
||||
if (authed && ((tokenError && response.StatusCode == HttpStatusCode.BadRequest) ||
|
||||
response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden))
|
||||
{
|
||||
await _logoutCallbackAsync(new Tuple<string, bool, bool>(null, false, true));
|
||||
await _logoutCallbackAsync(true);
|
||||
return null;
|
||||
}
|
||||
try
|
||||
|
||||
@@ -6,28 +6,33 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class AppIdService : IAppIdService
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
|
||||
public AppIdService(IStateService stateService)
|
||||
public AppIdService(IStorageService storageService)
|
||||
{
|
||||
_stateService = stateService;
|
||||
_storageService = storageService;
|
||||
}
|
||||
|
||||
public async Task<string> GetAppIdAsync()
|
||||
public Task<string> GetAppIdAsync()
|
||||
{
|
||||
var appId = await _stateService.GetAppIdAsync();
|
||||
if (appId != null)
|
||||
return MakeAndGetAppIdAsync("appId");
|
||||
}
|
||||
|
||||
public Task<string> GetAnonymousAppIdAsync()
|
||||
{
|
||||
return MakeAndGetAppIdAsync("anonymousAppId");
|
||||
}
|
||||
|
||||
private async Task<string> MakeAndGetAppIdAsync(string key)
|
||||
{
|
||||
var existingId = await _storageService.GetAsync<string>(key);
|
||||
if (existingId != null)
|
||||
{
|
||||
return appId;
|
||||
return existingId;
|
||||
}
|
||||
appId = MakeAppId();
|
||||
await _stateService.SetAppIdAsync(appId);
|
||||
return appId;
|
||||
}
|
||||
|
||||
private string MakeAppId()
|
||||
{
|
||||
return Guid.NewGuid().ToString();
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
await _storageService.SaveAsync(key, guid);
|
||||
return guid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user