mirror of
https://github.com/bitwarden/mobile
synced 2025-12-15 15:53:44 +00:00
Compare commits
47 Commits
v2.15.0
...
fix-avatar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4e599f680 | ||
|
|
d41e1a0bc3 | ||
|
|
e717992b2b | ||
|
|
d52a1bf668 | ||
|
|
6f3999016f | ||
|
|
796cf6dc25 | ||
|
|
59e7969856 | ||
|
|
afecc1f569 | ||
|
|
a3922f7fbb | ||
|
|
7e288c62f9 | ||
|
|
ed2bb9b94e | ||
|
|
1c6a8964fe | ||
|
|
6aa96b8964 | ||
|
|
f6d4b0a443 | ||
|
|
2791d4b8ec | ||
|
|
e20ae26808 | ||
|
|
e3a8d3bb55 | ||
|
|
65739489a7 | ||
|
|
42403210a0 | ||
|
|
120fb70039 | ||
|
|
d1ece79d2e | ||
|
|
ebae2585f6 | ||
|
|
5e15a2f30e | ||
|
|
c6547771a5 | ||
|
|
9fdcba386e | ||
|
|
86397a6f1e | ||
|
|
4c2e7331e3 | ||
|
|
b55a450f44 | ||
|
|
b28e265ed4 | ||
|
|
6164c764b4 | ||
|
|
ad3b401ed3 | ||
|
|
adb8bb4f1b | ||
|
|
04c7409418 | ||
|
|
705b8ac12b | ||
|
|
23a164b245 | ||
|
|
6f936343ae | ||
|
|
5eeec7d9ed | ||
|
|
b95efae7fb | ||
|
|
4a1f28caf8 | ||
|
|
dddc38ef64 | ||
|
|
05bcc10277 | ||
|
|
ea1ee2c3d3 | ||
|
|
2a373dd3fc | ||
|
|
14d2b833d8 | ||
|
|
9fdf2ada6f | ||
|
|
833103b2a0 | ||
|
|
6bae85b22d |
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -397,6 +397,15 @@ 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 }}
|
||||
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -3,6 +3,15 @@ name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_type:
|
||||
description: 'Release Options'
|
||||
required: true
|
||||
default: 'Initial Release'
|
||||
type: choice
|
||||
options:
|
||||
- Initial Release
|
||||
- Redeploy
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -31,6 +40,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Check to make sure Mobile release version has been bumped
|
||||
if: ${{ github.event.inputs.release_type == 'Initial Release' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
|
||||
83
.github/workflows/version-bump.yml
vendored
Normal file
83
.github/workflows/version-bump.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: Version Bump
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_number:
|
||||
description: "New Version"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
bump_version:
|
||||
name: "Create version_bump_${{ github.event.inputs.version_number }} branch"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||
|
||||
- name: Create Version Branch
|
||||
run: |
|
||||
git switch -c version_bump_${{ github.event.inputs.version_number }}
|
||||
git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Checkout Version Branch
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||
with:
|
||||
ref: version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Bump Version - Android XML
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
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
|
||||
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
|
||||
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
|
||||
with:
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS/Info.plist"
|
||||
|
||||
- name: Commit files
|
||||
run: |
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
|
||||
|
||||
- name: Push changes
|
||||
run: git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Create Version PR
|
||||
env:
|
||||
PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
BASE_BRANCH: master
|
||||
TITLE: "Bump version to ${{ github.event.inputs.version_number }}"
|
||||
run: |
|
||||
gh pr create --title "$TITLE" \
|
||||
--base "$BASE" \
|
||||
--head "$PR_BRANCH" \
|
||||
--label "version update" \
|
||||
--label "automated pr" \
|
||||
--body "
|
||||
## Type of change
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature development
|
||||
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||
- [ ] Build/deploy pipeline (DevOps)
|
||||
- [X] Other
|
||||
|
||||
## Objective
|
||||
Automated version bump to ${{ github.event.inputs.version_number }}"
|
||||
@@ -6,6 +6,7 @@ using Android.Views;
|
||||
using System;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Utilities;
|
||||
|
||||
namespace Bit.Droid.Accessibility
|
||||
{
|
||||
@@ -17,6 +18,7 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
Intent?.Validate();
|
||||
base.OnCreate(bundle);
|
||||
HandleIntent(Intent, 932473);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ 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"),
|
||||
@@ -67,6 +68,7 @@ namespace Bit.Droid.Accessibility
|
||||
new Browser("com.opera.mini.native", "url_field"),
|
||||
new Browser("com.opera.mini.native.beta", "url_field"),
|
||||
new Browser("com.opera.touch", "addressbarEdit"),
|
||||
new Browser("com.qflair.browserq", "url"),
|
||||
new Browser("com.qwant.liberty", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v4)
|
||||
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
||||
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
||||
@@ -101,8 +103,10 @@ namespace Bit.Droid.Accessibility
|
||||
new Browser("org.mozilla.fennec_fdroid", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.firefox", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.focus", "display_url"),
|
||||
new Browser("org.mozilla.klar", "display_url"),
|
||||
new Browser("org.mozilla.focus", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.focus.beta", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.focus.nightly", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.klar", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
||||
new Browser("org.mozilla.rocket", "display_url"),
|
||||
new Browser("org.torproject.torbrowser", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0.3)
|
||||
|
||||
@@ -10,7 +10,6 @@ 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;
|
||||
|
||||
@@ -25,7 +24,7 @@ namespace Bit.Droid.Accessibility
|
||||
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
||||
private const string BitwardenWebsite = "vault.bitwarden.com";
|
||||
|
||||
private IStorageService _storageService;
|
||||
private IStateService _stateService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private DateTime? _lastSettingsReload = null;
|
||||
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
||||
@@ -444,9 +443,9 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private void LoadServices()
|
||||
{
|
||||
if (_storageService == null)
|
||||
if (_stateService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
}
|
||||
if (_broadcasterService == null)
|
||||
{
|
||||
@@ -460,12 +459,12 @@ namespace Bit.Droid.Accessibility
|
||||
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||
{
|
||||
_lastSettingsReload = now;
|
||||
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
var uris = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
if (uris != null)
|
||||
{
|
||||
_blacklistedUris = new HashSet<string>(uris);
|
||||
}
|
||||
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
|
||||
var isAutoFillTileAdded = await _stateService.GetAutofillTileAddedAsync();
|
||||
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,8 @@
|
||||
<Compile Include="Utilities\ThemeHelpers.cs" />
|
||||
<Compile Include="WebAuthCallbackActivity.cs" />
|
||||
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||
<Compile Include="Services\ClipboardService.cs" />
|
||||
<Compile Include="Utilities\IntentExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
||||
@@ -169,9 +171,11 @@
|
||||
<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.xml" />
|
||||
<AndroidResource Include="Resources\drawable\cog_environment.xml" />
|
||||
<AndroidResource Include="Resources\drawable\cog_settings.xml" />
|
||||
<AndroidResource Include="Resources\drawable\icon.xml" />
|
||||
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
||||
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
|
||||
<AndroidResource Include="Resources\drawable\id.xml" />
|
||||
<AndroidResource Include="Resources\drawable\info.xml" />
|
||||
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
||||
@@ -205,6 +209,8 @@
|
||||
<AndroidResource Include="Resources\values-night\styles.xml" />
|
||||
<AndroidResource Include="Resources\values\styles.xml" />
|
||||
<AndroidResource Include="Resources\values\colors.xml" />
|
||||
<AndroidResource Include="Resources\values\manifest.xml" />
|
||||
<AndroidResource Include="Resources\values-v30\manifest.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
|
||||
@@ -270,5 +276,8 @@
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\values-v30\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -37,6 +37,8 @@ namespace Bit.Droid.Autofill
|
||||
"com.duckduckgo.mobile.android",
|
||||
"com.google.android.googlequicksearchbox",
|
||||
"org.mozilla.focus",
|
||||
"org.mozilla.focus.beta",
|
||||
"org.mozilla.focus.nightly",
|
||||
"org.mozilla.klar",
|
||||
};
|
||||
|
||||
@@ -71,6 +73,7 @@ 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",
|
||||
@@ -84,6 +87,7 @@ namespace Bit.Droid.Autofill
|
||||
"com.opera.mini.native",
|
||||
"com.opera.mini.native.beta",
|
||||
"com.opera.touch",
|
||||
"com.qflair.browserq",
|
||||
"com.qwant.liberty",
|
||||
"com.sec.android.app.sbrowser",
|
||||
"com.sec.android.app.sbrowser.beta",
|
||||
|
||||
@@ -22,9 +22,8 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
private ICipherService _cipherService;
|
||||
private IVaultTimeoutService _vaultTimeoutService;
|
||||
private IStorageService _storageService;
|
||||
private IPolicyService _policyService;
|
||||
private IUserService _userService;
|
||||
private IStateService _stateService;
|
||||
|
||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||
FillCallback callback)
|
||||
@@ -38,18 +37,18 @@ namespace Bit.Droid.Autofill
|
||||
var parser = new Parser(structure, ApplicationContext);
|
||||
parser.Parse();
|
||||
|
||||
if (_storageService == null)
|
||||
if (_stateService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
}
|
||||
|
||||
var shouldAutofill = await parser.ShouldAutofillAsync(_storageService);
|
||||
var shouldAutofill = await parser.ShouldAutofillAsync(_stateService);
|
||||
if (!shouldAutofill)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
||||
var inlineAutofillEnabled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||
|
||||
if (_vaultTimeoutService == null)
|
||||
{
|
||||
@@ -81,12 +80,12 @@ namespace Bit.Droid.Autofill
|
||||
return;
|
||||
}
|
||||
|
||||
if (_storageService == null)
|
||||
if (_stateService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
}
|
||||
|
||||
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
|
||||
var disableSavePrompt = await _stateService.GetAutofillDisableSavePromptAsync();
|
||||
if (disableSavePrompt.GetValueOrDefault())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -80,13 +80,13 @@ namespace Bit.Droid.Autofill
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ShouldAutofillAsync(IStorageService storageService)
|
||||
public async Task<bool> ShouldAutofillAsync(IStateService stateService)
|
||||
{
|
||||
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
|
||||
FieldCollection != null && FieldCollection.Fillable;
|
||||
if (fillable)
|
||||
{
|
||||
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
var blacklistedUris = await stateService.GetAutofillBlacklistedUrisAsync();
|
||||
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
||||
{
|
||||
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
||||
|
||||
@@ -19,39 +19,23 @@ using System.Threading.Tasks;
|
||||
using AndroidX.Core.Content;
|
||||
using Bit.App.Utilities;
|
||||
using ZXing.Net.Mobile.Android;
|
||||
using Android.Util;
|
||||
|
||||
namespace Bit.Droid
|
||||
{
|
||||
[Activity(
|
||||
Label = "Bitwarden",
|
||||
Icon = "@mipmap/ic_launcher",
|
||||
Theme = "@style/LaunchTheme",
|
||||
MainLauncher = true,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
||||
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
||||
ConfigChanges.Navigation | ConfigChanges.UiMode)]
|
||||
[IntentFilter(
|
||||
new[] { Intent.ActionSend },
|
||||
Categories = new[] { Intent.CategoryDefault },
|
||||
DataMimeTypes = new[]
|
||||
{
|
||||
@"application/*",
|
||||
@"image/*",
|
||||
@"video/*",
|
||||
@"text/*"
|
||||
})]
|
||||
// Activity and IntentFilter declarations have been moved to Properties/AndroidManifest.xml
|
||||
// They have been hardcoded so we can use the default LaunchMode on Android 11+
|
||||
// LaunchMode defined in values/manifest.xml for Android 10- and values-v30/manifest.xml for Android 11+
|
||||
// See https://github.com/bitwarden/mobile/pull/1673 for details
|
||||
[Register("com.x8bit.bitwarden.MainActivity")]
|
||||
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||
{
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private IMessagingService _messagingService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private IUserService _userService;
|
||||
private IStateService _stateService;
|
||||
private IAppIdService _appIdService;
|
||||
private IStorageService _storageService;
|
||||
private IEventService _eventService;
|
||||
private PendingIntent _clearClipboardPendingIntent;
|
||||
private PendingIntent _eventUploadPendingIntent;
|
||||
private AppOptions _appOptions;
|
||||
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
|
||||
@@ -63,9 +47,6 @@ namespace Bit.Droid
|
||||
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
||||
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
||||
PendingIntentFlags.UpdateCurrent);
|
||||
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
||||
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
||||
PendingIntentFlags.UpdateCurrent);
|
||||
|
||||
var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build();
|
||||
StrictMode.SetThreadPolicy(policy);
|
||||
@@ -73,14 +54,16 @@ namespace Bit.Droid
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||
|
||||
TabLayoutResource = Resource.Layout.Tabbar;
|
||||
ToolbarResource = Resource.Layout.Toolbar;
|
||||
|
||||
// this needs to be called here before base.OnCreate(...)
|
||||
Intent?.Validate();
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
if (!CoreHelpers.InDebugMode())
|
||||
{
|
||||
@@ -88,7 +71,7 @@ namespace Bit.Droid
|
||||
}
|
||||
|
||||
#if !FDROID
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||
var appCenterTask = appCenterHelper.InitAsync();
|
||||
#endif
|
||||
|
||||
@@ -123,10 +106,6 @@ namespace Bit.Droid
|
||||
{
|
||||
ExitApp();
|
||||
}
|
||||
else if (message.Command == "copiedToClipboard")
|
||||
{
|
||||
var task = ClearClipboardAlarmAsync(message.Data as Tuple<string, int?, bool>);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -159,7 +138,15 @@ namespace Bit.Droid
|
||||
base.OnNewIntent(intent);
|
||||
try
|
||||
{
|
||||
if (intent.GetBooleanExtra("generatorTile", false))
|
||||
if (intent?.GetStringExtra("uri") is string uri)
|
||||
{
|
||||
_messagingService.Send("popAllAndGoToAutofillCiphers");
|
||||
if (_appOptions != null)
|
||||
{
|
||||
_appOptions.Uri = uri;
|
||||
}
|
||||
}
|
||||
else if (intent.GetBooleanExtra("generatorTile", false))
|
||||
{
|
||||
_messagingService.Send("popAllAndGoToTabGenerator");
|
||||
if (_appOptions != null)
|
||||
@@ -386,7 +373,7 @@ namespace Bit.Droid
|
||||
{
|
||||
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
||||
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
|
||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
|
||||
}
|
||||
|
||||
private void ExitApp()
|
||||
@@ -395,30 +382,6 @@ namespace Bit.Droid
|
||||
Java.Lang.JavaSystem.Exit(0);
|
||||
}
|
||||
|
||||
private async Task ClearClipboardAlarmAsync(Tuple<string, int?, bool> data)
|
||||
{
|
||||
if (data.Item3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var clearMs = data.Item2;
|
||||
if (clearMs == null)
|
||||
{
|
||||
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
||||
if (clearSeconds != null)
|
||||
{
|
||||
clearMs = clearSeconds.Value * 1000;
|
||||
}
|
||||
}
|
||||
if (clearMs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
|
||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent);
|
||||
}
|
||||
|
||||
private void StartEventAlarm()
|
||||
{
|
||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||
|
||||
@@ -97,13 +97,14 @@ namespace Bit.Droid
|
||||
var secureStorageService = new SecureStorageService();
|
||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
|
||||
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||
var deviceActionService = new DeviceActionService(stateService, 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(mobileStorageService, secureStorageService, cryptoFunctionService);
|
||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||
|
||||
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
||||
@@ -113,6 +114,8 @@ 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<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||
@@ -131,7 +134,7 @@ namespace Bit.Droid
|
||||
ServiceContainer.Register<IPushNotificationListenerService>(
|
||||
"pushNotificationListenerService", notificationListenerService);
|
||||
var androidPushNotificationService = new AndroidPushNotificationService(
|
||||
mobileStorageService, notificationListenerService);
|
||||
stateService, notificationListenerService);
|
||||
ServiceContainer.Register<IPushNotificationService>(
|
||||
"pushNotificationService", androidPushNotificationService);
|
||||
#endif
|
||||
@@ -147,10 +150,6 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:versionCode="1"
|
||||
android:versionName="2.15.0"
|
||||
android:installLocation="internalOnly"
|
||||
package="com.x8bit.bitwarden">
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.15.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.NFC"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||
|
||||
<application
|
||||
android:label="Bitwarden"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:allowBackup="false"
|
||||
tools:replace="android:allowBackup"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.x8bit.bitwarden.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
<application android:label="Bitwarden" android:theme="@style/LaunchTheme" android:allowBackup="false" tools:replace="android:allowBackup" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:networkSecurityConfig="@xml/network_security_config">
|
||||
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.x8bit.bitwarden.fileprovider" android:exported="false" android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/>
|
||||
</provider>
|
||||
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1"/>
|
||||
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions"/>
|
||||
|
||||
<!-- Support for Samsung "Multi Window" mode (for Android < 7.0 users) -->
|
||||
<meta-data android:name="com.samsung.android.sdk.multiwindow.enable" android:value="true" />
|
||||
<meta-data android:name="com.samsung.android.sdk.multiwindow.penwindow.enable" android:value="true" />
|
||||
<meta-data android:name="com.samsung.android.sdk.multiwindow.enable" android:value="true"/>
|
||||
<meta-data android:name="com.samsung.android.sdk.multiwindow.penwindow.enable" android:value="true"/>
|
||||
|
||||
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
|
||||
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true" />
|
||||
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true"/>
|
||||
<!-- Declare MainActivity manually so we can set LaunchMode using API dependant resource -->
|
||||
<activity android:name="com.x8bit.bitwarden.MainActivity" android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|uiMode" android:exported="true" android:icon="@mipmap/ic_launcher" android:label="Bitwarden" android:launchMode="@integer/launchModeAPIlevel" android:theme="@style/LaunchTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="application/*"/>
|
||||
<data android:mimeType="image/*"/>
|
||||
<data android:mimeType="video/*"/>
|
||||
<data android:mimeType="text/*"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<!-- Package visibility (for Android 11+) -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="*"/>
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="*"/>
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace Bit.Droid.Push
|
||||
{
|
||||
public async override void OnNewToken(string token)
|
||||
{
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||
|
||||
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
|
||||
await stateService.SetPushRegisteredTokenAsync(token);
|
||||
await pushNotificationService.RegisterAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Utilities;
|
||||
@@ -14,9 +13,10 @@ namespace Bit.Droid.Receivers
|
||||
{
|
||||
public override async void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
await AppHelpers.PerformUpdateTasksAsync(ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"), storageService);
|
||||
await AppHelpers.PerformUpdateTasksAsync(
|
||||
ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||
ServiceContainer.Resolve<IStateService>("stateService"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
src/Android/Resources/drawable/cog_environment.xml
Normal file
9
src/Android/Resources/drawable/cog_environment.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<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>
|
||||
9
src/Android/Resources/drawable/ic_warning.xml
Normal file
9
src/Android/Resources/drawable/ic_warning.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="19dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="19">
|
||||
<path
|
||||
android:fillColor="#dd4b39"
|
||||
android:pathData="M19.16 18.71H2.64c-0.36 0-0.72-0.09-1.03-0.27c-0.31-0.2-0.57-0.46-0.74-0.78c-0.18-0.32-0.27-0.67-0.27-1.04c0-0.36 0.1-0.72 0.28-1.03L9.14 1.1C9.32 0.76 9.58 0.5 9.89 0.32c0.3-0.18 0.65-0.28 1-0.28c0.36 0 0.7 0.1 1.02 0.28c0.3 0.18 0.56 0.44 0.74 0.75l8.26 14.51c0.18 0.31 0.28 0.67 0.28 1.03c0 0.37-0.09 0.72-0.26 1.04c-0.18 0.32-0.44 0.59-0.75 0.78c-0.31 0.18-0.66 0.28-1.02 0.27zM10.9 1.38c-0.13 0-0.26 0.04-0.38 0.1c-0.11 0.07-0.2 0.16-0.27 0.28L1.99 16.27c-0.07 0.11-0.1 0.24-0.1 0.36C1.9 16.76 1.92 16.9 2 17c0.06 0.12 0.16 0.22 0.27 0.3c0.12 0.06 0.25 0.1 0.38 0.1h16.52c0.13 0 0.26-0.04 0.37-0.1c0.12-0.08 0.21-0.18 0.28-0.3c0.06-0.1 0.1-0.23 0.1-0.36c0-0.12-0.04-0.25-0.1-0.36l-8.26-14.5c-0.07-0.13-0.17-0.22-0.28-0.29c-0.11-0.06-0.24-0.1-0.37-0.1zm0 11.42c-0.17 0-0.34-0.07-0.46-0.2c-0.12-0.12-0.19-0.29-0.19-0.46v-6.1c0-0.18 0.07-0.35 0.2-0.47c0.11-0.13 0.28-0.2 0.45-0.2c0.17 0 0.33 0.07 0.45 0.2c0.12 0.12 0.19 0.3 0.19 0.47v6.1c0 0.17-0.07 0.34-0.19 0.47c-0.12 0.12-0.28 0.2-0.45 0.2zm0 3.3c0.42 0 0.76-0.36 0.76-0.8c0-0.43-0.34-0.78-0.76-0.78c-0.43 0-0.77 0.35-0.77 0.79c0 0.43 0.34 0.79 0.77 0.79z"/>
|
||||
</vector>
|
||||
4
src/Android/Resources/values-v30/manifest.xml
Normal file
4
src/Android/Resources/values-v30/manifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<resources>
|
||||
<integer name="launchModeAPIlevel">0</integer>
|
||||
</resources>
|
||||
4
src/Android/Resources/values/manifest.xml
Normal file
4
src/Android/Resources/values/manifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<resources>
|
||||
<integer name="launchModeAPIlevel">2</integer>
|
||||
</resources>
|
||||
@@ -77,6 +77,9 @@
|
||||
<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"/>
|
||||
@@ -116,6 +119,9 @@
|
||||
<compatibility-package
|
||||
android:name="com.opera.touch"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.qflair.browserq"
|
||||
android:maxLongVersionCode="10000000000"/>
|
||||
<compatibility-package
|
||||
android:name="com.qwant.liberty"
|
||||
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,25 +10,27 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class AndroidPushNotificationService : IPushNotificationService
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
||||
|
||||
public AndroidPushNotificationService(
|
||||
IStorageService storageService,
|
||||
IStateService stateService,
|
||||
IPushNotificationListenerService pushNotificationListenerService)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
_pushNotificationListenerService = pushNotificationListenerService;
|
||||
}
|
||||
|
||||
public bool IsRegisteredForPush => NotificationManagerCompat.From(Android.App.Application.Context)?.AreNotificationsEnabled() ?? false;
|
||||
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey);
|
||||
return await _stateService.GetPushCurrentTokenAsync();
|
||||
}
|
||||
|
||||
public async Task RegisterAsync()
|
||||
{
|
||||
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey);
|
||||
var registeredToken = await _stateService.GetPushRegisteredTokenAsync();
|
||||
var currentToken = await GetTokenAsync();
|
||||
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
||||
{
|
||||
@@ -36,7 +38,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
|
||||
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
src/Android/Services/ClipboardService.cs
Normal file
57
src/Android/Services/ClipboardService.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Droid.Receivers;
|
||||
using Plugin.CurrentActivity;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
{
|
||||
public class ClipboardService : IClipboardService
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
|
||||
|
||||
public ClipboardService(IStateService stateService)
|
||||
{
|
||||
_stateService = stateService;
|
||||
|
||||
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
||||
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
|
||||
0,
|
||||
new Intent(CrossCurrentActivity.Current.Activity, typeof(ClearClipboardAlarmReceiver)),
|
||||
PendingIntentFlags.UpdateCurrent));
|
||||
}
|
||||
|
||||
public async Task CopyTextAsync(string text, int expiresInMs = -1)
|
||||
{
|
||||
await Clipboard.SetTextAsync(text);
|
||||
|
||||
await ClearClipboardAlarmAsync(expiresInMs);
|
||||
}
|
||||
|
||||
private async Task ClearClipboardAlarmAsync(int expiresInMs = -1)
|
||||
{
|
||||
var clearMs = expiresInMs;
|
||||
if (clearMs < 0)
|
||||
{
|
||||
// if not set then we need to check if the user set this config
|
||||
var clearSeconds = await _stateService.GetClearClipboardAsync();
|
||||
if (clearSeconds != null)
|
||||
{
|
||||
clearMs = clearSeconds.Value * 1000;
|
||||
}
|
||||
}
|
||||
if (clearMs < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs;
|
||||
var alarmManager = CrossCurrentActivity.Current.Activity.GetSystemService(Context.AlarmService) as AlarmManager;
|
||||
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class DeviceActionService : IDeviceActionService
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
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(
|
||||
IStorageService storageService,
|
||||
IStateService stateService,
|
||||
IMessagingService messagingService,
|
||||
IBroadcasterService broadcasterService,
|
||||
Func<IEventService> eventServiceFunc)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
_messagingService = messagingService;
|
||||
_broadcasterService = broadcasterService;
|
||||
_eventServiceFunc = eventServiceFunc;
|
||||
@@ -250,7 +250,7 @@ namespace Bit.Droid.Services
|
||||
try
|
||||
{
|
||||
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
||||
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
|
||||
await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
@@ -833,9 +833,8 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
||||
{
|
||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
|
||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
||||
var autoCopyDisabled = await _stateService.GetDisableAutoTotpCopyAsync();
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
||||
{
|
||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
@@ -18,7 +17,7 @@ namespace Bit.Droid.Tile
|
||||
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
||||
public class AutofillTileService : TileService
|
||||
{
|
||||
private IStorageService _storageService;
|
||||
private IStateService _stateService;
|
||||
|
||||
public override void OnTileAdded()
|
||||
{
|
||||
@@ -59,11 +58,11 @@ namespace Bit.Droid.Tile
|
||||
private void SetTileAdded(bool isAdded)
|
||||
{
|
||||
AccessibilityHelpers.IsAutofillTileAdded = isAdded;
|
||||
if (_storageService == null)
|
||||
if (_stateService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
}
|
||||
_storageService.SaveAsync(Constants.AutofillTileAdded, isAdded);
|
||||
_stateService.SetAutofillTileAddedAsync(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 IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
private string _userId;
|
||||
private string _appId;
|
||||
|
||||
public AppCenterHelper(
|
||||
IAppIdService appIdService,
|
||||
IUserService userService)
|
||||
IStateService stateService)
|
||||
{
|
||||
_appIdService = appIdService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_userId = await _userService.GetUserIdAsync();
|
||||
_userId = await _stateService.GetActiveUserIdAsync();
|
||||
_appId = await _appIdService.GetAppIdAsync();
|
||||
|
||||
AppCenter.Start(AppSecret, typeof(Crashes));
|
||||
|
||||
22
src/Android/Utilities/IntentExtensions.cs
Normal file
22
src/Android/Utilities/IntentExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
|
||||
namespace Bit.Droid.Utilities
|
||||
{
|
||||
public static class IntentExtensions
|
||||
{
|
||||
public static void Validate(this Intent intent)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if getting the bundle of the extras causes any exception when unparcelling
|
||||
// Note: getting the bundle like this will cause to call unparcel() internally
|
||||
var b = intent?.Extras?.GetBundle("trashstringwhichhasnousebuttocheckunparcel");
|
||||
}
|
||||
catch (BadParcelableException)
|
||||
{
|
||||
intent.ReplaceExtras((Bundle)null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Bit.Droid.Utilities;
|
||||
|
||||
namespace Bit.Droid
|
||||
{
|
||||
@@ -9,5 +11,12 @@ namespace Bit.Droid
|
||||
[IntentFilter(new[] { Android.Content.Intent.ActionView },
|
||||
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
|
||||
DataScheme = "bitwarden")]
|
||||
public class WebAuthCallbackActivity : Xamarin.Essentials.WebAuthenticatorCallbackActivity { }
|
||||
public class WebAuthCallbackActivity : Xamarin.Essentials.WebAuthenticatorCallbackActivity
|
||||
{
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
Intent?.Validate();
|
||||
base.OnCreate(savedInstanceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace Bit.App.Abstractions
|
||||
string[] ProtectedFields { get; }
|
||||
|
||||
Task<bool> ShowPasswordPromptAsync();
|
||||
|
||||
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
||||
|
||||
Task<bool> Enabled();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Bit.App.Abstractions
|
||||
{
|
||||
public interface IPushNotificationService
|
||||
{
|
||||
bool IsRegisteredForPush { get; }
|
||||
Task<string> GetTokenAsync();
|
||||
Task RegisterAsync();
|
||||
Task UnregisterAsync();
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
<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.2125" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2291" />
|
||||
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
||||
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
@@ -120,6 +122,7 @@
|
||||
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -131,6 +134,7 @@
|
||||
<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;
|
||||
@@ -17,7 +17,6 @@ namespace Bit.App
|
||||
{
|
||||
public partial class App : Application
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
@@ -25,7 +24,6 @@ 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;
|
||||
|
||||
@@ -39,7 +37,6 @@ 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");
|
||||
@@ -47,7 +44,6 @@ 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");
|
||||
|
||||
@@ -84,8 +80,12 @@ 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((message.Data as bool?).GetValueOrDefault()));
|
||||
await LogOutAsync(userId, userInitiated, expired));
|
||||
}
|
||||
else if (message.Command == "loggedOut")
|
||||
{
|
||||
@@ -106,6 +106,18 @@ 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);
|
||||
@@ -113,7 +125,8 @@ namespace Bit.App
|
||||
}
|
||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||
message.Command == "popAllAndGoToTabMyVault" ||
|
||||
message.Command == "popAllAndGoToTabSend")
|
||||
message.Command == "popAllAndGoToTabSend" ||
|
||||
message.Command == "popAllAndGoToAutofillCiphers")
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
@@ -123,7 +136,11 @@ namespace Bit.App
|
||||
{
|
||||
await tabsPage.Navigation.PopModalAsync(false);
|
||||
}
|
||||
if (message.Command == "popAllAndGoToTabMyVault")
|
||||
if (message.Command == "popAllAndGoToAutofillCiphers")
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
||||
}
|
||||
else if (message.Command == "popAllAndGoToTabMyVault")
|
||||
{
|
||||
Options.MyVaultTile = false;
|
||||
tabsPage.ResetToVaultPage();
|
||||
@@ -162,7 +179,7 @@ namespace Bit.App
|
||||
if (string.IsNullOrWhiteSpace(Options.Uri))
|
||||
{
|
||||
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
||||
_storageService);
|
||||
_stateService);
|
||||
if (!updated)
|
||||
{
|
||||
SyncIfNeeded();
|
||||
@@ -171,6 +188,8 @@ namespace Bit.App
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
// Reset delay on every start
|
||||
_vaultTimeoutService.DelayLockAndLogoutMs = null;
|
||||
}
|
||||
_messagingService.Send("startEventTimer");
|
||||
}
|
||||
@@ -184,7 +203,7 @@ namespace Bit.App
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
if (!isLocked)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
SetTabsPageFromAutofill(isLocked);
|
||||
await SleptAsync();
|
||||
@@ -203,7 +222,7 @@ namespace Bit.App
|
||||
|
||||
private async Task SleptAsync()
|
||||
{
|
||||
await HandleVaultTimeoutAsync();
|
||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
_messagingService.Send("stopEventTimer");
|
||||
}
|
||||
|
||||
@@ -229,27 +248,65 @@ namespace Bit.App
|
||||
new System.Globalization.UmAlQuraCalendar();
|
||||
}
|
||||
|
||||
private async Task LogOutAsync(bool expired)
|
||||
private async Task LogOutAsync(string userId, bool? userInitiated, bool? expired)
|
||||
{
|
||||
await AppHelpers.LogOutAsync();
|
||||
await AppHelpers.LogOutAsync(userId, userInitiated.GetValueOrDefault(true));
|
||||
await SetMainPageAsync();
|
||||
_authService.LogOut(() =>
|
||||
{
|
||||
Current.MainPage = new HomePage();
|
||||
if (expired)
|
||||
if (expired.GetValueOrDefault())
|
||||
{
|
||||
_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 _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (authed)
|
||||
{
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
|
||||
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
||||
if (isLocked || shouldTimeout)
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||
if (vaultTimeoutAction == "logOut")
|
||||
{
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||
}
|
||||
}
|
||||
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
||||
{
|
||||
@@ -270,40 +327,13 @@ namespace Bit.App
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ClearCacheIfNeededAsync()
|
||||
{
|
||||
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey);
|
||||
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
||||
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
|
||||
{
|
||||
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
||||
@@ -346,12 +376,12 @@ namespace Bit.App
|
||||
{
|
||||
InitializeComponent();
|
||||
SetCulture();
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
Current.RequestedThemeChanged += (s, a) =>
|
||||
{
|
||||
UpdateTheme();
|
||||
};
|
||||
Current.MainPage = new HomePage();
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
var mainPageTask = SetMainPageAsync();
|
||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||
}
|
||||
@@ -377,17 +407,16 @@ namespace Bit.App
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
_messagingService.Send("updatedTheme");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LockedAsync(bool autoPromptBiometric)
|
||||
{
|
||||
await _stateService.PurgeAsync();
|
||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||
if (vaultTimeout == 0)
|
||||
{
|
||||
autoPromptBiometric = false;
|
||||
@@ -417,7 +446,7 @@ namespace Bit.App
|
||||
}
|
||||
}
|
||||
}
|
||||
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
|
||||
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||
}
|
||||
|
||||
101
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal file
101
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal file
@@ -0,0 +1,101 @@
|
||||
<?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>
|
||||
35
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal file
35
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal file
31
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
114
src/App/Controls/AvatarImageSource.cs
Normal file
114
src/App/Controls/AvatarImageSource.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/App/Controls/ExtendedToolbarItem.cs
Normal file
9
src/App/Controls/ExtendedToolbarItem.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class ExtendedToolbarItem : ToolbarItem
|
||||
{
|
||||
public bool UseOriginalImage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Bit.App.Pages
|
||||
public class BaseChangePasswordViewModel : BaseViewModel
|
||||
{
|
||||
protected readonly IPlatformUtilsService _platformUtilsService;
|
||||
protected readonly IUserService _userService;
|
||||
protected readonly IStateService _stateService;
|
||||
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");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_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 _userService.GetEmailAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
List<string> userInput = null;
|
||||
var atPosition = email.IndexOf('@');
|
||||
if (atPosition > -1)
|
||||
|
||||
81
src/App/Pages/Accounts/DeleteAccountPage.xaml
Normal file
81
src/App/Pages/Accounts/DeleteAccountPage.xaml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?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"
|
||||
x:Class="Bit.App.Pages.Accounts.DeleteAccountPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:DeleteAccountViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
<ContentPage.BindingContext>
|
||||
<pages:DeleteAccountViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ContentPage.Resources>
|
||||
<Style TargetType="Label" x:Key="lblDescription">
|
||||
<Setter Property="FontSize" Value="{OnPlatform Android=Large, iOS=Small}" />
|
||||
</Style>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentPage.Content>
|
||||
<Grid Padding="20, 30" RowSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Source="ic_warning"
|
||||
WidthRequest="28"
|
||||
HeightRequest="25"
|
||||
HorizontalOptions="Start" />
|
||||
<Label
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Text="{u:I18n DeletingYourAccountIsPermanent}"
|
||||
HorizontalOptions="Start"
|
||||
StyleClass="title-danger"
|
||||
Margin="0,15,0,0"/>
|
||||
<Label
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Text="{u:I18n DeleteAccountExplanation}"
|
||||
Style="{StaticResource lblDescription}"
|
||||
HorizontalOptions="Start"
|
||||
Margin="0,6,50,0"
|
||||
Opacity="0.6" />
|
||||
<Button
|
||||
Grid.Row="3"
|
||||
Text="{u:I18n DeleteAccount}"
|
||||
StyleClass="btn-danger"
|
||||
HorizontalOptions="Start"
|
||||
VerticalOptions="Start"
|
||||
Margin="0,20,0,0"
|
||||
Padding="16,0"
|
||||
CornerRadius="2"
|
||||
TextTransform="Uppercase"
|
||||
Clicked="DeleteAccount_Clicked"/>
|
||||
<Button
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Text="{u:I18n Cancel}"
|
||||
StyleClass="btn-secondary"
|
||||
HorizontalOptions="Start"
|
||||
VerticalOptions="Start"
|
||||
Margin="0,20,0,0"
|
||||
Padding="16,0"
|
||||
CornerRadius="2"
|
||||
TextTransform="Uppercase"
|
||||
Clicked="Close_Clicked" />
|
||||
</Grid>
|
||||
</ContentPage.Content>
|
||||
</pages:BaseContentPage>
|
||||
33
src/App/Pages/Accounts/DeleteAccountPage.xaml.cs
Normal file
33
src/App/Pages/Accounts/DeleteAccountPage.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages.Accounts
|
||||
{
|
||||
public partial class DeleteAccountPage : BaseContentPage
|
||||
{
|
||||
DeleteAccountViewModel _vm;
|
||||
|
||||
public DeleteAccountPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as DeleteAccountViewModel;
|
||||
_vm.Page = this;
|
||||
}
|
||||
|
||||
private async void Close_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
await Navigation.PopModalAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async void DeleteAccount_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
await _vm.DeleteAccountAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/App/Pages/Accounts/DeleteAccountViewModel.cs
Normal file
84
src/App/Pages/Accounts/DeleteAccountViewModel.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
#if !FDROID
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
#endif
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class DeleteAccountViewModel : BaseViewModel
|
||||
{
|
||||
readonly IApiService _apiService;
|
||||
readonly IPasswordRepromptService _passwordRepromptService;
|
||||
readonly IMessagingService _messagingService;
|
||||
readonly ICryptoService _cryptoService;
|
||||
readonly IPlatformUtilsService _platformUtilsService;
|
||||
readonly IDeviceActionService _deviceActionService;
|
||||
|
||||
public DeleteAccountViewModel()
|
||||
{
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
|
||||
PageTitle = AppResources.DeleteAccount;
|
||||
}
|
||||
|
||||
public async Task DeleteAccountAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
||||
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
var (password, valid) = await _passwordRepromptService.ShowPasswordPromptAndGetItAsync();
|
||||
if (!valid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.DeletingYourAccount);
|
||||
|
||||
var masterPasswordHashKey = await _cryptoService.HashPasswordAsync(password, null);
|
||||
await _apiService.DeleteAccountAsync(new Core.Models.Request.DeleteAccountRequest
|
||||
{
|
||||
MasterPasswordHash = masterPasswordHashKey
|
||||
});
|
||||
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
|
||||
_messagingService.Send("logout");
|
||||
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.YourAccountHasBeenPermanentlyDeleted);
|
||||
}
|
||||
catch (ApiException apiEx)
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
|
||||
if (apiEx?.Error != null)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(apiEx.Error.GetSingleMessage(), AppResources.AnErrorHasOccurred);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
#if !FDROID
|
||||
Crashes.TrackError(ex);
|
||||
#endif
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,11 @@ 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;
|
||||
@@ -35,7 +32,6 @@ namespace Bit.App.Pages
|
||||
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,53 +1,107 @@
|
||||
<?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>
|
||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
<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}" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<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" />
|
||||
<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>
|
||||
</StackLayout>
|
||||
</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>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -12,13 +12,10 @@ 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();
|
||||
@@ -29,6 +26,11 @@ 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)
|
||||
@@ -37,10 +39,18 @@ namespace Bit.App.Pages
|
||||
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
protected override async void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
_mainContent.Content = _mainLayout;
|
||||
if (await ShowAccountSwitcherAsync())
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
|
||||
{
|
||||
if (message.Command == "updatedTheme")
|
||||
@@ -127,5 +137,23 @@ 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,15 +1,27 @@
|
||||
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,10 +1,12 @@
|
||||
<?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}">
|
||||
@@ -13,6 +15,19 @@
|
||||
<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" />
|
||||
@@ -25,119 +40,162 @@
|
||||
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>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
</ContentPage.ToolbarItems>
|
||||
<AbsoluteLayout
|
||||
x:Name="_absLayout"
|
||||
VerticalOptions="FillAndExpand"
|
||||
HorizontalOptions="FillAndExpand">
|
||||
<ContentView
|
||||
x:Name="_mainContent"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<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>
|
||||
<!-- 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>
|
||||
<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>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
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;
|
||||
@@ -63,6 +61,15 @@ 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)
|
||||
{
|
||||
@@ -147,5 +154,23 @@ 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,7 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
@@ -10,6 +8,7 @@ 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
|
||||
@@ -21,10 +20,7 @@ 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;
|
||||
@@ -49,10 +45,7 @@ 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");
|
||||
@@ -120,6 +113,11 @@ 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 ? "" : "";
|
||||
@@ -130,16 +128,16 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
||||
PinLock = (_pinSet.Item1 && _stateService.GetPinProtectedAsync() != 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 _userService.GetEmailAsync();
|
||||
_email = await _stateService.GetEmailAsync();
|
||||
var webVault = _environmentService.GetWebVaultUrl();
|
||||
if (string.IsNullOrWhiteSpace(webVault))
|
||||
{
|
||||
@@ -204,8 +202,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
ShowPassword = false;
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
|
||||
if (PinLock)
|
||||
{
|
||||
@@ -216,9 +214,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
||||
_vaultTimeoutService.PinProtectedKey);
|
||||
await _stateService.GetPinProtectedCachedAsync());
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
failed = decPin != Pin;
|
||||
if (!failed)
|
||||
@@ -287,12 +285,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_pinSet.Item1)
|
||||
{
|
||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
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));
|
||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||
await _stateService.SetPinProtectedCachedAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||
}
|
||||
MasterPassword = string.Empty;
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
@@ -358,7 +356,7 @@ namespace Bit.App.Pages
|
||||
page.MasterPasswordEntry.Focus();
|
||||
}
|
||||
});
|
||||
_vaultTimeoutService.BiometricLocked = !success;
|
||||
_stateService.BiometricLocked = !success;
|
||||
if (success)
|
||||
{
|
||||
await DoContinueAsync();
|
||||
@@ -377,9 +375,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task DoContinueAsync()
|
||||
{
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
_stateService.BiometricLocked = false;
|
||||
_messagingService.Send("unlocked");
|
||||
UnlockedAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?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}">
|
||||
@@ -13,6 +15,21 @@
|
||||
<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" />
|
||||
@@ -25,68 +42,109 @@
|
||||
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>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
</ContentPage.ToolbarItems>
|
||||
<!-- Account Switching Overlay -->
|
||||
<ContentView
|
||||
x:Name="_accountListOverlay"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
IsVisible="False"
|
||||
BackgroundColor="#22000000"
|
||||
Padding="0">
|
||||
|
||||
<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
|
||||
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>
|
||||
<StackLayout Padding="10, 0">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
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;
|
||||
@@ -11,8 +9,6 @@ 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;
|
||||
|
||||
@@ -20,9 +16,6 @@ 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;
|
||||
@@ -33,7 +26,6 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
_vm.Email = email;
|
||||
@@ -54,6 +46,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
ToolbarItems.Add(_getPasswordHint);
|
||||
}
|
||||
|
||||
if (!_appOptions?.IosExtension ?? false)
|
||||
{
|
||||
ToolbarItems.Remove(_closeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public Entry MasterPasswordEntry { get; set; }
|
||||
@@ -61,6 +58,15 @@ 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)
|
||||
{
|
||||
@@ -130,5 +136,23 @@ 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,25 +1,21 @@
|
||||
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;
|
||||
@@ -34,7 +30,6 @@ 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");
|
||||
@@ -67,6 +62,11 @@ 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 _storageService.GetAsync<string>(Keys_RememberedEmail);
|
||||
Email = await _stateService.GetRememberedEmailAsync();
|
||||
}
|
||||
var rememberEmail = await _storageService.GetAsync<bool?>(Keys_RememberEmail);
|
||||
var rememberEmail = await _stateService.GetRememberEmailAsync();
|
||||
RememberEmail = rememberEmail.GetValueOrDefault(true);
|
||||
}
|
||||
|
||||
@@ -131,11 +131,11 @@ namespace Bit.App.Pages
|
||||
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
||||
if (RememberEmail)
|
||||
{
|
||||
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
||||
await _stateService.SetRememberedEmailAsync(Email);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
||||
await _stateService.SetRememberedEmailAsync(null);
|
||||
}
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
|
||||
@@ -163,8 +163,6 @@ 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,8 +10,6 @@ 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;
|
||||
@@ -20,10 +18,7 @@ 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;
|
||||
@@ -36,7 +31,6 @@ 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,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
@@ -16,16 +15,12 @@ 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;
|
||||
|
||||
@@ -40,7 +35,6 @@ 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");
|
||||
|
||||
@@ -66,9 +60,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(OrgIdentifier))
|
||||
{
|
||||
OrgIdentifier = await _storageService.GetAsync<string>(Keys_RememberedOrgIdentifier);
|
||||
OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
|
||||
}
|
||||
var rememberOrgIdentifier = await _storageService.GetAsync<bool?>(Keys_RememberOrgIdentifier);
|
||||
var rememberOrgIdentifier = await _stateService.GetRememberOrgIdentifierAsync();
|
||||
RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true);
|
||||
}
|
||||
|
||||
@@ -172,11 +166,11 @@ namespace Bit.App.Pages
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
if (RememberOrgIdentifier)
|
||||
{
|
||||
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
||||
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.RemoveAsync(Keys_RememberedOrgIdentifier);
|
||||
await _stateService.SetRememberedOrgIdentifierAsync(null);
|
||||
}
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
if (response.TwoFactor)
|
||||
@@ -193,8 +187,6 @@ 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,6 +1,4 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -8,22 +6,18 @@ 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,6 +1,4 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
@@ -10,14 +8,11 @@ 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;
|
||||
@@ -26,7 +21,6 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
_vm.OrgIdentifier = orgIdentifier;
|
||||
|
||||
@@ -7,7 +7,6 @@ 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;
|
||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
||||
private readonly IApiService _apiService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly II18nService _i18nService;
|
||||
@@ -40,7 +39,7 @@ namespace Bit.App.Pages
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
@@ -159,7 +158,7 @@ namespace Bit.App.Pages
|
||||
|
||||
var kdf = KdfType.PBKDF2_SHA256;
|
||||
var kdfIterations = 100000;
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var email = await _stateService.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);
|
||||
@@ -196,8 +195,8 @@ namespace Bit.App.Pages
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||
// Set Password and relevant information
|
||||
await _apiService.SetPasswordAsync(request);
|
||||
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
||||
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
||||
await _stateService.SetKdfTypeAsync(kdf);
|
||||
await _stateService.SetKdfIterationsAsync(kdfIterations);
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||
@@ -216,7 +215,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
ResetPasswordKey = encryptedKey.EncryptedString
|
||||
};
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
// Enroll user
|
||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||
}
|
||||
@@ -289,7 +288,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||
{
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
List<string> userInput = null;
|
||||
var atPosition = email.IndexOf('@');
|
||||
if (atPosition > -1)
|
||||
|
||||
@@ -14,8 +14,6 @@ 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;
|
||||
@@ -30,10 +28,8 @@ 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,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
@@ -23,7 +22,6 @@ 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;
|
||||
@@ -43,7 +41,6 @@ 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");
|
||||
@@ -303,8 +300,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
TwoFactorAuthSuccessAction?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ 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 _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
|
||||
// Create new key and hash new password
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
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;
|
||||
@@ -13,8 +14,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class BaseContentPage : ContentPage
|
||||
{
|
||||
private IStorageService _storageService;
|
||||
private IStateService _stateService;
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private IMessagingService _messagingService;
|
||||
|
||||
protected int ShowModalAnimationDelay = 400;
|
||||
protected int ShowPageAnimationDelay = 100;
|
||||
@@ -30,10 +32,10 @@ namespace Bit.App.Pages
|
||||
|
||||
public DateTime? LastPageAction { get; set; }
|
||||
|
||||
protected override void OnAppearing()
|
||||
protected async override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
SaveActivity();
|
||||
await SaveActivity();
|
||||
}
|
||||
|
||||
public bool DoOnce(Action action = null, int milliseconds = 1000)
|
||||
@@ -106,22 +108,121 @@ 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 (_storageService == null)
|
||||
if (_stateService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
}
|
||||
if (_deviceActionService == null)
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
}
|
||||
if (_messagingService == null)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveActivity()
|
||||
private async Task SaveActivity()
|
||||
{
|
||||
SetServices();
|
||||
_storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -6,6 +7,7 @@ namespace Bit.App.Pages
|
||||
public abstract class BaseViewModel : ExtendedViewModel
|
||||
{
|
||||
private string _pageTitle = string.Empty;
|
||||
private AvatarImageSource _avatar;
|
||||
|
||||
public string PageTitle
|
||||
{
|
||||
@@ -13,6 +15,12 @@ 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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
|
||||
private bool _showNoData;
|
||||
|
||||
@@ -20,6 +21,7 @@ namespace Bit.App.Pages
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||
"passwordGenerationService");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
|
||||
PageTitle = AppResources.PasswordHistory;
|
||||
History = new ExtendedObservableCollection<GeneratedPasswordHistory>();
|
||||
@@ -51,7 +53,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async void CopyAsync(GeneratedPasswordHistory ph)
|
||||
{
|
||||
await _platformUtilsService.CopyToClipboardAsync(ph.Password);
|
||||
await _clipboardService.CopyTextAsync(ph.Password);
|
||||
_platformUtilsService.ShowToast("info", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
|
||||
private PasswordGenerationOptions _options;
|
||||
private PasswordGeneratorPolicyOptions _enforcedPolicyOptions;
|
||||
@@ -38,6 +39,8 @@ namespace Bit.App.Pages
|
||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||
"passwordGenerationService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
|
||||
PageTitle = AppResources.PasswordGenerator;
|
||||
TypeOptions = new List<string> { AppResources.Password, AppResources.Passphrase };
|
||||
}
|
||||
@@ -305,7 +308,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task CopyAsync()
|
||||
{
|
||||
await _platformUtilsService.CopyToClipboardAsync(Password);
|
||||
await _clipboardService.CopyTextAsync(Password);
|
||||
_platformUtilsService.ShowToast("success", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
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");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_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 _userService.CanAccessPremiumAsync();
|
||||
_emailVerified = await _userService.GetEmailVerifiedAsync();
|
||||
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_emailVerified = await _stateService.GetEmailVerifiedAsync();
|
||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
||||
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
||||
|
||||
@@ -32,21 +32,19 @@ namespace Bit.App.Pages
|
||||
|
||||
private readonly ISendService _sendService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
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");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_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;
|
||||
@@ -116,7 +114,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
@@ -125,7 +123,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
||||
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||
{
|
||||
SyncRefreshing = true;
|
||||
await _syncService.FullSyncAsync(false);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -11,7 +10,7 @@ namespace Bit.App.Pages
|
||||
public class AutofillServicesPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly MobileI18nService _i18nService;
|
||||
|
||||
private bool _autofillServiceToggled;
|
||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
||||
public AutofillServicesPageViewModel()
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
||||
PageTitle = AppResources.AutofillServices;
|
||||
}
|
||||
@@ -152,7 +151,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
InlineAutofillToggled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
||||
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
@@ -202,7 +201,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.InlineAutofillEnabledKey, InlineAutofillToggled);
|
||||
await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class ExtensionPageViewModel : BaseViewModel
|
||||
{
|
||||
private const string StartedKey = "appExtensionStarted";
|
||||
private const string ActivatedKey = "appExtensionActivated";
|
||||
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
private bool _started;
|
||||
private bool _activated;
|
||||
@@ -20,8 +16,7 @@ namespace Bit.App.Pages
|
||||
public ExtensionPageViewModel()
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
PageTitle = AppResources.AppExtension;
|
||||
}
|
||||
|
||||
@@ -52,8 +47,8 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
var started = await _storageService.GetAsync<bool?>(StartedKey);
|
||||
var activated = await _storageService.GetAsync<bool?>(ActivatedKey);
|
||||
var started = await _stateService.GetAppExtensionStartedAsync();
|
||||
var activated = await _stateService.GetAppExtensionActivatedAsync();
|
||||
Started = started.GetValueOrDefault();
|
||||
Activated = activated.GetValueOrDefault();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
@@ -13,9 +12,6 @@ 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;
|
||||
@@ -34,9 +30,6 @@ 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");
|
||||
@@ -166,19 +159,17 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
AutofillDisableSavePrompt = (await _storageService.GetAsync<bool?>(
|
||||
Constants.AutofillDisableSavePromptKey)).GetValueOrDefault();
|
||||
var blacklistedUrisList = await _storageService.GetAsync<List<string>>(
|
||||
Constants.AutofillBlacklistedUrisKey);
|
||||
AutofillDisableSavePrompt = (await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
|
||||
var blacklistedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
|
||||
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
|
||||
DisableFavicon = (await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey)).GetValueOrDefault();
|
||||
var theme = await _storageService.GetAsync<string>(Constants.ThemeKey);
|
||||
DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
var theme = await _stateService.GetThemeAsync();
|
||||
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
|
||||
var defaultUriMatch = await _storageService.GetAsync<int?>(Constants.DefaultUriMatch);
|
||||
var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
|
||||
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
|
||||
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
|
||||
var clearClipboard = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
||||
var clearClipboard = await _stateService.GetClearClipboardAsync();
|
||||
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
|
||||
_inited = true;
|
||||
}
|
||||
@@ -187,7 +178,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.DisableAutoTotpCopyKey, DisableAutoTotpCopy);
|
||||
await _stateService.SetDisableAutoTotpCopyAsync(DisableAutoTotpCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +186,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
||||
await _stateService.SetDisableFaviconAsync(DisableFavicon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,8 +194,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited && ClearClipboardSelectedIndex > -1)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.ClearClipboardKey,
|
||||
ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||
await _stateService.SetClearClipboardAsync(ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,8 +203,8 @@ namespace Bit.App.Pages
|
||||
if (_inited && ThemeSelectedIndex > -1)
|
||||
{
|
||||
var theme = ThemeOptions[ThemeSelectedIndex].Key;
|
||||
await _storageService.SaveAsync(Constants.ThemeKey, theme);
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Application.Current.Resources);
|
||||
await _stateService.SetThemeAsync(theme);
|
||||
ThemeManager.SetTheme(Application.Current.Resources);
|
||||
_messagingService.Send("updatedTheme");
|
||||
}
|
||||
}
|
||||
@@ -224,8 +213,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited && UriMatchSelectedIndex > -1)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.DefaultUriMatch,
|
||||
(int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||
await _stateService.SetDefaultUriMatchAsync((int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +221,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.AutofillDisableSavePromptKey, AutofillDisableSavePrompt);
|
||||
await _stateService.SetAutofillDisableSavePromptAsync(AutofillDisableSavePrompt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +231,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
|
||||
{
|
||||
await _storageService.RemoveAsync(Constants.AutofillBlacklistedUrisKey);
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(null);
|
||||
AutofillBlacklistedUris = null;
|
||||
return;
|
||||
}
|
||||
@@ -265,7 +253,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
urisList.Add(cleanedUri);
|
||||
}
|
||||
await _storageService.SaveAsync(Constants.AutofillBlacklistedUrisKey, urisList);
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
|
||||
AutofillBlacklistedUris = string.Join(", ", urisList);
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.ComponentModel;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Pages.Accounts;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -134,6 +135,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await _vm.LogOutAsync();
|
||||
}
|
||||
else if (item.Name == AppResources.DeleteAccount)
|
||||
{
|
||||
await Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage()));
|
||||
}
|
||||
else if (item.Name == AppResources.LockNow)
|
||||
{
|
||||
await _vm.LockAsync();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
@@ -17,17 +16,17 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
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;
|
||||
private readonly ILocalizeService _localizeService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
|
||||
private const int CustomVaultTimeoutValue = -100;
|
||||
|
||||
@@ -67,17 +66,17 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_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");
|
||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
|
||||
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
|
||||
PageTitle = AppResources.Settings;
|
||||
@@ -109,7 +108,7 @@ namespace Bit.App.Pages
|
||||
|
||||
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
|
||||
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
|
||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey) ?? "lock";
|
||||
var action = await _stateService.GetVaultTimeoutActionAsync() ?? "lock";
|
||||
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
|
||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
_pin = pinSet.Item1 || pinSet.Item2;
|
||||
@@ -135,7 +134,7 @@ namespace Bit.App.Pages
|
||||
AppResources.Close);
|
||||
if (copy)
|
||||
{
|
||||
await _platformUtilsService.CopyToClipboardAsync(debugText);
|
||||
await _clipboardService.CopyTextAsync(debugText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ namespace Bit.App.Pages
|
||||
List<string> fingerprint;
|
||||
try
|
||||
{
|
||||
fingerprint = await _cryptoService.GetFingerprintAsync(await _userService.GetUserIdAsync());
|
||||
fingerprint = await _cryptoService.GetFingerprintAsync(await _stateService.GetActiveUserIdAsync());
|
||||
}
|
||||
catch (Exception e) when (e.Message == "No public key available.")
|
||||
{
|
||||
@@ -327,9 +326,9 @@ namespace Bit.App.Pages
|
||||
AppResources.Yes, AppResources.No);
|
||||
}
|
||||
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
||||
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
||||
kdfIterations.GetValueOrDefault(5000));
|
||||
@@ -339,12 +338,12 @@ namespace Bit.App.Pages
|
||||
if (masterPassOnRestart)
|
||||
{
|
||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString);
|
||||
_vaultTimeoutService.PinProtectedKey = pinProtectedKey;
|
||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||
await _stateService.SetPinProtectedCachedAsync(pinProtectedKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString);
|
||||
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -379,13 +378,13 @@ namespace Bit.App.Pages
|
||||
if (_biometric)
|
||||
{
|
||||
await _biometricService.SetupBiometricAsync();
|
||||
await _storageService.SaveAsync(Constants.BiometricUnlockKey, true);
|
||||
await _stateService.SetBiometricUnlockAsync(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.RemoveAsync(Constants.BiometricUnlockKey);
|
||||
await _stateService.SetBiometricUnlockAsync(null);
|
||||
}
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
_stateService.BiometricLocked = false;
|
||||
await _cryptoService.ToggleKeyAsync();
|
||||
BuildList();
|
||||
}
|
||||
@@ -490,7 +489,8 @@ namespace Bit.App.Pages
|
||||
new SettingsPageListItem { Name = AppResources.Options },
|
||||
new SettingsPageListItem { Name = AppResources.About },
|
||||
new SettingsPageListItem { Name = AppResources.HelpAndFeedback },
|
||||
new SettingsPageListItem { Name = AppResources.RateTheApp }
|
||||
new SettingsPageListItem { Name = AppResources.RateTheApp },
|
||||
new SettingsPageListItem { Name = AppResources.DeleteAccount }
|
||||
};
|
||||
GroupedItems.ResetWithRange(new List<SettingsPageListGroup>
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -12,7 +11,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly ILocalizeService _localizeService;
|
||||
|
||||
@@ -24,7 +23,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||
|
||||
@@ -52,7 +51,7 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await SetLastSyncAsync();
|
||||
EnableSyncOnRefresh = await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey);
|
||||
EnableSyncOnRefresh = await _stateService.GetSyncOnRefreshAsync();
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.SyncOnRefreshKey, _syncOnRefresh);
|
||||
await _stateService.SetSyncOnRefreshAsync(_syncOnRefresh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -45,7 +46,7 @@ namespace Bit.App.Pages
|
||||
var settingsPage = new NavigationPage(new SettingsPage(this))
|
||||
{
|
||||
Title = AppResources.Settings,
|
||||
IconImageSource = "cog.png"
|
||||
IconImageSource = "cog_settings.png"
|
||||
};
|
||||
Children.Add(settingsPage);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
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;
|
||||
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
|
||||
public partial class AddEditPage : BaseContentPage
|
||||
{
|
||||
private readonly AppOptions _appOptions;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
@@ -38,7 +37,7 @@ namespace Bit.App.Pages
|
||||
bool cloneMode = false,
|
||||
ViewPage viewPage = null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
@@ -333,10 +332,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var addLoginShown = await _storageService.GetAsync<bool?>(Constants.AddSitePromptShownKey);
|
||||
var addLoginShown = await _stateService.GetAddSitePromptShownAsync();
|
||||
if (_vm.Cipher.Type == CipherType.Login && !_fromAutofill && !addLoginShown.GetValueOrDefault())
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.AddSitePromptShownKey, true);
|
||||
await _stateService.SetAddSitePromptShownAsync(true);
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
if (_deviceActionService.SystemMajorVersion() < 12)
|
||||
|
||||
@@ -22,7 +22,8 @@ namespace Bit.App.Pages
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
@@ -70,7 +71,8 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
@@ -302,9 +304,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task<bool> LoadAsync(AppOptions appOptions = null)
|
||||
{
|
||||
var myEmail = await _userService.GetEmailAsync();
|
||||
var myEmail = await _stateService.GetEmailAsync();
|
||||
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
|
||||
var orgs = await _userService.GetAllOrganizationAsync();
|
||||
var orgs = await _organizationService.GetAllAsync();
|
||||
foreach (var org in orgs.OrderBy(o => o.Name))
|
||||
{
|
||||
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private CipherView _cipher;
|
||||
private Cipher _cipherDomain;
|
||||
@@ -32,7 +33,8 @@ namespace Bit.App.Pages
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
Attachments = new ExtendedObservableCollection<AttachmentView>();
|
||||
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
|
||||
PageTitle = AppResources.Attachments;
|
||||
@@ -64,7 +66,7 @@ namespace Bit.App.Pages
|
||||
Cipher = await _cipherDomain.DecryptAsync();
|
||||
LoadAttachments();
|
||||
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
||||
var canAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
||||
if (!_canAccessAttachments)
|
||||
{
|
||||
@@ -135,6 +137,11 @@ 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,10 +8,8 @@ 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;
|
||||
|
||||
@@ -88,8 +86,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
ShowList = false;
|
||||
var groupedItems = new List<GroupingsPageListGroup>();
|
||||
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
|
||||
|
||||
@@ -76,8 +76,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
||||
{
|
||||
Search((Page as CiphersPage).SearchBar.Text, 200);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<?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">
|
||||
@@ -15,6 +17,15 @@
|
||||
</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}" />
|
||||
@@ -135,6 +146,8 @@
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<!-- Android FAB -->
|
||||
<Button
|
||||
x:Name="_fab"
|
||||
Image="plus.png"
|
||||
@@ -148,6 +161,40 @@
|
||||
<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,7 +1,5 @@
|
||||
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;
|
||||
@@ -9,6 +7,7 @@ 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
|
||||
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
@@ -37,7 +36,7 @@ namespace Bit.App.Pages
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
@@ -70,6 +69,10 @@ namespace Bit.App.Pages
|
||||
_absLayout.Children.Remove(_fab);
|
||||
ToolbarItems.Remove(_addItem);
|
||||
}
|
||||
if (!mainPage)
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
protected async override void OnAppearing()
|
||||
@@ -80,6 +83,11 @@ namespace Bit.App.Pages
|
||||
IsBusy = true;
|
||||
}
|
||||
|
||||
if (_vm.MainPage)
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
|
||||
_broadcasterService.Subscribe(_pageName, async (message) =>
|
||||
{
|
||||
if (message.Command == "syncStarted")
|
||||
@@ -100,7 +108,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
});
|
||||
|
||||
var migratedFromV1 = await _storageService.GetAsync<bool?>(Constants.MigratedFromV1);
|
||||
var migratedFromV1 = await _stateService.GetMigratedFromV1Async();
|
||||
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
||||
{
|
||||
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
||||
@@ -128,10 +136,10 @@ namespace Bit.App.Pages
|
||||
!_vm.HasCiphers &&
|
||||
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
|
||||
{
|
||||
var triedV1ReSync = await _storageService.GetAsync<bool?>(Constants.TriedV1Resync);
|
||||
var triedV1ReSync = await _stateService.GetTriedV1ResyncAsync();
|
||||
if (!triedV1ReSync.GetValueOrDefault())
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.TriedV1Resync, true);
|
||||
await _stateService.SetTriedV1ResyncAsync(true);
|
||||
await _syncService.FullSyncAsync(true);
|
||||
}
|
||||
}
|
||||
@@ -145,14 +153,14 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
// Push registration
|
||||
var lastPushRegistration = await _storageService.GetAsync<DateTime?>(Constants.PushLastRegistrationDateKey);
|
||||
var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
|
||||
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var pushPromptShow = await _storageService.GetAsync<bool?>(Constants.PushInitialPromptShownKey);
|
||||
var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
|
||||
if (!pushPromptShow.GetValueOrDefault(false))
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.PushInitialPromptShownKey, true);
|
||||
await _stateService.SetPushInitialPromptShownAsync(true);
|
||||
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
|
||||
AppResources.OkGotIt);
|
||||
}
|
||||
@@ -173,8 +181,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (migratedFromV1.GetValueOrDefault())
|
||||
{
|
||||
var migratedFromV1AutofillPromptShown = await _storageService.GetAsync<bool?>(
|
||||
Constants.MigratedFromV1AutofillPromptShown);
|
||||
var migratedFromV1AutofillPromptShown =
|
||||
await _stateService.GetMigratedFromV1AutofillPromptShownAsync();
|
||||
if (!migratedFromV1AutofillPromptShown.GetValueOrDefault())
|
||||
{
|
||||
await DisplayAlert(AppResources.Autofill,
|
||||
@@ -182,7 +190,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
}
|
||||
await _storageService.SaveAsync(Constants.MigratedFromV1AutofillPromptShown, true);
|
||||
await _stateService.SetMigratedFromV1AutofillPromptShownAsync(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,5 +292,24 @@ 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,7 +1,6 @@
|
||||
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;
|
||||
@@ -11,6 +10,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -39,13 +39,11 @@ 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()
|
||||
@@ -54,13 +52,11 @@ 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;
|
||||
@@ -80,7 +76,6 @@ 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; }
|
||||
@@ -139,6 +134,11 @@ 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 _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
@@ -159,7 +159,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
||||
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||
{
|
||||
SyncRefreshing = true;
|
||||
await _syncService.FullSyncAsync(false);
|
||||
@@ -175,8 +175,7 @@ namespace Bit.App.Pages
|
||||
var groupedItems = new List<GroupingsPageListGroup>();
|
||||
var page = Page as GroupingsPage;
|
||||
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
try
|
||||
{
|
||||
await LoadDataAsync();
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
|
||||
private bool _showNoData;
|
||||
|
||||
@@ -19,6 +20,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
|
||||
PageTitle = AppResources.PasswordHistory;
|
||||
History = new ExtendedObservableCollection<PasswordHistoryView>();
|
||||
@@ -45,7 +47,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async void CopyAsync(PasswordHistoryView ph)
|
||||
{
|
||||
await _platformUtilsService.CopyToClipboardAsync(ph.Password);
|
||||
await _clipboardService.CopyTextAsync(ph.Password);
|
||||
_platformUtilsService.ShowToast("info", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
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");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||
_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 _userService.GetAllOrganizationAsync();
|
||||
var orgs = await _organizationService.GetAllAsync();
|
||||
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 _userService.GetOrganizationAsync(OrganizationId)).Name);
|
||||
(await _organizationService.GetAsync(OrganizationId)).Name);
|
||||
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
@@ -6,10 +10,6 @@ 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.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -18,7 +18,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ITotpService _totpService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuditService _auditService;
|
||||
@@ -26,6 +26,8 @@ namespace Bit.App.Pages
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||
private readonly ILocalizeService _localizeService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
|
||||
private CipherView _cipher;
|
||||
private List<ViewPageFieldViewModel> _fields;
|
||||
private bool _canAccessPremium;
|
||||
@@ -46,7 +48,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||
@@ -54,6 +56,8 @@ namespace Bit.App.Pages
|
||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
|
||||
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
|
||||
CopyUriCommand = new Command<LoginUriView>(CopyUri);
|
||||
CopyFieldCommand = new Command<FieldView>(CopyField);
|
||||
@@ -244,7 +248,7 @@ namespace Bit.App.Pages
|
||||
return false;
|
||||
}
|
||||
Cipher = await cipher.DecryptAsync();
|
||||
CanAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
CanAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
||||
|
||||
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
||||
@@ -653,7 +657,7 @@ namespace Bit.App.Pages
|
||||
|
||||
if (text != null)
|
||||
{
|
||||
await _platformUtilsService.CopyToClipboardAsync(text);
|
||||
await _clipboardService.CopyTextAsync(text);
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
|
||||
|
||||
30
src/App/Resources/AppResources.Designer.cs
generated
30
src/App/Resources/AppResources.Designer.cs
generated
@@ -3719,6 +3719,36 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
public static string DeleteAccount {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteAccount", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string DeletingYourAccountIsPermanent {
|
||||
get {
|
||||
return ResourceManager.GetString("DeletingYourAccountIsPermanent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string DeleteAccountExplanation {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteAccountExplanation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string DeletingYourAccount {
|
||||
get {
|
||||
return ResourceManager.GetString("DeletingYourAccount", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string YourAccountHasBeenPermanentlyDeleted {
|
||||
get {
|
||||
return ResourceManager.GetString("YourAccountHasBeenPermanentlyDeleted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string InvalidVerificationCode {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidVerificationCode", resourceCulture);
|
||||
|
||||
@@ -1060,7 +1060,7 @@
|
||||
<value>Van</value>
|
||||
</data>
|
||||
<data name="FullName" xml:space="preserve">
|
||||
<value>Full Name</value>
|
||||
<value>Volle naam</value>
|
||||
</data>
|
||||
<data name="LicenseNumber" xml:space="preserve">
|
||||
<value>Lisensienommer</value>
|
||||
@@ -1187,7 +1187,7 @@
|
||||
<value>Versteek</value>
|
||||
</data>
|
||||
<data name="FieldTypeLinked" xml:space="preserve">
|
||||
<value>Linked</value>
|
||||
<value>Gekoppel</value>
|
||||
</data>
|
||||
<data name="FieldTypeText" xml:space="preserve">
|
||||
<value>Teks</value>
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Ontgrendel</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Ontgrendel kluis</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minute</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>U kluis is vergrendel. Verifieer u PIN-kode om voort te gaan.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>U kluis is vergrendel. Bevestig u identiteit om voort te gaan.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Donker</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Voer u hoofwagwoord in om u kluisdata uit te stuur.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Stuur ’n bevestigingskode na u e-pos</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Kode verstuur!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Bevestig u identiteit om voort te gaan.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Hierdie uitstuur bevat u kluisdata in ’n ongeënkripteerde formaat. U behoort dit nie oor onbeveiligde kanale (soos e-pos) te bewaar of verstuur nie. Skrap dit sodra u dit klaar gebruik het.</value>
|
||||
@@ -2036,19 +2048,19 @@
|
||||
<value>Kan nie tans u wagwoord bywerk nie</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||
<value>Remove Master Password</value>
|
||||
<value>Verwyder hoofwagwoord</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||
<value>{0} is using SSO with customer-managed encryption. Continuing will remove your Master Password from your account and require SSO to login.</value>
|
||||
<value>{0} gebruik SSO met klantbestuurde enkripsie. Deur voort te gaan word u hoofwagwoord van u rekening verwyder en word SSO vereis om aan te teken.</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||
<value>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||
<value>Indien u nie u hoofwagwoord wil verwyder nie, kan u hierdie organisasie verlaat.</value>
|
||||
</data>
|
||||
<data name="LeaveOrganization" xml:space="preserve">
|
||||
<value>Leave Organization</value>
|
||||
<value>Verlaat organisasie</value>
|
||||
</data>
|
||||
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||
<value>Leave {0}?</value>
|
||||
<value>Verlaat {0}?</value>
|
||||
</data>
|
||||
<data name="Fido2Title" xml:space="preserve">
|
||||
<value>FIDO2 WebAuthn</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Een of meer organisasiebeleide verhoed u om u persoonlike kluis uit te stuur.</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Skrap rekening</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Die skrap van u rekening is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>U rekening en alle bybehorende data sal geskrap en onherwinbaar word. Is u seker u wil voortgaan?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>U rekening word geskrap</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>U rekening is permanent geskrap</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Ongeldige bevestigingskode.</value>
|
||||
</data>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Verstuur kode</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Verstuur</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Kilidi aç</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Anbar kilidini aç</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 dəqiqə</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Anbarınız kilidlənib. Davam etmək üçün PIN kodunuzu təsdiqləyin.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Anbarınız kilidlənib. Davam etmək üçün kimliyinizi təsdiqləyin.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Tünd</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Anbar verilənlərinizi ixrac etmək üçün ana parolunuzu daxil edin.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Anbar verilənlərinizi ixrac etmək üçün təsdiqləmə kodunu daxil edin.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>E-poçtunuza bir təsdiqləmə kodu göndərin</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Kod göndərildi!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Davam etmək üçün kimliyinizi təsdiqləyin.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Bu ixrac faylındakı anbar verilənləriniz şifrələnməmiş formatdadır. İxrac edilən faylı saxlamamalı və etibarsız yollarla (e-poçt kimi) göndərməməlisiniz. Bu faylı işiniz bitdikdən sonra dərhal silin.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Bir və ya daha çox təşkilat siyasəti, fərdi anbarınızı ixrac etməyinizin qarşısını alır.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Hesabı sil</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Hesabınız birdəfəlik silinəcək</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Hesabınız və əlaqəli bütün verilənlər silinəcək və bərpa oluna bilməyəcək. Davam etmək istədiyinizə əminsiniz?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Hesabınız silinir</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Hesabınız birdəfəlik silindi</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Etibarsız təsdiqləmə kodu</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Tək istifadəlik parol tələb et</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Kod göndər</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Göndərilir</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Разблакіраваць</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Unlock Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 хвілін</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Ваша сховішча заблакіравана. Каб працягнуць, увядзіце PIN-код.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Цёмная</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Увядзіце ваш асноўны пароль для экспарту даных са сховішча.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Send a verification code to your email</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Code Sent!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirm your identity to continue.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Экспартуемы файл утрымлівае даныя вашага сховішча ў незашыфраваным фармаце. Яго не варта захоўваць ці адпраўляць па небяспечным каналам (напрыклад, па электроннай пошце). Выдаліце яго адразу пасля выкарыстання.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Deleting your account is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<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>Deleting your account</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Your account has been permanently deleted</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Send Code</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Sending</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Отключване</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Отключване на трезора</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 минути</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Трезорът е заключен. Въведете своя ПИН, за да продължите.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Трезорът е заключен. Потвърдете самоличността си, за да продължите.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Тъмен</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Въведете главната парола, за да изнесете данните.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Въведете кода за потвърждаване, за да изнесете данните от трезора си.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Изпращане на код за потвърждаване до Вашата ел. поща</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Кодът е изпратен!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Потвърдете самоличността си, за да продължите.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Данните от трезора ви ще се изнесат в незащитен формат. Не го пращайте по незащитени канали като е-поща. Изтрийте файла незабавно след като свършите работата си с него.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Една или повече от настройките на организацията Ви не позволяват да изнасяте личния си трезор.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Изтриване на регистрацията</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Изтриването на регистрацията е необратимо</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Регистрацията Ви и всички свързани с нея данни ще бъдат изтрити и няма да могат да бъдат възстановени. Наистина ли искате да продължите?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Изтриване на регистрацията Ви</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Регистрацията Ви беше изтрита необратимо</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Грешен код за потвърждаване</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Заявка за еднократна парола</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Изпращане на кода</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Изпращане…</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Unlock</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Unlock Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minutes</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Your vault is locked. Verify your PIN code to continue.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Dark</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>আপনার ভল্ট তথ্য রফতানি করতে আপনার প্রধান পাসওয়ার্ডটি দিন।</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Send a verification code to your email</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Code Sent!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirm your identity to continue.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>এই রফতানীতে একটি বিনা-এনক্রিপ্টেড করা বিন্যাসে আপনার ভল্ট তথ্য রয়েছে। আপনার রফতানিকৃত হওয়া ফাইল নিরাপত্তাহীন চ্যানেলগুলির মাধ্যমে (যেমন ইমেল) সংরক্ষণ বা প্রেরণ করা উচিত নয়। আপনি এটি ব্যবহার করে কাজ শেষ করার পর সাথে সাথে মুছে ফেলুন।</value>
|
||||
@@ -2081,10 +2093,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Deleting your account is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<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>Deleting your account</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Your account has been permanently deleted</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Send Code</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Sending</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -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>Nevažeća glavna lozinka. Pokušajte ponovo.</value>
|
||||
<value>Glavna lozinka neispravna. 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>Posjeti naš web</value>
|
||||
<value>Posjetite našu Web Stranicu </value>
|
||||
</data>
|
||||
<data name="VisitOurWebsiteDescription" xml:space="preserve">
|
||||
<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>
|
||||
<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>
|
||||
</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>Izbegavanja nesigurnih slova</value>
|
||||
<value>Izbjegavaj dvosmislene znakove</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šćenju dodatka Bitwarden aplikacije u „Podešavanja“.</value>
|
||||
<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>
|
||||
</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 veb trezoru. Da li želite da posetite veb stranicu sada?</value>
|
||||
<value>Možete da promenite svoju adresu e-pošte na bitwarden.com web trezoru. Da li želite da posjetite Web 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 promenite svoju glavnu lozinku na bitwarden.com veb trezoru. Da li želite da posetite veb stranicu sada?</value>
|
||||
<value>Možete da promjenite svoju glavnu lozinku na bitwarden.com web trezoru. Da li želite da posjetite web stranicu sada?</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Zatvorite</value>
|
||||
<value>Zatvori</value>
|
||||
</data>
|
||||
<data name="Continue" xml:space="preserve">
|
||||
<value>Nastavite</value>
|
||||
<value>Nastavi</value>
|
||||
</data>
|
||||
<data name="CreateAccount" xml:space="preserve">
|
||||
<value>Napravite nalog</value>
|
||||
<value>Napravi račun</value>
|
||||
</data>
|
||||
<data name="CreatingAccount" xml:space="preserve">
|
||||
<value>Pravljenje naloga...</value>
|
||||
<value>Pravljenje računa...</value>
|
||||
<comment>Message shown when interacting with the server</comment>
|
||||
</data>
|
||||
<data name="EditItem" xml:space="preserve">
|
||||
<value>Uredite stavku</value>
|
||||
<value>Uredi stavku</value>
|
||||
</data>
|
||||
<data name="EnableAutomaticSyncing" xml:space="preserve">
|
||||
<value>Omogućite automatsku sinhronizaciju</value>
|
||||
<value>Omogući automatsku sinhronizaciju</value>
|
||||
</data>
|
||||
<data name="EnterEmailForHint" xml:space="preserve">
|
||||
<value>Unesite adresu e-pošte svog naloga da biste dobili nagoveštaj glavne lozinke.</value>
|
||||
<value>Unesite E-Mail adresu Vašeg računa da biste dobili nagovještaj o mogućoj glavnoj lozinki.</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 deljenje (savet: desni donji red menija).</value>
|
||||
<value>U Safari-ju pronađite Bitwarden pomoću ikone za dijeljenje (savjet: 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 instant pristup Vašim lozinkama!</value>
|
||||
<value>Dobijte trenutni 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 stavke na veliko na bitwarden.com veb trezoru. Da li želite da posetite veb stranicu sada?</value>
|
||||
<value>Možete da uvezete sve vaše lozinke preko bitwarden.com web trezora. Da li želite da posjetite web stranicu sada?</value>
|
||||
</data>
|
||||
<data name="ImportItemsDescription" xml:space="preserve">
|
||||
<value>Uvezite stavke na veliko iz drugih aplikacija za upravljanje lozinkom.</value>
|
||||
<value>Uvezite sve lozinke i datoteke iz drugih aplikacija za upravljanje lozinkom.</value>
|
||||
</data>
|
||||
<data name="LastSync" xml:space="preserve">
|
||||
<value>Poslednja sinhronizacija:</value>
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Otključaj</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Otključaj trezor</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minuta</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<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>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Tamno</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Unesi glavnu lozinku za izvoz podataka iz trezora.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Unesite verifikacijski kod za izvoz podataka iz trezora.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Pošalji verifikacijski kod na E-Mail</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Kod poslan!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Potvrdite lozinku za nastavak.</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>
|
||||
@@ -1802,64 +1814,64 @@
|
||||
<value>Koristi preklapanje</value>
|
||||
</data>
|
||||
<data name="DrawOverDescription" xml:space="preserve">
|
||||
<value>When enabled, allows the Bitwarden Accessibility Service to display a popup when login fields are selected.</value>
|
||||
<value>Kada je uključeno, omogućuje usluzi Bitwarden prikaz iskočnog okvira prilikom odabira polja za prijavu.</value>
|
||||
</data>
|
||||
<data name="DrawOverDescription2" xml:space="preserve">
|
||||
<value>If enabled, the Bitwarden Accessibility Service will display a popup when login fields are selected to assist with auto-filling your logins.</value>
|
||||
<value>Ako je uključena, usluga Bitwarden prikazati će iskočni okvir prilikom odabira polja za prijavu kao pomoć pri auto-ispuni.</value>
|
||||
</data>
|
||||
<data name="DrawOverDescription3" xml:space="preserve">
|
||||
<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>
|
||||
<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>
|
||||
</data>
|
||||
<data name="PersonalOwnershipSubmitError" xml:space="preserve">
|
||||
<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>
|
||||
<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>
|
||||
</data>
|
||||
<data name="PersonalOwnershipPolicyInEffect" xml:space="preserve">
|
||||
<value>An organization policy is affecting your ownership options.</value>
|
||||
<value>Pravila organizacije utječu na tvoje mogućnosti vlasništva.</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>All Sends</value>
|
||||
<value>Svi Send-ovi</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>Sends</value>
|
||||
<value>Send-ovi</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>A friendly name to describe this Send.</value>
|
||||
<value>Nadimak za ovaj 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>Text</value>
|
||||
<value>Tekst</value>
|
||||
</data>
|
||||
<data name="TypeTextInfo" xml:space="preserve">
|
||||
<value>The text you want to send.</value>
|
||||
<value>Tekst kojeg želiš poslati.</value>
|
||||
</data>
|
||||
<data name="HideTextByDefault" xml:space="preserve">
|
||||
<value>When accessing the Send, hide the text by default</value>
|
||||
<value>Zadano sakrij tekst pri pristupanju Send-u</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>File</value>
|
||||
<value>Datoteka</value>
|
||||
</data>
|
||||
<data name="TypeFileInfo" xml:space="preserve">
|
||||
<value>The file you want to send.</value>
|
||||
<value>Datoteka koju želiš poslati</value>
|
||||
</data>
|
||||
<data name="DeletionDate" xml:space="preserve">
|
||||
<value>Deletion Date</value>
|
||||
<value>Datum brisanja</value>
|
||||
</data>
|
||||
<data name="DeletionTime" xml:space="preserve">
|
||||
<value>Deletion Time</value>
|
||||
<value>Vrijeme brisanja</value>
|
||||
</data>
|
||||
<data name="DeletionDateInfo" xml:space="preserve">
|
||||
<value>The Send will be permanently deleted on the specified date and time.</value>
|
||||
<value>Ovaj Send će biti trajno izbrisan na odabrani datum i vrijeme.</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>Pending deletion</value>
|
||||
<value>Brisanje na čekanju </value>
|
||||
</data>
|
||||
<data name="ExpirationDate" xml:space="preserve">
|
||||
<value>Rok upotrebe</value>
|
||||
@@ -1868,115 +1880,115 @@
|
||||
<value>Vrijeme roka upotrebe</value>
|
||||
</data>
|
||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||
<value>If set, access to this Send will expire on the specified date and time.</value>
|
||||
<value>Ako je postavljeno, pristup ovom Sendu ističe na navedeni datum i vrijeme.</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>Expired</value>
|
||||
<value>Isteklo</value>
|
||||
</data>
|
||||
<data name="MaximumAccessCount" xml:space="preserve">
|
||||
<value>Maximum Access Count</value>
|
||||
<value>Maksimalan broj pristupa</value>
|
||||
</data>
|
||||
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
||||
<value>If set, users will no longer be able to access this Send once the maximum access count is reached.</value>
|
||||
<value>Ako je određeno, ovom Send-u će se moći pristupiti samo ograničeni broj puta.</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>Max access count reached</value>
|
||||
<value>Dostignut najveći broj pristupanja</value>
|
||||
</data>
|
||||
<data name="CurrentAccessCount" xml:space="preserve">
|
||||
<value>Current Access Count</value>
|
||||
<value>Trenutni broj pristupa</value>
|
||||
</data>
|
||||
<data name="NewPassword" xml:space="preserve">
|
||||
<value>New Password</value>
|
||||
<value>Nova lozinka</value>
|
||||
</data>
|
||||
<data name="PasswordInfo" xml:space="preserve">
|
||||
<value>Optionally require a password for users to access this Send.</value>
|
||||
<value>Neobavezno zahtijevaj lozinku od korisnika za pristup ovom Send-u.</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>Remove Password</value>
|
||||
<value>Ukloni lozinku</value>
|
||||
</data>
|
||||
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
||||
<value>Are you sure you want to remove the password?</value>
|
||||
<value>Sigurno želiš ukloniti lozinku?</value>
|
||||
</data>
|
||||
<data name="RemovingSendPassword" xml:space="preserve">
|
||||
<value>Removing password</value>
|
||||
<value>Uklanjanje lozinke </value>
|
||||
</data>
|
||||
<data name="SendPasswordRemoved" xml:space="preserve">
|
||||
<value>Password has been removed.</value>
|
||||
<value>Lozinka je uklonjena.</value>
|
||||
</data>
|
||||
<data name="NotesInfo" xml:space="preserve">
|
||||
<value>Private notes about this Send.</value>
|
||||
<value>Privatne bilješke o ovom Send-u.</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>Disable this Send so that no one can access it.</value>
|
||||
<value>Onemogući ovaj Send da mu niko drugi ne može pristupiti.</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>There are no Sends in your account.</value>
|
||||
<value>Nema Send-ova na tvom računu.</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>Add a Send</value>
|
||||
<value>Dodaj 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>Copy Link</value>
|
||||
<value>Kopiraj link</value>
|
||||
</data>
|
||||
<data name="ShareLink" xml:space="preserve">
|
||||
<value>Share Link</value>
|
||||
<value>Podjeli link</value>
|
||||
</data>
|
||||
<data name="SendLink" xml:space="preserve">
|
||||
<value>Send link</value>
|
||||
<value>Link od Send-a</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>Search Sends</value>
|
||||
<value>Pretraži Send-ove</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>Edit Send</value>
|
||||
<value>Uredi 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>Add Send</value>
|
||||
<value>Dodaj 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>Are you sure you want to delete this Send?</value>
|
||||
<value>Sigurno želiš izbrisati ovaj 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 has been deleted.</value>
|
||||
<value>Send izbrisan.</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 updated.</value>
|
||||
<value>Send ažuriran.</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>New send created.</value>
|
||||
<value>Novi Send kreiran.</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 day</value>
|
||||
<value>1 dan</value>
|
||||
</data>
|
||||
<data name="TwoDays" xml:space="preserve">
|
||||
<value>2 days</value>
|
||||
<value>2 dana</value>
|
||||
</data>
|
||||
<data name="ThreeDays" xml:space="preserve">
|
||||
<value>3 days</value>
|
||||
<value>3 dana</value>
|
||||
</data>
|
||||
<data name="SevenDays" xml:space="preserve">
|
||||
<value>7 days</value>
|
||||
<value>7 dana</value>
|
||||
</data>
|
||||
<data name="ThirtyDays" xml:space="preserve">
|
||||
<value>30 days</value>
|
||||
<value>30 dana</value>
|
||||
</data>
|
||||
<data name="Custom" xml:space="preserve">
|
||||
<value>Custom</value>
|
||||
<value>Prilagođeno</value>
|
||||
</data>
|
||||
<data name="ShareOnSave" xml:space="preserve">
|
||||
<value>Podijeli Send nakon spremanja.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<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>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Brisanje tvog računa je nepovratno</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Tvoj račun i svi povezani podaci biti će nepovratno obrisani. Sigurno želiš nastaviti?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Brisanje vašeg računa</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Vaš račun je trajno obrisan.</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Neispravan verifikacijski kod</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Zahtijevaj jednokratnu lozinku</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Pošalji kod</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Slanje</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1060,7 +1060,7 @@
|
||||
<value>Cognoms</value>
|
||||
</data>
|
||||
<data name="FullName" xml:space="preserve">
|
||||
<value>Full Name</value>
|
||||
<value>Nom complet</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>Linked</value>
|
||||
<value>Enllaçat</value>
|
||||
</data>
|
||||
<data name="FieldTypeText" xml:space="preserve">
|
||||
<value>Text</value>
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Desbloqueja</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Desbloqueja la caixa forta</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minuts</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<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>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Fosc</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Introduïu la contrasenya mestra per exportar les dades de la caixa forta.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Envia un codi de verificació al correu electrònic</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Codi enviat!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirmeu la vostra identitat per continuar.</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>
|
||||
@@ -2036,19 +2048,19 @@
|
||||
<value>Actualment no es pot actualitzar la contrasenya</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||
<value>Remove Master Password</value>
|
||||
<value>Suprimiu la contrasenya mestra</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||
<value>{0} is using SSO with customer-managed encryption. Continuing will remove your Master Password from your account and require SSO to login.</value>
|
||||
<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>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||
<value>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||
<value>Si no voleu suprimir la vostra contrasenya mestra, podeu abandonar aquesta organització.</value>
|
||||
</data>
|
||||
<data name="LeaveOrganization" xml:space="preserve">
|
||||
<value>Leave Organization</value>
|
||||
<value>Abandona l'organització</value>
|
||||
</data>
|
||||
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||
<value>Leave {0}?</value>
|
||||
<value>Abandona {0}</value>
|
||||
</data>
|
||||
<data name="Fido2Title" xml:space="preserve">
|
||||
<value>FIDO2 WebAuthn</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Una o més polítiques d'organització us impedeixen exportar la vostra caixa forta.</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Suprimeix el compte</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>La supressió del vostre compte és 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>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>S'està suprimint el vostre compte</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>El vostre compte ha sigut suprimit de forma permanent.</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Codi de verificació no vàlid</value>
|
||||
</data>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Envia codi</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>S'està enviant...</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Odemknout</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Unlock Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minut</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Váš trezor je uzamčen. Pro pokračování musíte zadat PIN.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Tmavý</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Zadejte své hlavní heslo pro export dat.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Send a verification code to your email</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Code Sent!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirm your identity to continue.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Tento export obsahuje data vašeho trezoru v nezašifrovaném formátu. Soubor exportu byste neměli ukládat ani odesílat přes nezabezpečené kanály (např. e-mailem). Odstraňte jej okamžitě po jeho použití.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Deleting your account is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<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>Deleting your account</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Your account has been permanently deleted</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Send Code</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Sending</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Lås op</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Lås boks op</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minutter</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Din boks er låst. Bekræft din pinkode for at fortsætte.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Din boks er låst. Bekræft din identitet for at fortsætte.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Mørk</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Angiv din hovedadgangskode for at eksportere boksdata.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Angiv bekræftelseskoden for at eksportere dine boks-data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Send en bekræftelseskode til din e-mail</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Kode sendt!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Bekræft din identitet for at fortsætte.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Denne eksport indeholder dine boksdata i en ukrypteret form. Du bør ikke gemme eller sende den eksporterede fil via usikre kanaler (f.eks. e-mail). Slet den straks efter at du er færdig med at bruge den.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>En eller flere organisationspolitikker forhindrer eksport af din personlige boks.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Slet konto</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Sletning af din konto er permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Din konto og alle tilknyttede data vil blive slettet og kan ikke gendannes. Er du sikker på, at du vil fortsætte?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Sletter din konto</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Din konto er blevet permanent slettet</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Ugyldig bekræftelseskode</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Anmod om engangsadgangskode</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Send kode</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Sender</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Entsperren</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Tresor entsperren</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 Minuten</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Dein Tresor ist gesperrt. Gebe deinen PIN-Code ein um fortzufahren.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Dein Tresor ist gesperrt. Überprüfe deine Identität, um fortzufahren.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Dunkel</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Gib das Masterpasswort ein, um deine Tresordaten zu exportieren.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Gebe den Verifizierungscode ein, um deine Tresordaten zu exportieren.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Einen Bestätigungscode an deine E-Mail senden</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Code gesendet!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Bestätige deine Identität, um fortzufahren.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Dieser Export enthält deine Tresordaten in einem unverschlüsseltem Format. Du solltest sie nicht speichern oder über unsichere Kanäle (z. B. E-Mail) senden. Lösche sie sofort nach ihrer Verwendung.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Eine oder mehrere Unternehmensrichtlinien verhindern es, dass du deinen persönlichen Tresor exportieren kannst.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Konto löschen</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Das Löschen deines Kontos ist unwiderruflich</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Dein Konto und alle damit verbundenen Daten werden gelöscht und sind nicht wiederherstellbar. Bist du sicher, dass du fortfahren möchtest?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Löschen deines Kontos</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Dein Konto wurde unwiderruflich gelöscht.</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Ungültiger Bestätigungscode.</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Einmalpasswort anfordern</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Code senden</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Sende</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1061,7 +1061,7 @@
|
||||
<value>Επίθετο</value>
|
||||
</data>
|
||||
<data name="FullName" xml:space="preserve">
|
||||
<value>Full Name</value>
|
||||
<value>Ονοματεπώνυμο</value>
|
||||
</data>
|
||||
<data name="LicenseNumber" xml:space="preserve">
|
||||
<value>Αριθμός Άδειας</value>
|
||||
@@ -1188,7 +1188,7 @@
|
||||
<value>Κρυφό</value>
|
||||
</data>
|
||||
<data name="FieldTypeLinked" xml:space="preserve">
|
||||
<value>Linked</value>
|
||||
<value>Συνδεδεμένο</value>
|
||||
</data>
|
||||
<data name="FieldTypeText" xml:space="preserve">
|
||||
<value>Κείμενο</value>
|
||||
@@ -1456,6 +1456,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Ξεκλείδωμα</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Ξεκλείδωμα Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 λεπτά</value>
|
||||
</data>
|
||||
@@ -1472,6 +1475,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Το vault σας είναι κλειδωμένο. Επαληθεύστε τον κωδικό PIN για να συνεχίσετε.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Το vault σας είναι κλειδωμένο. Επαληθεύστε την ταυτότητα σας για να συνεχίσετε.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Σκοτεινό</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1612,8 +1618,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Εισαγάγετε τον κύριο κωδικό για εξαγωγή των δεδομένων vault.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Στείλτε έναν κωδικό επαλήθευσης στο email σας</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Ο Κωδικός Στάλθηκε</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Επιβεβαιώστε την ταυτότητα σας για να συνεχίσετε.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Αυτή η εξαγωγή περιέχει τα δεδομένα σε μη κρυπτογραφημένη μορφή. Δεν πρέπει να αποθηκεύετε ή να στείλετε το εξαγόμενο αρχείο μέσω μη ασφαλών τρόπων (όπως μέσω email). Διαγράψτε το αμέσως μόλις τελειώσετε με τη χρήση του.</value>
|
||||
@@ -2037,19 +2049,19 @@
|
||||
<value>Δεν είναι δυνατή η ενημέρωση του κωδικού πρόσβασης</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||
<value>Remove Master Password</value>
|
||||
<value>Αφαίρεση Κύριου Κωδικού Πρόσβασης</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||
<value>{0} is using SSO with customer-managed encryption. Continuing will remove your Master Password from your account and require SSO to login.</value>
|
||||
<value>{0} χρησιμοποιεί SSO με κρυπτογράφηση διαχείρισης πελατών. Συνεχίζοντας θα καταργήσετε τον Κύριο Κωδικό από το λογαριασμό σας και θα απαιτήσετε SSO για να συνδεθείτε.</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||
<value>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||
<value>Αν δεν θέλετε να αφαιρέσετε τον Κύριο Κωδικό Πρόσβασης, μπορείτε να φύγετε από αυτόν τον οργανισμό.</value>
|
||||
</data>
|
||||
<data name="LeaveOrganization" xml:space="preserve">
|
||||
<value>Leave Organization</value>
|
||||
<value>Αποχώρηση από τον οργανισμό</value>
|
||||
</data>
|
||||
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||
<value>Leave {0}?</value>
|
||||
<value>Αποχώρηση {0}?</value>
|
||||
</data>
|
||||
<data name="Fido2Title" xml:space="preserve">
|
||||
<value>FIDO2 WebAuthn</value>
|
||||
@@ -2081,10 +2093,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Μία ή περισσότερες οργανωτικές πολιτικές αποτρέπουν την εξαγωγή του προσωπικού vault.</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Διαγραφή Λογαριασμού</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Η διαγραφή του λογαριασμού σας είναι μόνιμη</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Ο λογαριασμός σας και όλα τα σχετικά δεδομένα θα διαγραφούν και δεν θα ανακτηθούν. Είστε σίγουροι ότι θέλετε να συνεχίσετε?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Διαγραφή του λογαριασμού σας</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Ο λογαριασμός σας έχει διαγραφεί οριστικά</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Μη Έγκυρος Κωδικός Επαλήθευσης.</value>
|
||||
</data>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Αποστολή Κωδικού</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Αποστολή....</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Unlock</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Unlock Vault</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minutes</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Your vault is locked. Verify your PIN code to continue.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Dark</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Enter your master password to export your vault data.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Send a verification code to your email</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Code Sent!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirm your identity to continue.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>This export contains your vault data in an unencrypted format. You should not store or send the exported file over insecure channels (such as email). Delete it immediately after you are done using it.</value>
|
||||
@@ -2093,10 +2105,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>One or more organisation policies prevents your from exporting your personal vault.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Deleting your account is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<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>Deleting your account</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Your account has been permanently deleted</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Send Code</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Sending</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1060,7 +1060,7 @@
|
||||
<value>Apellido</value>
|
||||
</data>
|
||||
<data name="FullName" xml:space="preserve">
|
||||
<value>Full Name</value>
|
||||
<value>Nombre completo</value>
|
||||
</data>
|
||||
<data name="LicenseNumber" xml:space="preserve">
|
||||
<value>Nº de licencia</value>
|
||||
@@ -1187,7 +1187,7 @@
|
||||
<value>Oculto</value>
|
||||
</data>
|
||||
<data name="FieldTypeLinked" xml:space="preserve">
|
||||
<value>Linked</value>
|
||||
<value>Vinculado</value>
|
||||
</data>
|
||||
<data name="FieldTypeText" xml:space="preserve">
|
||||
<value>Texto</value>
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Desbloquear</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Desbloquear bóveda</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minutos</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Su caja fuerte está bloqueada. Verifique su código PIN para continuar.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Tu caja fuerte está bloqueada. Verifica tu identidad para continuar.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Oscuro</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Introduzca su contraseña maestra para exportar la información de su caja fuerte.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Enter the verification code to export your vault data.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Envía un código de verificación a tu correo electrónico</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>¡Código Enviado!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Confirme su identidad para continuar.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Ésta exportación contiene sus datos de la caja fuerte en un formato no cifrado. No debería almacenar o enviar el archivo exportado por canales no seguros (como el correo electrónico). Elimínelo inmediatamente cuando termine de utilizarlo.</value>
|
||||
@@ -2036,19 +2048,19 @@
|
||||
<value>Actualmente no se puede actualizar la contraseña</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||
<value>Remove Master Password</value>
|
||||
<value>Remueve Contraseña Maestra</value>
|
||||
</data>
|
||||
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||
<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>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||
<value>Si no desea eliminar su contraseña maestra, puede abandonar esta organización.</value>
|
||||
</data>
|
||||
<data name="LeaveOrganization" xml:space="preserve">
|
||||
<value>Leave Organization</value>
|
||||
<value>Abandonar Organización</value>
|
||||
</data>
|
||||
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||
<value>Leave {0}?</value>
|
||||
<value>¿Abandonar {0}?</value>
|
||||
</data>
|
||||
<data name="Fido2Title" xml:space="preserve">
|
||||
<value>FIDO2 WebAuthn</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Una o más políticas de organización impiden que usted exporte su caja fuerte personal.</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Invalid Verification Code.</value>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Request one-time password</value>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Deleting your account is permanent</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Tu cuenta y todos los datos asociados serán borrados e irrecuperables. ¿Estás seguro de que quieres continuar?</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Eliminar tu cuenta</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Tu cuenta ha sido eliminada permanentemente</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Código de verificación no válido.</value>
|
||||
</data>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Enviar código</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Enviando</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -711,7 +711,7 @@
|
||||
<value>Kirje vaatamine</value>
|
||||
</data>
|
||||
<data name="WebVault" xml:space="preserve">
|
||||
<value>Bitwardeni Veebihoidla</value>
|
||||
<value>Bitwarden veebihoidla</value>
|
||||
</data>
|
||||
<data name="Lost2FAApp" xml:space="preserve">
|
||||
<value>Kaotasid autentimise rakenduse?</value>
|
||||
@@ -1246,13 +1246,13 @@
|
||||
<comment>An entity of multiple related people (ex. a team or business organization).</comment>
|
||||
</data>
|
||||
<data name="HoldYubikeyNearTop" xml:space="preserve">
|
||||
<value>Hoia oma Yubikey seadme ülaosa ligidal.</value>
|
||||
<value>Hoia oma Yubikey seadme ülaosa lähedal.</value>
|
||||
</data>
|
||||
<data name="TryAgain" xml:space="preserve">
|
||||
<value>Proovi uuesti</value>
|
||||
</data>
|
||||
<data name="YubiKeyInstructionIos" xml:space="preserve">
|
||||
<value>Jätkamiseks hoia YubiKey NEO-d oma seadme tagumise poole vastu.</value>
|
||||
<value>Jätkamiseks hoia oma YubiKey NEO seadme tagumise poole vastas.</value>
|
||||
</data>
|
||||
<data name="BitwardenAutofillAccessibilityServiceDescription2" xml:space="preserve">
|
||||
<value>Juurdepääsetavuse teenus võib abiks olla olukordades, kus rakendused ei toeta standardset automaattäite teenust.</value>
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>Lukusta lahti</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>Hoidla lahtilukustamine</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>30 minuti pärast</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>Hoidla on lukus. Jätkamiseks sisesta PIN kood.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>Hoidla on lukus. Jätkamiseks kinnita oma identiteet.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>Tume</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>Hoidlas olevate andmete eksportimiseks on vajalik ülemparooli sisestamine.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>Sisesta hoidla andmete eksportimiseks kinnituskood.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>Saada kinnituskood oma e-postile</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>Kood on saadetud!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>Jätkamiseks kinnita oma identiteet.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>Eksporditav fail sisaldab hoidla sisu, mis on krüpteeringuta. Seda faili ei tohiks kaua käidelda ning mitte mingil juhul ebaturvaliselt saata (näiteks e-postiga). Kustuta see koheselt pärast kasutamist.</value>
|
||||
@@ -2080,10 +2092,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>Üks või enam organisatsiooni poliitikat ei võimalda sul oma personaalset hoidlat eksportida.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>Kustuta konto</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>Konto kustutamist ei saa ennistada</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>Konto koos kõikide andmetega kustutatakse ning seda ei saa taastada. Oled kindel, et soovid jätkata? </value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>Konto kustutamine </value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>Sinu konto on lõplikult kustutatud</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>Vale kinnituskood</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>Küsi ühekordset parooli</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>Saada kood</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>Saatmine</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1455,6 +1455,9 @@
|
||||
<data name="Unlock" xml:space="preserve">
|
||||
<value>باز کردن قفل</value>
|
||||
</data>
|
||||
<data name="UnlockVault" xml:space="preserve">
|
||||
<value>بازگشایی گاوصندوق</value>
|
||||
</data>
|
||||
<data name="ThirtyMinutes" xml:space="preserve">
|
||||
<value>۳۰ دقیقه</value>
|
||||
</data>
|
||||
@@ -1471,6 +1474,9 @@
|
||||
<data name="VaultLockedPIN" xml:space="preserve">
|
||||
<value>گاوصندوق شما قفل شده است. برای ادامه کد پین خود را تأیید کنید.</value>
|
||||
</data>
|
||||
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||
<value>گاوصندوق شما قفل شده است. برای ادامه هویت خود را تایید کنید.</value>
|
||||
</data>
|
||||
<data name="Dark" xml:space="preserve">
|
||||
<value>تیره</value>
|
||||
<comment>A dark color</comment>
|
||||
@@ -1611,8 +1617,14 @@
|
||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||
<value>کلمه عبور اصلی خود را برای خروجی گرفتن از گاوصندوقتان وارد کنید.</value>
|
||||
</data>
|
||||
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||
<value>کد تأیید را وارد کنید تا داده های گاوصندوق خود را صادر کنید.</value>
|
||||
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||
<value>ارسال یک کد تأیید به ایمیل شما</value>
|
||||
</data>
|
||||
<data name="CodeSent" xml:space="preserve">
|
||||
<value>کد ارسال شد!</value>
|
||||
</data>
|
||||
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||
<value>برای ادامه هویت خود را تأیید کنید.</value>
|
||||
</data>
|
||||
<data name="ExportVaultWarning" xml:space="preserve">
|
||||
<value>این خروجی شامل دادههای گاوصندوق در یک قالب رمزنگاری نشده است. شما نباید آن را از طریق یک راه ارتباطی ناامن (مثل ایمیل) ذخیره یا ارسال کنید. به محض اینکه استفادهتان از آن تمام شد، آن را حذف کنید.</value>
|
||||
@@ -2081,10 +2093,28 @@
|
||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||
<value>یک یا چند خط مشی سازمان از صادرات گاوصندوق شخصی شما جلوگیری می کند.</value>
|
||||
</data>
|
||||
<data name="DeleteAccount" xml:space="preserve">
|
||||
<value>حذف حساب</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||
<value>حذف حساب شما دائمی است</value>
|
||||
</data>
|
||||
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||
<value>حساب شما و تمام داده های مرتبط پاک می شود و قابل بازیابی نیست. آیا مطمئن هستید که میخواهید ادامه دهید؟</value>
|
||||
</data>
|
||||
<data name="DeletingYourAccount" xml:space="preserve">
|
||||
<value>در حال حذف حساب شما</value>
|
||||
</data>
|
||||
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||
<value>حساب شما برای همیشه حذف شده است</value>
|
||||
</data>
|
||||
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||
<value>کد تایید نامعتبر است.</value>
|
||||
</data>
|
||||
<data name="RequestOTP" xml:space="preserve">
|
||||
<value>درخواست رمز یکبار مصرف</value>
|
||||
<data name="SendCode" xml:space="preserve">
|
||||
<value>ارسال کد</value>
|
||||
</data>
|
||||
<data name="Sending" xml:space="preserve">
|
||||
<value>درحال ارسال</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user