mirror of
https://github.com/bitwarden/mobile
synced 2025-12-16 16:23:29 +00:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ececcb7bd7 | ||
|
|
81cccf22db | ||
|
|
4e36bc83ae | ||
|
|
9db70119b9 | ||
|
|
3d485f6fb5 | ||
|
|
372c875cb2 | ||
|
|
2e60787128 | ||
|
|
d283586d96 | ||
|
|
3a2a1b527f | ||
|
|
d908d5e64d | ||
|
|
8c59552d41 | ||
|
|
77c4c423e0 | ||
|
|
47241fd5a9 | ||
|
|
51cfd70398 | ||
|
|
de566be994 | ||
|
|
819d1b616a | ||
|
|
17cdc96352 | ||
|
|
fcc94d85af | ||
|
|
79a76c4638 | ||
|
|
efd83d07dd | ||
|
|
0f14aa242c | ||
|
|
a33232dec0 | ||
|
|
084072e485 | ||
|
|
db7ca3b93e | ||
|
|
34d0ecf64b | ||
|
|
2076c11cbd | ||
|
|
4a508ea7a2 | ||
|
|
9384b3e538 | ||
|
|
317e7dad9a | ||
|
|
fac295c97b | ||
|
|
f94812719d | ||
|
|
be993bcd02 | ||
|
|
c74ed668b5 | ||
|
|
9201da8515 | ||
|
|
2e8824ce05 | ||
|
|
ded3f07fa6 | ||
|
|
31a3ec963b | ||
|
|
4722d2f632 | ||
|
|
fa1bc3fa14 | ||
|
|
fa8d59075b | ||
|
|
23ca0f4b93 | ||
|
|
04f4ad48f0 | ||
|
|
a9be659e27 | ||
|
|
39596d7533 | ||
|
|
dd2c24dcc7 | ||
|
|
ad6cf9318b | ||
|
|
ea471b0749 | ||
|
|
dbaa32b02c | ||
|
|
46128bcfe6 | ||
|
|
02562be8c7 | ||
|
|
95581bd4d9 | ||
|
|
aba34c38e9 | ||
|
|
1af447c47f |
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -6,6 +6,10 @@ on:
|
|||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'l10n_master'
|
- 'l10n_master'
|
||||||
- 'gh-pages'
|
- 'gh-pages'
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/workflows/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cloc:
|
cloc:
|
||||||
@@ -237,6 +241,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||||
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
|
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
|
||||||
|
$corePath = $($env:GITHUB_WORKSPACE + "/src/Core/Core.csproj");
|
||||||
|
|
||||||
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
|
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
|
||||||
|
|
||||||
@@ -302,6 +307,18 @@ jobs:
|
|||||||
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||||
|
|
||||||
$xml.Save($appPath);
|
$xml.Save($appPath);
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Uninstall from Core.csproj"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($corePath);
|
||||||
|
|
||||||
|
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
||||||
|
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||||
|
|
||||||
|
$xml.Save($corePath);
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
@@ -529,8 +546,7 @@ jobs:
|
|||||||
|| github.ref == 'refs/heads/hotfix-rc'
|
|| github.ref == 'refs/heads/hotfix-rc'
|
||||||
env:
|
env:
|
||||||
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
|
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
|
||||||
run: |
|
run: appcenter crashes upload-symbols -a kspearrin/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
||||||
appcenter crashes upload-symbols -a kspearrin/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Deploy to App Store
|
- name: Deploy to App Store
|
||||||
|
|||||||
16
.github/workflows/enforce-labels.yml
vendored
Normal file
16
.github/workflows/enforce-labels.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Enforce PR labels
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [labeled, unlabeled, opened, edited, synchronize]
|
||||||
|
jobs:
|
||||||
|
enforce-label:
|
||||||
|
name: EnforceLabel
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Enforce Label
|
||||||
|
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
||||||
|
with:
|
||||||
|
BANNED_LABELS: "hold"
|
||||||
|
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
||||||
19
.github/workflows/release.yml
vendored
19
.github/workflows/release.yml
vendored
@@ -12,6 +12,7 @@ on:
|
|||||||
options:
|
options:
|
||||||
- Initial Release
|
- Initial Release
|
||||||
- Redeploy
|
- Redeploy
|
||||||
|
- dry-run
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
@@ -21,6 +22,7 @@ jobs:
|
|||||||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Branch check
|
- name: Branch check
|
||||||
|
if: github.event.inputs.release_type != 'dry-run'
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
@@ -30,12 +32,13 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||||
|
|
||||||
- name: Retrieve Mobile release version
|
- name: Retrieve Mobile release version
|
||||||
id: retrieve-mobile-version
|
id: retrieve-mobile-version
|
||||||
run: |
|
run: |
|
||||||
ver=$(sed -E -n '/^<manifest/s/^.*[ ]android:versionName="([^"]+)".*$/\1/p' ./src/Android/Properties/AndroidManifest.xml | tr -d '"')
|
ver=$(sed -E -n '/^<manifest/s/^.*[ ]android:versionName="([^"]+)".*$/\1/p' \
|
||||||
|
./src/Android/Properties/AndroidManifest.xml | tr -d '"')
|
||||||
echo "::set-output name=mobile_version::${ver}"
|
echo "::set-output name=mobile_version::${ver}"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -62,7 +65,7 @@ jobs:
|
|||||||
echo "::set-output name=branch-name::$BRANCH_NAME"
|
echo "::set-output name=branch-name::$BRANCH_NAME"
|
||||||
|
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
@@ -72,7 +75,8 @@ jobs:
|
|||||||
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
if: github.event.inputs.release_type != 'dry-run'
|
||||||
|
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01 # v1.9.0
|
||||||
with:
|
with:
|
||||||
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||||
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
||||||
@@ -92,10 +96,10 @@ jobs:
|
|||||||
needs: release
|
needs: release
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||||
|
|
||||||
- name: Download F-Droid .apk artifact
|
- name: Download F-Droid .apk artifact
|
||||||
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
@@ -103,7 +107,7 @@ jobs:
|
|||||||
name: com.x8bit.bitwarden-fdroid.apk
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.3.0
|
uses: actions/setup-node@1f8c6b94b26d0feae1e387ca63ccbdc44d27b561 # v2.5.1
|
||||||
with:
|
with:
|
||||||
node-version: '10.x'
|
node-version: '10.x'
|
||||||
|
|
||||||
@@ -167,4 +171,5 @@ jobs:
|
|||||||
cd $GITHUB_WORKSPACE
|
cd $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- name: Deploy to gh-pages
|
- name: Deploy to gh-pages
|
||||||
|
if: github.event.inputs.release_type != 'dry-run'
|
||||||
run: npm run deploy
|
run: npm run deploy
|
||||||
|
|||||||
11
.github/workflows/workflow-linter.yml
vendored
Normal file
11
.github/workflows/workflow-linter.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: Workflow Linter
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .github/workflows/**
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-workflow:
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
|
||||||
@@ -23,6 +23,10 @@ The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin
|
|||||||
- Restore the nuget packages.
|
- Restore the nuget packages.
|
||||||
- Build and run the app.
|
- Build and run the app.
|
||||||
|
|
||||||
|
# We're Hiring!
|
||||||
|
|
||||||
|
Interested in contributing in a big way? Consider joining our team! We're hiring for many positions. Please take a look at our [Careers page](https://bitwarden.com/careers/) to see what opportunities are currently open as well as what it's like to work at Bitwarden.
|
||||||
|
|
||||||
# Contribute
|
# Contribute
|
||||||
|
|
||||||
Code contributions are welcome! Visual Studio with Xamarin is required to work on this project. Please commit any pull requests against the `master` branch.
|
Code contributions are welcome! Visual Studio with Xamarin is required to work on this project. Please commit any pull requests against the `master` branch.
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ namespace Bit.Droid.Accessibility
|
|||||||
// So keep them in sync with:
|
// So keep them in sync with:
|
||||||
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
|
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
|
||||||
// - Resources/xml/autofillservice.xml
|
// - Resources/xml/autofillservice.xml
|
||||||
|
new Browser("alook.browser", "search_fragment_input_view"),
|
||||||
|
new Browser("alook.browser.google", "search_fragment_input_view"),
|
||||||
new Browser("com.amazon.cloud9", "url"),
|
new Browser("com.amazon.cloud9", "url"),
|
||||||
new Browser("com.android.browser", "url"),
|
new Browser("com.android.browser", "url"),
|
||||||
new Browser("com.android.chrome", "url_bar"),
|
new Browser("com.android.chrome", "url_bar"),
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Android.Views;
|
|||||||
using Android.Views.Accessibility;
|
using Android.Views.Accessibility;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
||||||
private const string BitwardenWebsite = "vault.bitwarden.com";
|
private const string BitwardenWebsite = "vault.bitwarden.com";
|
||||||
|
|
||||||
private IStorageService _storageService;
|
private IStateService _stateService;
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
private DateTime? _lastSettingsReload = null;
|
private DateTime? _lastSettingsReload = null;
|
||||||
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
||||||
@@ -444,9 +443,9 @@ namespace Bit.Droid.Accessibility
|
|||||||
|
|
||||||
private void LoadServices()
|
private void LoadServices()
|
||||||
{
|
{
|
||||||
if (_storageService == null)
|
if (_stateService == null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
}
|
}
|
||||||
if (_broadcasterService == null)
|
if (_broadcasterService == null)
|
||||||
{
|
{
|
||||||
@@ -460,12 +459,12 @@ namespace Bit.Droid.Accessibility
|
|||||||
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||||
{
|
{
|
||||||
_lastSettingsReload = now;
|
_lastSettingsReload = now;
|
||||||
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
var uris = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||||
if (uris != null)
|
if (uris != null)
|
||||||
{
|
{
|
||||||
_blacklistedUris = new HashSet<string>(uris);
|
_blacklistedUris = new HashSet<string>(uris);
|
||||||
}
|
}
|
||||||
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
|
var isAutoFillTileAdded = await _stateService.GetAutofillTileAddedAsync();
|
||||||
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
|
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
||||||
<PackageReference Include="Xamarin.Essentials">
|
<PackageReference Include="Xamarin.Essentials">
|
||||||
<Version>1.7.0</Version>
|
<Version>1.7.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>122.0.0</Version>
|
<Version>122.0.0</Version>
|
||||||
@@ -171,7 +171,8 @@
|
|||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable\card.xml" />
|
<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\icon.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
|
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ namespace Bit.Droid.Autofill
|
|||||||
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||||
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
||||||
{
|
{
|
||||||
|
"alook.browser",
|
||||||
|
"alook.browser.google",
|
||||||
"com.amazon.cloud9",
|
"com.amazon.cloud9",
|
||||||
"com.android.browser",
|
"com.android.browser",
|
||||||
"com.android.chrome",
|
"com.android.chrome",
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using Android;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Android;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
@@ -9,12 +12,6 @@ using Bit.Core;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Autofill
|
namespace Bit.Droid.Autofill
|
||||||
{
|
{
|
||||||
@@ -26,9 +23,9 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
private ICipherService _cipherService;
|
private ICipherService _cipherService;
|
||||||
private IVaultTimeoutService _vaultTimeoutService;
|
private IVaultTimeoutService _vaultTimeoutService;
|
||||||
private IStorageService _storageService;
|
|
||||||
private IPolicyService _policyService;
|
private IPolicyService _policyService;
|
||||||
private IUserService _userService;
|
private IStateService _stateService;
|
||||||
|
private LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||||
|
|
||||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||||
FillCallback callback)
|
FillCallback callback)
|
||||||
@@ -44,18 +41,18 @@ namespace Bit.Droid.Autofill
|
|||||||
var parser = new Parser(structure, ApplicationContext);
|
var parser = new Parser(structure, ApplicationContext);
|
||||||
parser.Parse();
|
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)
|
if (!shouldAutofill)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
var inlineAutofillEnabled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||||
|
|
||||||
if (_vaultTimeoutService == null)
|
if (_vaultTimeoutService == null)
|
||||||
{
|
{
|
||||||
@@ -76,7 +73,7 @@ namespace Bit.Droid.Autofill
|
|||||||
|
|
||||||
// build response
|
// build response
|
||||||
var response = AutofillHelpers.CreateFillResponse(parser, items, locked, inlineAutofillEnabled, request);
|
var response = AutofillHelpers.CreateFillResponse(parser, items, locked, inlineAutofillEnabled, request);
|
||||||
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
|
var disableSavePrompt = await _stateService.GetAutofillDisableSavePromptAsync();
|
||||||
if (!disableSavePrompt.GetValueOrDefault())
|
if (!disableSavePrompt.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
AutofillHelpers.AddSaveInfo(parser, request, response, parser.FieldCollection);
|
AutofillHelpers.AddSaveInfo(parser, request, response, parser.FieldCollection);
|
||||||
@@ -85,9 +82,7 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Value.Exception(e);
|
||||||
Crashes.TrackError(e);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,12 +96,12 @@ namespace Bit.Droid.Autofill
|
|||||||
return;
|
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())
|
if (disableSavePrompt.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -161,9 +156,7 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Value.Exception(e);
|
||||||
Crashes.TrackError(e);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) &&
|
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
|
||||||
FieldCollection != null && FieldCollection.Fillable;
|
FieldCollection != null && FieldCollection.Fillable;
|
||||||
if (fillable)
|
if (fillable)
|
||||||
{
|
{
|
||||||
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
var blacklistedUris = await stateService.GetAutofillBlacklistedUrisAsync();
|
||||||
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
||||||
{
|
{
|
||||||
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Bit.Droid
|
|||||||
private IDeviceActionService _deviceActionService;
|
private IDeviceActionService _deviceActionService;
|
||||||
private IMessagingService _messagingService;
|
private IMessagingService _messagingService;
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
private IUserService _userService;
|
private IStateService _stateService;
|
||||||
private IAppIdService _appIdService;
|
private IAppIdService _appIdService;
|
||||||
private IEventService _eventService;
|
private IEventService _eventService;
|
||||||
private PendingIntent _eventUploadPendingIntent;
|
private PendingIntent _eventUploadPendingIntent;
|
||||||
@@ -53,7 +53,7 @@ namespace Bit.Droid
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
|
|
||||||
@@ -69,8 +69,8 @@ namespace Bit.Droid
|
|||||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !FDROID
|
#if !DEBUG && !FDROID
|
||||||
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
|
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||||
var appCenterTask = appCenterHelper.InitAsync();
|
var appCenterTask = appCenterHelper.InitAsync();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
||||||
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
||||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
|
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExitApp()
|
private void ExitApp()
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Resolve<IApiService>("apiService"),
|
ServiceContainer.Resolve<IApiService>("apiService"),
|
||||||
ServiceContainer.Resolve<IMessagingService>("messagingService"),
|
ServiceContainer.Resolve<IMessagingService>("messagingService"),
|
||||||
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
|
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
|
||||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"));
|
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||||
|
ServiceContainer.Resolve<ILogger>("logger"));
|
||||||
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
||||||
|
|
||||||
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
||||||
@@ -87,7 +88,14 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
private void RegisterLocalServices()
|
private void RegisterLocalServices()
|
||||||
{
|
{
|
||||||
ServiceContainer.Register<ILogService>("logService", new AndroidLogService());
|
ServiceContainer.Register<INativeLogService>("nativeLogService", new AndroidLogService());
|
||||||
|
#if FDROID
|
||||||
|
ServiceContainer.Register<ILogger>("logger", new StubLogger());
|
||||||
|
#elif DEBUG
|
||||||
|
ServiceContainer.Register<ILogger>("logger", DebugLogger.Instance);
|
||||||
|
#else
|
||||||
|
ServiceContainer.Register<ILogger>("logger", Logger.Instance);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Note: This might cause a race condition. Investigate more.
|
// Note: This might cause a race condition. Investigate more.
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
@@ -113,13 +121,16 @@ namespace Bit.Droid
|
|||||||
var secureStorageService = new SecureStorageService();
|
var secureStorageService = new SecureStorageService();
|
||||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||||
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
|
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||||
|
var stateMigrationService =
|
||||||
|
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
||||||
|
var deviceActionService = new DeviceActionService(stateService, messagingService,
|
||||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||||
broadcasterService);
|
broadcasterService);
|
||||||
var biometricService = new BiometricService();
|
var biometricService = new BiometricService();
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||||
|
|
||||||
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
||||||
@@ -129,7 +140,9 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
||||||
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
||||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||||
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(mobileStorageService));
|
ServiceContainer.Register<IStateService>("stateService", stateService);
|
||||||
|
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
|
||||||
|
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
|
||||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||||
@@ -148,7 +161,7 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<IPushNotificationListenerService>(
|
ServiceContainer.Register<IPushNotificationListenerService>(
|
||||||
"pushNotificationListenerService", notificationListenerService);
|
"pushNotificationListenerService", notificationListenerService);
|
||||||
var androidPushNotificationService = new AndroidPushNotificationService(
|
var androidPushNotificationService = new AndroidPushNotificationService(
|
||||||
mobileStorageService, notificationListenerService);
|
stateService, notificationListenerService);
|
||||||
ServiceContainer.Register<IPushNotificationService>(
|
ServiceContainer.Register<IPushNotificationService>(
|
||||||
"pushNotificationService", androidPushNotificationService);
|
"pushNotificationService", androidPushNotificationService);
|
||||||
#endif
|
#endif
|
||||||
@@ -164,10 +177,6 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
private async Task BootstrapAsync()
|
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();
|
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.16.4" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.17.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ namespace Bit.Droid.Push
|
|||||||
{
|
{
|
||||||
public async override void OnNewToken(string token)
|
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");
|
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||||
|
|
||||||
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
|
await stateService.SetPushRegisteredTokenAsync(token);
|
||||||
await pushNotificationService.RegisterAsync();
|
await pushNotificationService.RegisterAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using Android.App;
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
@@ -14,9 +13,10 @@ namespace Bit.Droid.Receivers
|
|||||||
{
|
{
|
||||||
public override async void OnReceive(Context context, Intent intent)
|
public override async void OnReceive(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
await AppHelpers.PerformUpdateTasksAsync(
|
||||||
await AppHelpers.PerformUpdateTasksAsync(ServiceContainer.Resolve<ISyncService>("syncService"),
|
ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"), storageService);
|
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||||
|
ServiceContainer.Resolve<IStateService>("stateService"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
|
using Bit.App.Pages;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
using Google.Android.Material.BottomNavigation;
|
using Google.Android.Material.BottomNavigation;
|
||||||
|
using Google.Android.Material.Navigation;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
using Xamarin.Forms.Platform.Android.AppCompat;
|
using Xamarin.Forms.Platform.Android.AppCompat;
|
||||||
@@ -9,7 +11,7 @@ using Xamarin.Forms.Platform.Android.AppCompat;
|
|||||||
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedRenderer))]
|
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedRenderer))]
|
||||||
namespace Bit.Droid.Renderers
|
namespace Bit.Droid.Renderers
|
||||||
{
|
{
|
||||||
public class CustomTabbedRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemReselectedListener
|
public class CustomTabbedRenderer : TabbedPageRenderer, NavigationBarView.IOnItemReselectedListener
|
||||||
{
|
{
|
||||||
private TabbedPage _page;
|
private TabbedPage _page;
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ namespace Bit.Droid.Renderers
|
|||||||
if (e.NewElement != null)
|
if (e.NewElement != null)
|
||||||
{
|
{
|
||||||
_page = e.NewElement;
|
_page = e.NewElement;
|
||||||
GetBottomNavigationView()?.SetOnNavigationItemReselectedListener(this);
|
GetBottomNavigationView()?.SetOnItemReselectedListener(this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -53,6 +55,10 @@ namespace Bit.Droid.Renderers
|
|||||||
{
|
{
|
||||||
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
|
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
|
||||||
{
|
{
|
||||||
|
if (_page is TabsPage tabsPage)
|
||||||
|
{
|
||||||
|
tabsPage.OnPageReselected();
|
||||||
|
}
|
||||||
Device.BeginInvokeOnMainThread(async () => await _page.CurrentPage.Navigation.PopToRootAsync());
|
Device.BeginInvokeOnMainThread(async () => await _page.CurrentPage.Navigation.PopToRootAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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="640dp"
|
||||||
|
android:height="512dp"
|
||||||
|
android:viewportWidth="640"
|
||||||
|
android:viewportHeight="512">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M532.989 289.887l-3.872-2.528c-3.197-1.866-5.744-4.667-7.299-8.026-1.558-3.358-2.048-7.113-1.405-10.759v-24.64c-0.682-3.57-0.202-7.266 1.37-10.542s4.154-5.964 7.366-7.666l4.768-2.4c12.013-7.054 20.768-18.547 24.384-32 3.421-13.333 1.661-27.466-4.928-39.552l-25.056-43.872c-7.011-11.727-18.259-20.313-31.418-23.987-13.161-3.674-27.229-2.155-39.303 4.243l-4.384 2.208c-3.286 1.769-6.983 2.63-10.711 2.496-3.731-0.135-7.357-1.261-10.505-3.263-7.082-4.796-14.579-8.951-22.4-12.416-3.28-1.636-6.038-4.157-7.962-7.278s-2.935-6.719-2.918-10.386v-6.528c0.099-6.965-1.197-13.879-3.808-20.335s-6.49-12.326-11.401-17.264c-4.915-4.937-10.765-8.842-17.209-11.486s-13.351-3.972-20.317-3.907h-51.2c-6.952-0.043-13.842 1.301-20.267 3.954s-12.257 6.561-17.154 11.496c-4.896 4.935-8.758 10.797-11.361 17.243s-3.892 13.347-3.794 20.298v5.472c0.032 3.614-0.938 7.165-2.802 10.261s-4.55 5.614-7.758 7.275c-5.691 2.572-11.197 5.533-16.48 8.864l-6.080 3.584c-3.102 2.221-6.788 3.481-10.6 3.623s-7.582-0.839-10.84-2.823l-3.968-1.952c-5.856-3.516-12.377-5.778-19.153-6.642s-13.656-0.314-20.208 1.618c-13.446 3.716-24.885 12.58-31.84 24.672l-24.96 43.68c-3.566 6.048-5.867 12.757-6.763 19.721s-0.37 14.037 1.547 20.791c1.743 6.495 4.779 12.571 8.925 17.866s9.317 9.699 15.203 12.95l2.88 2.848 1.312 0.928c3.197 1.867 5.744 4.667 7.3 8.026s2.046 7.113 1.403 10.758v24.704c0.326 3.533-0.314 7.087-1.853 10.283s-3.918 5.913-6.883 7.861l-4.768 2.4c-11.724 7.217-20.258 18.63-23.866 31.917s-2.020 27.447 4.442 39.603l25.088 43.872c6.806 11.955 18.058 20.739 31.308 24.445 13.25 3.702 27.425 2.026 39.445-4.669l4.352-2.176c3.287-1.792 6.994-2.669 10.736-2.547 3.742 0.125 7.382 1.248 10.544 3.251 7.082 4.797 14.578 8.954 22.4 12.416 3.281 1.635 6.038 4.157 7.962 7.28 1.923 3.12 2.934 6.717 2.918 10.384v5.472c-0.102 6.954 1.185 13.859 3.788 20.31s6.468 12.317 11.368 17.251c4.901 4.938 10.738 8.845 17.169 11.495s13.327 3.987 20.282 3.936h51.2c6.957 0.051 13.856-1.286 20.288-3.936s12.272-6.557 17.175-11.491c4.902-4.938 8.771-10.8 11.379-17.251 2.605-6.451 3.897-13.357 3.798-20.313v-5.472c-0.032-3.613 0.938-7.165 2.803-10.259 1.863-3.098 4.547-5.616 7.757-7.277 5.683-2.567 11.181-5.526 16.448-8.864l1.376-0.8 4.704-2.784c3.111-2.211 6.803-3.466 10.618-3.606 3.815-0.144 7.587 0.832 10.854 2.807l3.968 1.952c5.993 3.568 12.653 5.878 19.565 6.791 6.915 0.912 13.945 0.409 20.659-1.478 6.599-1.805 12.755-4.95 18.080-9.248 5.325-4.295 9.706-9.645 12.864-15.712l24.96-43.68c3.504-5.907 5.757-12.474 6.615-19.289 0.861-6.816 0.307-13.735-1.622-20.327-3.584-13.397-12.298-24.846-24.256-31.873zM319.997 346.752c-17.949 0-35.495-5.322-50.419-15.296-14.924-9.971-26.556-24.144-33.424-40.727s-8.666-34.83-5.165-52.434c3.502-17.604 12.145-33.775 24.837-46.466s28.862-21.335 46.466-24.837c17.604-3.502 35.852-1.704 52.434 5.164s30.755 18.501 40.73 33.425c9.971 14.924 15.293 32.47 15.293 50.419 0 24.069-9.562 47.153-26.579 64.17-17.021 17.021-40.103 26.582-64.173 26.582z" />
|
||||||
|
</vector>
|
||||||
@@ -6,4 +6,4 @@
|
|||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="#FF000000"
|
||||||
android:pathData="M532.989 289.887l-3.872-2.528c-3.197-1.866-5.744-4.667-7.299-8.026-1.558-3.358-2.048-7.113-1.405-10.759v-24.64c-0.682-3.57-0.202-7.266 1.37-10.542s4.154-5.964 7.366-7.666l4.768-2.4c12.013-7.054 20.768-18.547 24.384-32 3.421-13.333 1.661-27.466-4.928-39.552l-25.056-43.872c-7.011-11.727-18.259-20.313-31.418-23.987-13.161-3.674-27.229-2.155-39.303 4.243l-4.384 2.208c-3.286 1.769-6.983 2.63-10.711 2.496-3.731-0.135-7.357-1.261-10.505-3.263-7.082-4.796-14.579-8.951-22.4-12.416-3.28-1.636-6.038-4.157-7.962-7.278s-2.935-6.719-2.918-10.386v-6.528c0.099-6.965-1.197-13.879-3.808-20.335s-6.49-12.326-11.401-17.264c-4.915-4.937-10.765-8.842-17.209-11.486s-13.351-3.972-20.317-3.907h-51.2c-6.952-0.043-13.842 1.301-20.267 3.954s-12.257 6.561-17.154 11.496c-4.896 4.935-8.758 10.797-11.361 17.243s-3.892 13.347-3.794 20.298v5.472c0.032 3.614-0.938 7.165-2.802 10.261s-4.55 5.614-7.758 7.275c-5.691 2.572-11.197 5.533-16.48 8.864l-6.080 3.584c-3.102 2.221-6.788 3.481-10.6 3.623s-7.582-0.839-10.84-2.823l-3.968-1.952c-5.856-3.516-12.377-5.778-19.153-6.642s-13.656-0.314-20.208 1.618c-13.446 3.716-24.885 12.58-31.84 24.672l-24.96 43.68c-3.566 6.048-5.867 12.757-6.763 19.721s-0.37 14.037 1.547 20.791c1.743 6.495 4.779 12.571 8.925 17.866s9.317 9.699 15.203 12.95l2.88 2.848 1.312 0.928c3.197 1.867 5.744 4.667 7.3 8.026s2.046 7.113 1.403 10.758v24.704c0.326 3.533-0.314 7.087-1.853 10.283s-3.918 5.913-6.883 7.861l-4.768 2.4c-11.724 7.217-20.258 18.63-23.866 31.917s-2.020 27.447 4.442 39.603l25.088 43.872c6.806 11.955 18.058 20.739 31.308 24.445 13.25 3.702 27.425 2.026 39.445-4.669l4.352-2.176c3.287-1.792 6.994-2.669 10.736-2.547 3.742 0.125 7.382 1.248 10.544 3.251 7.082 4.797 14.578 8.954 22.4 12.416 3.281 1.635 6.038 4.157 7.962 7.28 1.923 3.12 2.934 6.717 2.918 10.384v5.472c-0.102 6.954 1.185 13.859 3.788 20.31s6.468 12.317 11.368 17.251c4.901 4.938 10.738 8.845 17.169 11.495s13.327 3.987 20.282 3.936h51.2c6.957 0.051 13.856-1.286 20.288-3.936s12.272-6.557 17.175-11.491c4.902-4.938 8.771-10.8 11.379-17.251 2.605-6.451 3.897-13.357 3.798-20.313v-5.472c-0.032-3.613 0.938-7.165 2.803-10.259 1.863-3.098 4.547-5.616 7.757-7.277 5.683-2.567 11.181-5.526 16.448-8.864l1.376-0.8 4.704-2.784c3.111-2.211 6.803-3.466 10.618-3.606 3.815-0.144 7.587 0.832 10.854 2.807l3.968 1.952c5.993 3.568 12.653 5.878 19.565 6.791 6.915 0.912 13.945 0.409 20.659-1.478 6.599-1.805 12.755-4.95 18.080-9.248 5.325-4.295 9.706-9.645 12.864-15.712l24.96-43.68c3.504-5.907 5.757-12.474 6.615-19.289 0.861-6.816 0.307-13.735-1.622-20.327-3.584-13.397-12.298-24.846-24.256-31.873zM319.997 346.752c-17.949 0-35.495-5.322-50.419-15.296-14.924-9.971-26.556-24.144-33.424-40.727s-8.666-34.83-5.165-52.434c3.502-17.604 12.145-33.775 24.837-46.466s28.862-21.335 46.466-24.837c17.604-3.502 35.852-1.704 52.434 5.164s30.755 18.501 40.73 33.425c9.971 14.924 15.293 32.47 15.293 50.419 0 24.069-9.562 47.153-26.579 64.17-17.021 17.021-40.103 26.582-64.173 26.582z" />
|
android:pathData="M532.989 289.887l-3.872-2.528c-3.197-1.866-5.744-4.667-7.299-8.026-1.558-3.358-2.048-7.113-1.405-10.759v-24.64c-0.682-3.57-0.202-7.266 1.37-10.542s4.154-5.964 7.366-7.666l4.768-2.4c12.013-7.054 20.768-18.547 24.384-32 3.421-13.333 1.661-27.466-4.928-39.552l-25.056-43.872c-7.011-11.727-18.259-20.313-31.418-23.987-13.161-3.674-27.229-2.155-39.303 4.243l-4.384 2.208c-3.286 1.769-6.983 2.63-10.711 2.496-3.731-0.135-7.357-1.261-10.505-3.263-7.082-4.796-14.579-8.951-22.4-12.416-3.28-1.636-6.038-4.157-7.962-7.278s-2.935-6.719-2.918-10.386v-6.528c0.099-6.965-1.197-13.879-3.808-20.335s-6.49-12.326-11.401-17.264c-4.915-4.937-10.765-8.842-17.209-11.486s-13.351-3.972-20.317-3.907h-51.2c-6.952-0.043-13.842 1.301-20.267 3.954s-12.257 6.561-17.154 11.496c-4.896 4.935-8.758 10.797-11.361 17.243s-3.892 13.347-3.794 20.298v5.472c0.032 3.614-0.938 7.165-2.802 10.261s-4.55 5.614-7.758 7.275c-5.691 2.572-11.197 5.533-16.48 8.864l-6.080 3.584c-3.102 2.221-6.788 3.481-10.6 3.623s-7.582-0.839-10.84-2.823l-3.968-1.952c-5.856-3.516-12.377-5.778-19.153-6.642s-13.656-0.314-20.208 1.618c-13.446 3.716-24.885 12.58-31.84 24.672l-24.96 43.68c-3.566 6.048-5.867 12.757-6.763 19.721s-0.37 14.037 1.547 20.791c1.743 6.495 4.779 12.571 8.925 17.866s9.317 9.699 15.203 12.95l2.88 2.848 1.312 0.928c3.197 1.867 5.744 4.667 7.3 8.026s2.046 7.113 1.403 10.758v24.704c0.326 3.533-0.314 7.087-1.853 10.283s-3.918 5.913-6.883 7.861l-4.768 2.4c-11.724 7.217-20.258 18.63-23.866 31.917s-2.020 27.447 4.442 39.603l25.088 43.872c6.806 11.955 18.058 20.739 31.308 24.445 13.25 3.702 27.425 2.026 39.445-4.669l4.352-2.176c3.287-1.792 6.994-2.669 10.736-2.547 3.742 0.125 7.382 1.248 10.544 3.251 7.082 4.797 14.578 8.954 22.4 12.416 3.281 1.635 6.038 4.157 7.962 7.28 1.923 3.12 2.934 6.717 2.918 10.384v5.472c-0.102 6.954 1.185 13.859 3.788 20.31s6.468 12.317 11.368 17.251c4.901 4.938 10.738 8.845 17.169 11.495s13.327 3.987 20.282 3.936h51.2c6.957 0.051 13.856-1.286 20.288-3.936s12.272-6.557 17.175-11.491c4.902-4.938 8.771-10.8 11.379-17.251 2.605-6.451 3.897-13.357 3.798-20.313v-5.472c-0.032-3.613 0.938-7.165 2.803-10.259 1.863-3.098 4.547-5.616 7.757-7.277 5.683-2.567 11.181-5.526 16.448-8.864l1.376-0.8 4.704-2.784c3.111-2.211 6.803-3.466 10.618-3.606 3.815-0.144 7.587 0.832 10.854 2.807l3.968 1.952c5.993 3.568 12.653 5.878 19.565 6.791 6.915 0.912 13.945 0.409 20.659-1.478 6.599-1.805 12.755-4.95 18.080-9.248 5.325-4.295 9.706-9.645 12.864-15.712l24.96-43.68c3.504-5.907 5.757-12.474 6.615-19.289 0.861-6.816 0.307-13.735-1.622-20.327-3.584-13.397-12.298-24.846-24.256-31.873zM319.997 346.752c-17.949 0-35.495-5.322-50.419-15.296-14.924-9.971-26.556-24.144-33.424-40.727s-8.666-34.83-5.165-52.434c3.502-17.604 12.145-33.775 24.837-46.466s28.862-21.335 46.466-24.837c17.604-3.502 35.852-1.704 52.434 5.164s30.755 18.501 40.73 33.425c9.971 14.924 15.293 32.47 15.293 50.419 0 24.069-9.562 47.153-26.579 64.17-17.021 17.021-40.103 26.582-64.173 26.582z" />
|
||||||
</vector>
|
</vector>
|
||||||
@@ -11,6 +11,12 @@
|
|||||||
-->
|
-->
|
||||||
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
|
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:supportsInlineSuggestions="true">
|
android:supportsInlineSuggestions="true">
|
||||||
|
<compatibility-package
|
||||||
|
android:name="alook.browser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="alook.browser.google"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.amazon.cloud9"
|
android:name="com.amazon.cloud9"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System;
|
|||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public class AndroidLogService : ILogService
|
public class AndroidLogService : INativeLogService
|
||||||
{
|
{
|
||||||
private static readonly string _tag = "BITWARDEN";
|
private static readonly string _tag = "BITWARDEN";
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AndroidX.Core.App;
|
using AndroidX.Core.App;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -11,14 +10,14 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
public class AndroidPushNotificationService : IPushNotificationService
|
public class AndroidPushNotificationService : IPushNotificationService
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
||||||
|
|
||||||
public AndroidPushNotificationService(
|
public AndroidPushNotificationService(
|
||||||
IStorageService storageService,
|
IStateService stateService,
|
||||||
IPushNotificationListenerService pushNotificationListenerService)
|
IPushNotificationListenerService pushNotificationListenerService)
|
||||||
{
|
{
|
||||||
_storageService = storageService;
|
_stateService = stateService;
|
||||||
_pushNotificationListenerService = pushNotificationListenerService;
|
_pushNotificationListenerService = pushNotificationListenerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,12 +25,12 @@ namespace Bit.Droid.Services
|
|||||||
|
|
||||||
public async Task<string> GetTokenAsync()
|
public async Task<string> GetTokenAsync()
|
||||||
{
|
{
|
||||||
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey);
|
return await _stateService.GetPushCurrentTokenAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RegisterAsync()
|
public async Task RegisterAsync()
|
||||||
{
|
{
|
||||||
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey);
|
var registeredToken = await _stateService.GetPushRegisteredTokenAsync();
|
||||||
var currentToken = await GetTokenAsync();
|
var currentToken = await GetTokenAsync();
|
||||||
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
||||||
{
|
{
|
||||||
@@ -39,7 +38,7 @@ namespace Bit.Droid.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
|
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
public class ClipboardService : IClipboardService
|
public class ClipboardService : IClipboardService
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
|
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
|
||||||
|
|
||||||
public ClipboardService(IStorageService storageService)
|
public ClipboardService(IStateService stateService)
|
||||||
{
|
{
|
||||||
_storageService = storageService;
|
_stateService = stateService;
|
||||||
|
|
||||||
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
||||||
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
|
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
|
||||||
@@ -39,7 +39,7 @@ namespace Bit.Droid.Services
|
|||||||
if (clearMs < 0)
|
if (clearMs < 0)
|
||||||
{
|
{
|
||||||
// if not set then we need to check if the user set this config
|
// if not set then we need to check if the user set this config
|
||||||
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
var clearSeconds = await _stateService.GetClearClipboardAsync();
|
||||||
if (clearSeconds != null)
|
if (clearSeconds != null)
|
||||||
{
|
{
|
||||||
clearMs = clearSeconds.Value * 1000;
|
clearMs = clearSeconds.Value * 1000;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
public class DeviceActionService : IDeviceActionService
|
public class DeviceActionService : IDeviceActionService
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly Func<IEventService> _eventServiceFunc;
|
private readonly Func<IEventService> _eventServiceFunc;
|
||||||
@@ -47,12 +47,12 @@ namespace Bit.Droid.Services
|
|||||||
private string _userAgent;
|
private string _userAgent;
|
||||||
|
|
||||||
public DeviceActionService(
|
public DeviceActionService(
|
||||||
IStorageService storageService,
|
IStateService stateService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IBroadcasterService broadcasterService,
|
IBroadcasterService broadcasterService,
|
||||||
Func<IEventService> eventServiceFunc)
|
Func<IEventService> eventServiceFunc)
|
||||||
{
|
{
|
||||||
_storageService = storageService;
|
_stateService = stateService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_broadcasterService = broadcasterService;
|
_broadcasterService = broadcasterService;
|
||||||
_eventServiceFunc = eventServiceFunc;
|
_eventServiceFunc = eventServiceFunc;
|
||||||
@@ -333,7 +333,7 @@ namespace Bit.Droid.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
||||||
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
|
await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
@@ -916,9 +916,8 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
||||||
{
|
{
|
||||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
var autoCopyDisabled = await _stateService.GetDisableAutoTotpCopyAsync();
|
||||||
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
|
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
|
||||||
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
@@ -938,5 +937,16 @@ namespace Bit.Droid.Services
|
|||||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", text);
|
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetSystemFontSizeScale()
|
||||||
|
{
|
||||||
|
var activity = CrossCurrentActivity.Current?.Activity as MainActivity;
|
||||||
|
return activity?.Resources?.Configuration?.FontScale ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnAccountSwitchCompleteAsync()
|
||||||
|
{
|
||||||
|
// for any Android-specific cleanup required after switching accounts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Android.Content;
|
|||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Service.QuickSettings;
|
using Android.Service.QuickSettings;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Droid.Accessibility;
|
using Bit.Droid.Accessibility;
|
||||||
@@ -18,7 +17,7 @@ namespace Bit.Droid.Tile
|
|||||||
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
||||||
public class AutofillTileService : TileService
|
public class AutofillTileService : TileService
|
||||||
{
|
{
|
||||||
private IStorageService _storageService;
|
private IStateService _stateService;
|
||||||
|
|
||||||
public override void OnTileAdded()
|
public override void OnTileAdded()
|
||||||
{
|
{
|
||||||
@@ -59,11 +58,11 @@ namespace Bit.Droid.Tile
|
|||||||
private void SetTileAdded(bool isAdded)
|
private void SetTileAdded(bool isAdded)
|
||||||
{
|
{
|
||||||
AccessibilityHelpers.IsAutofillTileAdded = 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()
|
private void ScanAndFill()
|
||||||
|
|||||||
@@ -12,22 +12,22 @@ namespace Bit.Droid.Utilities
|
|||||||
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
||||||
|
|
||||||
private readonly IAppIdService _appIdService;
|
private readonly IAppIdService _appIdService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
private string _userId;
|
private string _userId;
|
||||||
private string _appId;
|
private string _appId;
|
||||||
|
|
||||||
public AppCenterHelper(
|
public AppCenterHelper(
|
||||||
IAppIdService appIdService,
|
IAppIdService appIdService,
|
||||||
IUserService userService)
|
IStateService stateService)
|
||||||
{
|
{
|
||||||
_appIdService = appIdService;
|
_appIdService = appIdService;
|
||||||
_userService = userService;
|
_stateService = stateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
_userId = await _userService.GetUserIdAsync();
|
_userId = await _stateService.GetActiveUserIdAsync();
|
||||||
_appId = await _appIdService.GetAppIdAsync();
|
_appId = await _appIdService.GetAppIdAsync();
|
||||||
|
|
||||||
AppCenter.Start(AppSecret, typeof(Crashes));
|
AppCenter.Start(AppSecret, typeof(Crashes));
|
||||||
|
|||||||
@@ -45,5 +45,7 @@ namespace Bit.App.Abstractions
|
|||||||
long GetActiveTime();
|
long GetActiveTime();
|
||||||
void CloseMainApp();
|
void CloseMainApp();
|
||||||
bool SupportsFido2();
|
bool SupportsFido2();
|
||||||
|
float GetSystemFontSizeScale();
|
||||||
|
Task OnAccountSwitchCompleteAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,13 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
||||||
|
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||||
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
<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.2337" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
||||||
<PackageReference Include="Xamarin.CommunityToolkit" Version="1.3.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -121,17 +122,20 @@
|
|||||||
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
|
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Resources\" />
|
<Folder Include="Resources\" />
|
||||||
<Folder Include="Behaviors\" />
|
<Folder Include="Behaviors\" />
|
||||||
|
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
|
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
|
||||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -416,5 +420,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Behaviors\" />
|
<None Remove="Behaviors\" />
|
||||||
<None Remove="Xamarin.CommunityToolkit" />
|
<None Remove="Xamarin.CommunityToolkit" />
|
||||||
|
<None Remove="Controls\AccountSwitchingOverlay\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ using Bit.App.Pages;
|
|||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Services;
|
using Bit.App.Services;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Xaml;
|
using Xamarin.Forms.Xaml;
|
||||||
|
|
||||||
@@ -18,7 +18,6 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
@@ -26,7 +25,6 @@ namespace Bit.App
|
|||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IStorageService _secureStorageService;
|
private readonly IStorageService _secureStorageService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
|
||||||
@@ -40,7 +38,6 @@ namespace Bit.App
|
|||||||
Current = this;
|
Current = this;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
@@ -48,7 +45,6 @@ namespace Bit.App
|
|||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
|
||||||
@@ -77,7 +73,10 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else if (message.Command == "locked")
|
else if (message.Command == "locked")
|
||||||
{
|
{
|
||||||
await LockedAsync(!(message.Data as bool?).GetValueOrDefault());
|
var extras = message.Data as Tuple<string, bool>;
|
||||||
|
var userId = extras?.Item1;
|
||||||
|
var userInitiated = extras?.Item2 ?? false;
|
||||||
|
Device.BeginInvokeOnMainThread(async () => await LockedAsync(userId, userInitiated));
|
||||||
}
|
}
|
||||||
else if (message.Command == "lockVault")
|
else if (message.Command == "lockVault")
|
||||||
{
|
{
|
||||||
@@ -85,8 +84,11 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else if (message.Command == "logout")
|
else if (message.Command == "logout")
|
||||||
{
|
{
|
||||||
Device.BeginInvokeOnMainThread(async () =>
|
var extras = message.Data as Tuple<string, bool, bool>;
|
||||||
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
|
var userId = extras?.Item1;
|
||||||
|
var userInitiated = extras?.Item2 ?? true;
|
||||||
|
var expired = extras?.Item3 ?? false;
|
||||||
|
Device.BeginInvokeOnMainThread(async () => await LogOutAsync(userId, userInitiated, expired));
|
||||||
}
|
}
|
||||||
else if (message.Command == "loggedOut")
|
else if (message.Command == "loggedOut")
|
||||||
{
|
{
|
||||||
@@ -107,6 +109,18 @@ namespace Bit.App
|
|||||||
await SleptAsync();
|
await SleptAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (message.Command == "addAccount")
|
||||||
|
{
|
||||||
|
await AddAccount();
|
||||||
|
}
|
||||||
|
else if (message.Command == "accountAdded")
|
||||||
|
{
|
||||||
|
await UpdateThemeAsync();
|
||||||
|
}
|
||||||
|
else if (message.Command == "switchedAccount")
|
||||||
|
{
|
||||||
|
await SwitchedAccountAsync();
|
||||||
|
}
|
||||||
else if (message.Command == "migrated")
|
else if (message.Command == "migrated")
|
||||||
{
|
{
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
@@ -168,7 +182,7 @@ namespace Bit.App
|
|||||||
if (string.IsNullOrWhiteSpace(Options.Uri))
|
if (string.IsNullOrWhiteSpace(Options.Uri))
|
||||||
{
|
{
|
||||||
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
||||||
_storageService);
|
_stateService);
|
||||||
if (!updated)
|
if (!updated)
|
||||||
{
|
{
|
||||||
SyncIfNeeded();
|
SyncIfNeeded();
|
||||||
@@ -192,7 +206,7 @@ namespace Bit.App
|
|||||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||||
if (!isLocked)
|
if (!isLocked)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||||
}
|
}
|
||||||
SetTabsPageFromAutofill(isLocked);
|
SetTabsPageFromAutofill(isLocked);
|
||||||
await SleptAsync();
|
await SleptAsync();
|
||||||
@@ -233,7 +247,7 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
await Device.InvokeOnMainThreadAsync(() =>
|
await Device.InvokeOnMainThreadAsync(() =>
|
||||||
{
|
{
|
||||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
ThemeManager.SetTheme(Current.Resources);
|
||||||
_messagingService.Send("updatedTheme");
|
_messagingService.Send("updatedTheme");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -246,12 +260,12 @@ namespace Bit.App
|
|||||||
new System.Globalization.UmAlQuraCalendar();
|
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);
|
||||||
|
await SetMainPageAsync();
|
||||||
_authService.LogOut(() =>
|
_authService.LogOut(() =>
|
||||||
{
|
{
|
||||||
Current.MainPage = new HomePage();
|
|
||||||
if (expired)
|
if (expired)
|
||||||
{
|
{
|
||||||
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
||||||
@@ -259,12 +273,50 @@ namespace Bit.App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task AddAccount()
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
Options.HideAccountSwitcher = false;
|
||||||
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SwitchedAccountAsync()
|
||||||
|
{
|
||||||
|
await AppHelpers.OnAccountSwitchAsync();
|
||||||
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
if (await _vaultTimeoutService.ShouldTimeoutAsync())
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SetMainPageAsync();
|
||||||
|
}
|
||||||
|
await Task.Delay(50);
|
||||||
|
await UpdateThemeAsync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SetMainPageAsync()
|
private async Task SetMainPageAsync()
|
||||||
{
|
{
|
||||||
var authed = await _userService.IsAuthenticatedAsync();
|
var authed = await _stateService.IsAuthenticatedAsync();
|
||||||
if (authed)
|
if (authed)
|
||||||
{
|
{
|
||||||
if (await _vaultTimeoutService.IsLockedAsync())
|
if (await _vaultTimeoutService.IsLoggedOutByTimeoutAsync() ||
|
||||||
|
await _vaultTimeoutService.ShouldLogOutByTimeoutAsync())
|
||||||
|
{
|
||||||
|
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
||||||
|
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
||||||
|
|
||||||
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
||||||
|
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
||||||
|
}
|
||||||
|
else if (await _vaultTimeoutService.IsLockedAsync() ||
|
||||||
|
await _vaultTimeoutService.ShouldLockAsync())
|
||||||
{
|
{
|
||||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||||
}
|
}
|
||||||
@@ -287,13 +339,26 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Current.MainPage = new HomePage(Options);
|
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
||||||
|
if (await _vaultTimeoutService.IsLoggedOutByTimeoutAsync() ||
|
||||||
|
await _vaultTimeoutService.ShouldLogOutByTimeoutAsync())
|
||||||
|
{
|
||||||
|
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
||||||
|
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
||||||
|
|
||||||
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ClearCacheIfNeededAsync()
|
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)
|
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
|
||||||
{
|
{
|
||||||
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
||||||
@@ -336,12 +401,12 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
SetCulture();
|
SetCulture();
|
||||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
ThemeManager.SetTheme(Current.Resources);
|
||||||
Current.RequestedThemeChanged += (s, a) =>
|
Current.RequestedThemeChanged += (s, a) =>
|
||||||
{
|
{
|
||||||
UpdateThemeAsync();
|
UpdateThemeAsync();
|
||||||
};
|
};
|
||||||
Current.MainPage = new HomePage();
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
var mainPageTask = SetMainPageAsync();
|
var mainPageTask = SetMainPageAsync();
|
||||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||||
}
|
}
|
||||||
@@ -363,12 +428,18 @@ namespace Bit.App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LockedAsync(bool autoPromptBiometric)
|
private async Task LockedAsync(string userId, bool userInitiated)
|
||||||
{
|
{
|
||||||
await _stateService.PurgeAsync();
|
if (!await _stateService.IsActiveAccountAsync(userId))
|
||||||
|
{
|
||||||
|
_platformUtilsService.ShowToast("info", null, AppResources.AccountLockedSuccessfully);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var autoPromptBiometric = !userInitiated;
|
||||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||||
if (vaultTimeout == 0)
|
if (vaultTimeout == 0)
|
||||||
{
|
{
|
||||||
autoPromptBiometric = false;
|
autoPromptBiometric = false;
|
||||||
@@ -398,7 +469,7 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
|
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<ContentView
|
||||||
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||||
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:effects="clr-namespace:Bit.App.Effects"
|
||||||
|
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||||
|
x:Name="_mainOverlay"
|
||||||
|
x:DataType="controls:AccountSwitchingOverlayViewModel"
|
||||||
|
x:Class="Bit.App.Controls.AccountSwitchingOverlayView"
|
||||||
|
BackgroundColor="#22000000"
|
||||||
|
Padding="0"
|
||||||
|
IsVisible="False">
|
||||||
|
<StackLayout
|
||||||
|
x:Name="_accountListContainer"
|
||||||
|
VerticalOptions="Fill"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
BackgroundColor="Transparent">
|
||||||
|
<Frame
|
||||||
|
Padding="0"
|
||||||
|
HorizontalOptions="Fill"
|
||||||
|
VerticalOptions="Start"
|
||||||
|
xct:ShadowEffect.Color="Black"
|
||||||
|
xct:ShadowEffect.Radius="10"
|
||||||
|
xct:ShadowEffect.OffsetY="3">
|
||||||
|
<ListView
|
||||||
|
x:Name="_accountListView"
|
||||||
|
ItemsSource="{Binding BindingContext.AccountViews, Source={x:Reference _mainOverlay}}"
|
||||||
|
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||||
|
VerticalOptions="Start"
|
||||||
|
RowHeight="{Binding AccountListRowHeight, Source={x:Reference _mainOverlay}}"
|
||||||
|
effects:ScrollViewContentInsetAdjustmentBehaviorEffect.ContentInsetAdjustmentBehavior="Never">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="view:AccountView">
|
||||||
|
<controls:AccountViewCell
|
||||||
|
Account="{Binding .}"
|
||||||
|
SelectAccountCommand="{Binding SelectAccountCommand, Source={x:Reference _mainOverlay}}"
|
||||||
|
LongPressAccountCommand="{Binding LongPressAccountCommand, Source={x:Reference _mainOverlay}}"
|
||||||
|
/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
<ListView.Effects>
|
||||||
|
<effects:ScrollViewContentInsetAdjustmentBehaviorEffect />
|
||||||
|
</ListView.Effects>
|
||||||
|
</ListView>
|
||||||
|
</Frame>
|
||||||
|
<BoxView
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
HorizontalOptions="Fill"
|
||||||
|
VerticalOptions="FillAndExpand">
|
||||||
|
<BoxView.GestureRecognizers>
|
||||||
|
<TapGestureRecognizer Tapped="FreeSpaceOverlay_Tapped" />
|
||||||
|
</BoxView.GestureRecognizers>
|
||||||
|
</BoxView>
|
||||||
|
</StackLayout>
|
||||||
|
</ContentView>
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public partial class AccountSwitchingOverlayView : ContentView
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty MainPageProperty = BindableProperty.Create(
|
||||||
|
nameof(MainPage),
|
||||||
|
typeof(ContentPage),
|
||||||
|
typeof(AccountSwitchingOverlayView),
|
||||||
|
defaultBindingMode: BindingMode.OneWay);
|
||||||
|
|
||||||
|
public static readonly BindableProperty MainFabProperty = BindableProperty.Create(
|
||||||
|
nameof(MainFab),
|
||||||
|
typeof(View),
|
||||||
|
typeof(AccountSwitchingOverlayView),
|
||||||
|
defaultBindingMode: BindingMode.OneWay);
|
||||||
|
|
||||||
|
public ContentPage MainPage
|
||||||
|
{
|
||||||
|
get => (ContentPage)GetValue(MainPageProperty);
|
||||||
|
set => SetValue(MainPageProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public View MainFab
|
||||||
|
{
|
||||||
|
get => (View)GetValue(MainFabProperty);
|
||||||
|
set => SetValue(MainFabProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||||
|
|
||||||
|
public AccountSwitchingOverlayView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
ToggleVisibililtyCommand = new AsyncCommand(ToggleVisibilityAsync,
|
||||||
|
onException: ex => _logger.Value.Exception(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
|
SelectAccountCommand = new AsyncCommand<AccountViewCellViewModel>(SelectAccountAsync,
|
||||||
|
onException: ex => _logger.Value.Exception(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
|
LongPressAccountCommand = new AsyncCommand<AccountViewCellViewModel>(LongPressAccountAsync,
|
||||||
|
onException: ex => _logger.Value.Exception(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountSwitchingOverlayViewModel ViewModel => BindingContext as AccountSwitchingOverlayViewModel;
|
||||||
|
|
||||||
|
public ICommand ToggleVisibililtyCommand { get; }
|
||||||
|
|
||||||
|
public ICommand SelectAccountCommand { get; }
|
||||||
|
|
||||||
|
public ICommand LongPressAccountCommand { get; }
|
||||||
|
|
||||||
|
public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70;
|
||||||
|
|
||||||
|
public async Task ToggleVisibilityAsync()
|
||||||
|
{
|
||||||
|
if (IsVisible)
|
||||||
|
{
|
||||||
|
await HideAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ShowAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ShowAsync()
|
||||||
|
{
|
||||||
|
if (ViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ViewModel.RefreshAccountViewsAsync();
|
||||||
|
|
||||||
|
await Device.InvokeOnMainThreadAsync(async () =>
|
||||||
|
{
|
||||||
|
// start listView in default (off-screen) position
|
||||||
|
await _accountListContainer.TranslateTo(0, _accountListContainer.Height * -1, 0);
|
||||||
|
|
||||||
|
// re-measure in case accounts have been removed without changing screens
|
||||||
|
if (ViewModel.AccountViews != null)
|
||||||
|
{
|
||||||
|
_accountListView.HeightRequest = AccountListRowHeight * ViewModel.AccountViews.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set overlay opacity to zero before making visible and start fade-in
|
||||||
|
Opacity = 0;
|
||||||
|
IsVisible = true;
|
||||||
|
this.FadeTo(1, 100);
|
||||||
|
|
||||||
|
if (Device.RuntimePlatform == Device.Android && MainFab != null)
|
||||||
|
{
|
||||||
|
// start fab fade-out
|
||||||
|
MainFab.FadeTo(0, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// slide account list into view
|
||||||
|
await _accountListContainer.TranslateTo(0, 0, 200, Easing.SinOut);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HideAsync()
|
||||||
|
{
|
||||||
|
if (!IsVisible)
|
||||||
|
{
|
||||||
|
// already hidden, don't animate again
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Not all animations are awaited. This is intentional to allow multiple simultaneous animations.
|
||||||
|
await Device.InvokeOnMainThreadAsync(async () =>
|
||||||
|
{
|
||||||
|
// start overlay fade-out
|
||||||
|
this.FadeTo(0, 200);
|
||||||
|
|
||||||
|
if (Device.RuntimePlatform == Device.Android && MainFab != null)
|
||||||
|
{
|
||||||
|
// start fab fade-in
|
||||||
|
MainFab.FadeTo(1, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// slide account list out of view
|
||||||
|
await _accountListContainer.TranslateTo(0, _accountListContainer.Height * -1, 200, Easing.SinIn);
|
||||||
|
|
||||||
|
// remove overlay
|
||||||
|
IsVisible = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void FreeSpaceOverlay_Tapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await HideAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Value.Exception(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SelectAccountAsync(AccountViewCellViewModel item)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(100);
|
||||||
|
await HideAsync();
|
||||||
|
|
||||||
|
ViewModel?.SelectAccountCommand?.Execute(item);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Value.Exception(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LongPressAccountAsync(AccountViewCellViewModel item)
|
||||||
|
{
|
||||||
|
if (!item.IsAccount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(100);
|
||||||
|
await HideAsync();
|
||||||
|
|
||||||
|
ViewModel?.LongPressAccountCommand?.Execute(
|
||||||
|
new Tuple<ContentPage, AccountViewCellViewModel>(MainPage, item));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Value.Exception(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class AccountSwitchingOverlayViewModel : ExtendedViewModel
|
||||||
|
{
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IMessagingService _messagingService;
|
||||||
|
|
||||||
|
public AccountSwitchingOverlayViewModel(IStateService stateService,
|
||||||
|
IMessagingService messagingService,
|
||||||
|
ILogger logger)
|
||||||
|
{
|
||||||
|
_stateService = stateService;
|
||||||
|
_messagingService = messagingService;
|
||||||
|
|
||||||
|
SelectAccountCommand = new AsyncCommand<AccountViewCellViewModel>(SelectAccountAsync,
|
||||||
|
onException: ex => logger.Exception(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
|
LongPressAccountCommand = new AsyncCommand<Tuple<ContentPage, AccountViewCellViewModel>>(LongPressAccountAsync,
|
||||||
|
onException: ex => logger.Exception(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this needs to be a new list every time for the binding to get updated,
|
||||||
|
// XF doesn't currentlyl provide a direct way to update on same instance
|
||||||
|
// https://github.com/xamarin/Xamarin.Forms/issues/1950
|
||||||
|
public List<AccountView> AccountViews => _stateService?.AccountViews is null ? null : new List<AccountView>(_stateService.AccountViews);
|
||||||
|
|
||||||
|
public bool AllowActiveAccountSelection { get; set; }
|
||||||
|
|
||||||
|
public bool AllowAddAccountRow { get; set; }
|
||||||
|
|
||||||
|
public ICommand SelectAccountCommand { get; }
|
||||||
|
|
||||||
|
public ICommand LongPressAccountCommand { get; }
|
||||||
|
|
||||||
|
private async Task SelectAccountAsync(AccountViewCellViewModel item)
|
||||||
|
{
|
||||||
|
if (item.AccountView.IsAccount)
|
||||||
|
{
|
||||||
|
if (!item.AccountView.IsActive)
|
||||||
|
{
|
||||||
|
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||||
|
_messagingService.Send("switchedAccount");
|
||||||
|
}
|
||||||
|
else if (AllowActiveAccountSelection)
|
||||||
|
{
|
||||||
|
_messagingService.Send("switchedAccount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_messagingService.Send("addAccount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LongPressAccountAsync(Tuple<ContentPage, AccountViewCellViewModel> item)
|
||||||
|
{
|
||||||
|
var (page, account) = item;
|
||||||
|
if (account.AccountView.IsAccount)
|
||||||
|
{
|
||||||
|
await AppHelpers.AccountListOptions(page, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RefreshAccountViewsAsync()
|
||||||
|
{
|
||||||
|
await _stateService.RefreshAccountViewsAsync(AllowAddAccountRow);
|
||||||
|
|
||||||
|
Device.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(AccountViews)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal file
153
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<?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:Name="_accountView"
|
||||||
|
x:DataType="controls:AccountViewCellViewModel">
|
||||||
|
<Grid RowSpacing="0"
|
||||||
|
ColumnSpacing="0"
|
||||||
|
xct:TouchEffect.NativeAnimation="True"
|
||||||
|
xct:TouchEffect.Command="{Binding SelectAccountCommand, Source={x:Reference _accountView}}"
|
||||||
|
xct:TouchEffect.CommandParameter="{Binding .}"
|
||||||
|
xct:TouchEffect.LongPressCommand="{Binding LongPressAccountCommand, Source={x:Reference _accountView}}"
|
||||||
|
xct:TouchEffect.LongPressCommandParameter="{Binding .}">
|
||||||
|
|
||||||
|
<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="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
Grid.Column="0"
|
||||||
|
Source="{Binding AvatarImageSource}"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
Margin="10,0"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Column="1"
|
||||||
|
RowSpacing="1"
|
||||||
|
VerticalOptions="Center">
|
||||||
|
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Label
|
||||||
|
Grid.Row="0"
|
||||||
|
Text="{Binding AccountView.Email}"
|
||||||
|
IsVisible="{Binding IsActive}"
|
||||||
|
StyleClass="accountlist-title, accountlist-title-platform"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
<Label
|
||||||
|
Grid.Row="0"
|
||||||
|
Text="{Binding AccountView.Email}"
|
||||||
|
IsVisible="{Binding IsActive, Converter={StaticResource inverseBool}}"
|
||||||
|
StyleClass="accountlist-title, accountlist-title-platform"
|
||||||
|
TextColor="{DynamicResource MutedColor}"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
<Label
|
||||||
|
Grid.Row="1"
|
||||||
|
IsVisible="{Binding ShowHostname}"
|
||||||
|
Text="{Binding AccountView.Hostname}"
|
||||||
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
<Label
|
||||||
|
Grid.Row="2"
|
||||||
|
Text="{u:I18n AccountUnlocked}"
|
||||||
|
IsVisible="{Binding IsUnlockedAndNotActive}"
|
||||||
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
|
FontAttributes="Italic"
|
||||||
|
TextTransform="Lowercase"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
<Label
|
||||||
|
Grid.Row="2"
|
||||||
|
Text="{u:I18n AccountLocked}"
|
||||||
|
IsVisible="{Binding IsLockedAndNotActive}"
|
||||||
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
|
FontAttributes="Italic"
|
||||||
|
TextTransform="Lowercase"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
<Label
|
||||||
|
Grid.Row="2"
|
||||||
|
Text="{u:I18n AccountLoggedOut}"
|
||||||
|
IsVisible="{Binding IsLoggedOutAndNotActive}"
|
||||||
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
|
FontAttributes="Italic"
|
||||||
|
TextTransform="Lowercase"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<controls:IconLabel
|
||||||
|
Grid.Column="2"
|
||||||
|
Text="{Binding AuthStatusIconNotActive}"
|
||||||
|
IsVisible="{Binding IsActive, Converter={StaticResource inverseBool}}"
|
||||||
|
Margin="12,0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-icon, list-icon-platform" />
|
||||||
|
<controls:IconLabel
|
||||||
|
Grid.Column="2"
|
||||||
|
Text="{Binding AuthStatusIconActive}"
|
||||||
|
IsVisible="{Binding IsActive}"
|
||||||
|
Margin="12,0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-icon, list-icon-platform"
|
||||||
|
TextColor="{DynamicResource TextColor}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
IsVisible="{Binding IsAccount, Converter={StaticResource inverseBool}}"
|
||||||
|
VerticalOptions="CenterAndExpand">
|
||||||
|
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
Margin="14,0"
|
||||||
|
WidthRequest="{OnPlatform 24, iOS=24, Android=26}"
|
||||||
|
HeightRequest="{OnPlatform 24, iOS=24, Android=26}"
|
||||||
|
Source="plus.png"
|
||||||
|
xct:IconTintColorEffect.TintColor="{DynamicResource TextColor}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n AddAccount}"
|
||||||
|
StyleClass="accountlist-title, accountlist-title-platform"
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
Grid.Column="1" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ViewCell>
|
||||||
54
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal file
54
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using Bit.Core.Models.View;
|
||||||
|
using System.Windows.Input;
|
||||||
|
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));
|
||||||
|
|
||||||
|
public static readonly BindableProperty SelectAccountCommandProperty = BindableProperty.Create(
|
||||||
|
nameof(SelectAccountCommand), typeof(ICommand), typeof(AccountViewCell));
|
||||||
|
|
||||||
|
public static readonly BindableProperty LongPressAccountCommandProperty = BindableProperty.Create(
|
||||||
|
nameof(LongPressAccountCommand), typeof(ICommand), typeof(AccountViewCell));
|
||||||
|
|
||||||
|
public AccountViewCell()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountView Account
|
||||||
|
{
|
||||||
|
get => GetValue(AccountProperty) as AccountView;
|
||||||
|
set => SetValue(AccountProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand SelectAccountCommand
|
||||||
|
{
|
||||||
|
get => GetValue(SelectAccountCommandProperty) as ICommand;
|
||||||
|
set => SetValue(SelectAccountCommandProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand LongPressAccountCommand
|
||||||
|
{
|
||||||
|
get => GetValue(LongPressAccountCommandProperty) as ICommand;
|
||||||
|
set => SetValue(LongPressAccountCommandProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(string propertyName = null)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(propertyName);
|
||||||
|
if (propertyName == AccountProperty.PropertyName)
|
||||||
|
{
|
||||||
|
if (Account == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BindingContext = new AccountViewCellViewModel(Account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal file
93
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using Bit.Core;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class AccountViewCellViewModel : ExtendedViewModel
|
||||||
|
{
|
||||||
|
private AccountView _accountView;
|
||||||
|
private AvatarImageSource _avatar;
|
||||||
|
|
||||||
|
public AccountViewCellViewModel(AccountView accountView)
|
||||||
|
{
|
||||||
|
AccountView = accountView;
|
||||||
|
AvatarImageSource = new AvatarImageSource(AccountView.Name, AccountView.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountView AccountView
|
||||||
|
{
|
||||||
|
get => _accountView;
|
||||||
|
set => SetProperty(ref _accountView, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvatarImageSource AvatarImageSource
|
||||||
|
{
|
||||||
|
get => _avatar;
|
||||||
|
set => SetProperty(ref _avatar, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAccount
|
||||||
|
{
|
||||||
|
get => AccountView.IsAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowHostname
|
||||||
|
{
|
||||||
|
get => !string.IsNullOrWhiteSpace(AccountView.Hostname) && AccountView.Hostname != "vault.bitwarden.com";
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsActive
|
||||||
|
{
|
||||||
|
get => AccountView.IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsUnlocked
|
||||||
|
{
|
||||||
|
get => AccountView.AuthStatus == AuthenticationStatus.Unlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsUnlockedAndNotActive
|
||||||
|
{
|
||||||
|
get => IsUnlocked && !IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLocked
|
||||||
|
{
|
||||||
|
get => AccountView.AuthStatus == AuthenticationStatus.Locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLockedAndNotActive
|
||||||
|
{
|
||||||
|
get => IsLocked && !IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLoggedOut
|
||||||
|
{
|
||||||
|
get => AccountView.AuthStatus == AuthenticationStatus.LoggedOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLoggedOutAndNotActive
|
||||||
|
{
|
||||||
|
get => IsLoggedOut && !IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AuthStatusIconActive
|
||||||
|
{
|
||||||
|
get => BitwardenIcons.CheckCircle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AuthStatusIconNotActive
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsUnlocked)
|
||||||
|
{
|
||||||
|
return BitwardenIcons.Unlock;
|
||||||
|
}
|
||||||
|
return BitwardenIcons.Lock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/App/Controls/AvatarImageSource.cs
Normal file
153
src/App/Controls/AvatarImageSource.cs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
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 override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is AvatarImageSource avatar)
|
||||||
|
{
|
||||||
|
return avatar._data == _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.Equals(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() => _data?.GetHashCode() ?? -1;
|
||||||
|
|
||||||
|
public AvatarImageSource(string name = null, string email = null)
|
||||||
|
{
|
||||||
|
_data = name;
|
||||||
|
if (string.IsNullOrWhiteSpace(_data))
|
||||||
|
{
|
||||||
|
_data = email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 > 1)
|
||||||
|
{
|
||||||
|
upperData = _data.ToUpper();
|
||||||
|
chars = GetFirstLetters(upperData, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chars = upperData = _data.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 string GetFirstLetters(string data, int charCount)
|
||||||
|
{
|
||||||
|
var parts = data.Split();
|
||||||
|
if (parts.Length > 1 && charCount <= 2)
|
||||||
|
{
|
||||||
|
var text = "";
|
||||||
|
for (int i = 0; i < charCount; i++)
|
||||||
|
{
|
||||||
|
text += parts[i].Substring(0,1);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
if (data.Length > 2)
|
||||||
|
{
|
||||||
|
return data.Substring(0, 2);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return Color.FromHex(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
x:DataType="controls:CipherViewCellViewModel">
|
x:DataType="controls:CipherViewCellViewModel">
|
||||||
|
|
||||||
<Grid.Resources>
|
<Grid.Resources>
|
||||||
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
|
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
|
||||||
<u:IconImageConverter x:Key="iconImageConverter"/>
|
<u:IconImageConverter x:Key="iconImageConverter"/>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
<u:StringHasValueConverter x:Key="stringHasValueConverter" />
|
<u:StringHasValueConverter x:Key="stringHasValueConverter" />
|
||||||
</Grid.Resources>
|
</Grid.Resources>
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="40" />
|
<ColumnDefinition Width="40" x:Name="_iconColumn" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="60" />
|
<ColumnDefinition Width="60" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -35,17 +35,21 @@
|
|||||||
StyleClass="list-icon, list-icon-platform"
|
StyleClass="list-icon, list-icon-platform"
|
||||||
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
||||||
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||||
|
ShouldUpdateFontSizeDynamicallyForAccesibility="True"
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
<ff:CachedImage
|
<ff:CachedImage
|
||||||
|
x:Name="_iconImage"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
BitmapOptimizations="True"
|
BitmapOptimizations="True"
|
||||||
ErrorPlaceholder="login.png"
|
ErrorPlaceholder="login.png"
|
||||||
LoadingPlaceholder="login.png"
|
LoadingPlaceholder="login.png"
|
||||||
HorizontalOptions="Center"
|
HorizontalOptions="CenterAndExpand"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="CenterAndExpand"
|
||||||
|
Margin="9"
|
||||||
WidthRequest="22"
|
WidthRequest="22"
|
||||||
HeightRequest="22"
|
HeightRequest="22"
|
||||||
|
Aspect="AspectFit"
|
||||||
IsVisible="{Binding ShowIconImage}"
|
IsVisible="{Binding ShowIconImage}"
|
||||||
Source="{Binding IconImageSource, Mode=OneTime}"
|
Source="{Binding IconImageSource, Mode=OneTime}"
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
@@ -112,4 +116,4 @@
|
|||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Options}" />
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
|
|
||||||
</controls:ExtendedGrid>
|
</controls:ExtendedGrid>
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public partial class CipherViewCell : ExtendedGrid
|
public partial class CipherViewCell : ExtendedGrid
|
||||||
{
|
{
|
||||||
|
private const int ICON_COLUMN_DEFAULT_WIDTH = 40;
|
||||||
|
private const int ICON_IMAGE_DEFAULT_WIDTH = 22;
|
||||||
|
|
||||||
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
|
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
|
||||||
nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay);
|
nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay);
|
||||||
|
|
||||||
@@ -18,6 +23,11 @@ namespace Bit.App.Controls
|
|||||||
public CipherViewCell()
|
public CipherViewCell()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
var fontScale = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService").GetSystemFontSizeScale();
|
||||||
|
_iconColumn.Width = new GridLength(ICON_COLUMN_DEFAULT_WIDTH * fontScale, GridUnitType.Absolute);
|
||||||
|
_iconImage.WidthRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
|
||||||
|
_iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool? WebsiteIconsEnabled
|
public bool? WebsiteIconsEnabled
|
||||||
|
|||||||
29
src/App/Controls/ExtendedToolbarItem.cs
Normal file
29
src/App/Controls/ExtendedToolbarItem.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedToolbarItem : ToolbarItem
|
||||||
|
{
|
||||||
|
public bool UseOriginalImage { get; set; }
|
||||||
|
|
||||||
|
// HACK: For the issue of correctly updating the avatar toolbar item color on iOS
|
||||||
|
// we need to subscribe to the PropertyChanged event of the ToolbarItem on the CustomNavigationRenderer
|
||||||
|
// The problem is that there are a lot of private places where the navigation renderer disposes objects
|
||||||
|
// that we don't have access to, and that we should in order to properly prevent memory leaks
|
||||||
|
// So as a hack solution we have this OnAppearing/OnDisappearing actions and methods to be called on page lifecycle
|
||||||
|
// to subscribe/unsubscribe indirectly on the CustomNavigationRenderer
|
||||||
|
public Action OnAppearingAction { get; set; }
|
||||||
|
public Action OnDisappearingAction { get; set; }
|
||||||
|
|
||||||
|
public void OnAppearing()
|
||||||
|
{
|
||||||
|
OnAppearingAction?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDisappearing()
|
||||||
|
{
|
||||||
|
OnDisappearingAction?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
public class IconLabel : Label
|
public class IconLabel : Label
|
||||||
{
|
{
|
||||||
|
public bool ShouldUpdateFontSizeDynamicallyForAccesibility { get; set; }
|
||||||
|
|
||||||
public IconLabel()
|
public IconLabel()
|
||||||
{
|
{
|
||||||
switch (Device.RuntimePlatform)
|
switch (Device.RuntimePlatform)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="40" />
|
<ColumnDefinition Width="40" x:Name="_iconColumn" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="60" />
|
<ColumnDefinition Width="60" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
StyleClass="list-icon, list-icon-platform"
|
StyleClass="list-icon, list-icon-platform"
|
||||||
Text="{Binding Send, Converter={StaticResource sendIconGlyphConverter}}"
|
Text="{Binding Send, Converter={StaticResource sendIconGlyphConverter}}"
|
||||||
|
ShouldUpdateFontSizeDynamicallyForAccesibility="True"
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
@@ -18,6 +20,9 @@ namespace Bit.App.Controls
|
|||||||
public SendViewCell()
|
public SendViewCell()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_iconColumn.Width = new GridLength(40 * deviceActionService.GetSystemFontSizeScale(), GridUnitType.Absolute);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendView Send
|
public SendView Send
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Effects
|
||||||
|
{
|
||||||
|
public enum ScrollContentInsetAdjustmentBehavior
|
||||||
|
{
|
||||||
|
Automatic,
|
||||||
|
ScrollableAxes,
|
||||||
|
Never,
|
||||||
|
Always
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScrollViewContentInsetAdjustmentBehaviorEffect : RoutingEffect
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty ContentInsetAdjustmentBehaviorProperty =
|
||||||
|
BindableProperty.CreateAttached("ContentInsetAdjustmentBehavior", typeof(ScrollContentInsetAdjustmentBehavior), typeof(ScrollViewContentInsetAdjustmentBehaviorEffect), ScrollContentInsetAdjustmentBehavior.Automatic);
|
||||||
|
|
||||||
|
public static ScrollContentInsetAdjustmentBehavior GetContentInsetAdjustmentBehavior(BindableObject view)
|
||||||
|
{
|
||||||
|
return (ScrollContentInsetAdjustmentBehavior)view.GetValue(ContentInsetAdjustmentBehaviorProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetContentInsetAdjustmentBehavior(BindableObject view, ScrollContentInsetAdjustmentBehavior value)
|
||||||
|
{
|
||||||
|
view.SetValue(ContentInsetAdjustmentBehaviorProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScrollViewContentInsetAdjustmentBehaviorEffect()
|
||||||
|
: base($"Bitwarden.{nameof(ScrollViewContentInsetAdjustmentBehaviorEffect)}")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ namespace Bit.App.Models
|
|||||||
public bool IosExtension { get; set; }
|
public bool IosExtension { get; set; }
|
||||||
public Tuple<SendType, string, byte[], string> CreateSend { get; set; }
|
public Tuple<SendType, string, byte[], string> CreateSend { get; set; }
|
||||||
public bool CopyInsteadOfShareAfterSaving { get; set; }
|
public bool CopyInsteadOfShareAfterSaving { get; set; }
|
||||||
|
public bool HideAccountSwitcher { get; set; }
|
||||||
|
|
||||||
public void SetAllFrom(AppOptions o)
|
public void SetAllFrom(AppOptions o)
|
||||||
{
|
{
|
||||||
@@ -46,6 +47,7 @@ namespace Bit.App.Models
|
|||||||
IosExtension = o.IosExtension;
|
IosExtension = o.IosExtension;
|
||||||
CreateSend = o.CreateSend;
|
CreateSend = o.CreateSend;
|
||||||
CopyInsteadOfShareAfterSaving = o.CopyInsteadOfShareAfterSaving;
|
CopyInsteadOfShareAfterSaving = o.CopyInsteadOfShareAfterSaving;
|
||||||
|
HideAccountSwitcher = o.HideAccountSwitcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Bit.App.Pages
|
|||||||
public class BaseChangePasswordViewModel : BaseViewModel
|
public class BaseChangePasswordViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
protected readonly IPlatformUtilsService _platformUtilsService;
|
protected readonly IPlatformUtilsService _platformUtilsService;
|
||||||
protected readonly IUserService _userService;
|
protected readonly IStateService _stateService;
|
||||||
protected readonly IPolicyService _policyService;
|
protected readonly IPolicyService _policyService;
|
||||||
protected readonly IPasswordGenerationService _passwordGenerationService;
|
protected readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
protected readonly II18nService _i18nService;
|
protected readonly II18nService _i18nService;
|
||||||
@@ -31,7 +31,7 @@ namespace Bit.App.Pages
|
|||||||
protected BaseChangePasswordViewModel()
|
protected BaseChangePasswordViewModel()
|
||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
_passwordGenerationService =
|
_passwordGenerationService =
|
||||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
@@ -172,7 +172,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||||
{
|
{
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
List<string> userInput = null;
|
List<string> userInput = null;
|
||||||
var atPosition = email.IndexOf('@');
|
var atPosition = email.IndexOf('@');
|
||||||
if (atPosition > -1)
|
if (atPosition > -1)
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ using Bit.App.Utilities;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -15,11 +12,13 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
readonly IPlatformUtilsService _platformUtilsService;
|
readonly IPlatformUtilsService _platformUtilsService;
|
||||||
readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper;
|
readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper;
|
||||||
|
readonly ILogger _logger;
|
||||||
|
|
||||||
public DeleteAccountViewModel()
|
public DeleteAccountViewModel()
|
||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_verificationActionsFlowHelper = ServiceContainer.Resolve<IVerificationActionsFlowHelper>("verificationActionsFlowHelper");
|
_verificationActionsFlowHelper = ServiceContainer.Resolve<IVerificationActionsFlowHelper>("verificationActionsFlowHelper");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.DeleteAccount;
|
PageTitle = AppResources.DeleteAccount;
|
||||||
}
|
}
|
||||||
@@ -44,9 +43,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,16 +57,19 @@ namespace Bit.App.Pages
|
|||||||
readonly IMessagingService _messagingService;
|
readonly IMessagingService _messagingService;
|
||||||
readonly IPlatformUtilsService _platformUtilsService;
|
readonly IPlatformUtilsService _platformUtilsService;
|
||||||
readonly IDeviceActionService _deviceActionService;
|
readonly IDeviceActionService _deviceActionService;
|
||||||
|
readonly ILogger _logger;
|
||||||
|
|
||||||
public DeleteAccountActionFlowExecutioner(IApiService apiService,
|
public DeleteAccountActionFlowExecutioner(IApiService apiService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IPlatformUtilsService platformUtilsService,
|
IPlatformUtilsService platformUtilsService,
|
||||||
IDeviceActionService deviceActionService)
|
IDeviceActionService deviceActionService,
|
||||||
|
ILogger logger)
|
||||||
{
|
{
|
||||||
_apiService = apiService;
|
_apiService = apiService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_platformUtilsService = platformUtilsService;
|
_platformUtilsService = platformUtilsService;
|
||||||
_deviceActionService = deviceActionService;
|
_deviceActionService = deviceActionService;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Execute(IActionFlowParmeters parameters)
|
public async Task Execute(IActionFlowParmeters parameters)
|
||||||
@@ -102,9 +102,7 @@ namespace Bit.App.Pages
|
|||||||
catch (System.Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
#if !FDROID
|
_logger.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,11 @@ namespace Bit.App.Pages
|
|||||||
public partial class EnvironmentPage : BaseContentPage
|
public partial class EnvironmentPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly EnvironmentPageViewModel _vm;
|
private readonly EnvironmentPageViewModel _vm;
|
||||||
|
|
||||||
public EnvironmentPage()
|
public EnvironmentPage()
|
||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as EnvironmentPageViewModel;
|
_vm = BindingContext as EnvironmentPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
@@ -35,7 +32,6 @@ namespace Bit.App.Pages
|
|||||||
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,49 +6,76 @@
|
|||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
|
||||||
x:DataType="pages:HomeViewModel"
|
x:DataType="pages:HomeViewModel"
|
||||||
|
x:Name="_page"
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}">
|
||||||
<ContentPage.BindingContext>
|
<ContentPage.BindingContext>
|
||||||
<pages:HomeViewModel />
|
<pages:HomeViewModel />
|
||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<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}"
|
||||||
|
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
|
<ToolbarItem
|
||||||
|
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<StackLayout Spacing="0" Padding="10, 5">
|
<ContentPage.Resources>
|
||||||
<controls:IconButton Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}"
|
<ResourceDictionary>
|
||||||
StyleClass="btn-muted, btn-icon, btn-icon-platform"
|
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="0" Padding="10, 5">
|
||||||
HorizontalOptions="Start"
|
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
||||||
Clicked="Environment_Clicked"
|
<Image
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
x:Name="_logo"
|
||||||
AutomationProperties.Name="{u:I18n Options}">
|
Source="logo.png"
|
||||||
<controls:IconButton.Margin>
|
VerticalOptions="Center" />
|
||||||
<OnPlatform x:TypeArguments="Thickness">
|
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||||
<On Platform="iOS" Value="0, 10, 0, 0" />
|
StyleClass="text-lg"
|
||||||
<On Platform="Android" Value="0" />
|
HorizontalTextAlignment="Center">
|
||||||
</OnPlatform>
|
</Label>
|
||||||
</controls:IconButton.Margin>
|
<StackLayout Spacing="5">
|
||||||
</controls:IconButton>
|
<Button Text="{u:I18n LogIn}"
|
||||||
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
StyleClass="btn-primary"
|
||||||
<Image
|
Clicked="LogIn_Clicked" />
|
||||||
x:Name="_logo"
|
<Button Text="{u:I18n CreateAccount}"
|
||||||
Source="logo.png"
|
Clicked="Register_Clicked" />
|
||||||
VerticalOptions="Center" />
|
<Button Text="{u:I18n LogInSso}"
|
||||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
Clicked="LogInSso_Clicked" />
|
||||||
StyleClass="text-lg"
|
<Button Text="{u:I18n Cancel}"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
IsVisible="{Binding ShowCancelButton}"
|
||||||
<StackLayout Spacing="5">
|
Margin="0,10,0,0"
|
||||||
<Button Text="{u:I18n LogIn}"
|
Clicked="Cancel_Clicked" />
|
||||||
StyleClass="btn-primary"
|
</StackLayout>
|
||||||
Clicked="LogIn_Clicked" />
|
</StackLayout>
|
||||||
<Button Text="{u:I18n CreateAccount}"
|
|
||||||
Clicked="Register_Clicked" />
|
|
||||||
<Button Text="{u:I18n LogInSso}"
|
|
||||||
Clicked="LogInSso_Clicked" />
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</ResourceDictionary>
|
||||||
</StackLayout>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
|
<AbsoluteLayout
|
||||||
|
x:Name="_absLayout"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand">
|
||||||
|
<ContentView
|
||||||
|
x:Name="_mainContent"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||||
|
</ContentView>
|
||||||
|
|
||||||
|
<controls:AccountSwitchingOverlayView
|
||||||
|
x:Name="_accountListOverlay"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
MainPage="{Binding Source={x:Reference _page}}"
|
||||||
|
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
|
||||||
|
</AbsoluteLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Bit.App.Models;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -12,13 +12,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly HomeViewModel _vm;
|
private readonly HomeViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private IMessagingService _messagingService;
|
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
|
|
||||||
public HomePage(AppOptions appOptions = null)
|
public HomePage(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -29,6 +26,16 @@ namespace Bit.App.Pages
|
|||||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||||
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
||||||
UpdateLogo();
|
UpdateLogo();
|
||||||
|
|
||||||
|
if (_appOptions?.IosExtension ?? false)
|
||||||
|
{
|
||||||
|
_vm.ShowCancelButton = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_appOptions?.HideAccountSwitcher ?? false)
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_accountAvatar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DismissRegisterPageAndLogInAsync(string email)
|
public async Task DismissRegisterPageAndLogInAsync(string email)
|
||||||
@@ -37,10 +44,16 @@ namespace Bit.App.Pages
|
|||||||
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
|
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
_messagingService.Send("showStatusBar", false);
|
_mainContent.Content = _mainLayout;
|
||||||
|
_accountAvatar?.OnAppearing();
|
||||||
|
|
||||||
|
if (!_appOptions?.HideAccountSwitcher ?? false)
|
||||||
|
{
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
}
|
||||||
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
|
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "updatedTheme")
|
if (message.Command == "updatedTheme")
|
||||||
@@ -53,10 +66,21 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnBackButtonPressed()
|
||||||
|
{
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
_accountListOverlay.HideAsync().FireAndForget();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
{
|
{
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
_broadcasterService.Unsubscribe(nameof(HomePage));
|
_broadcasterService.Unsubscribe(nameof(HomePage));
|
||||||
|
_accountAvatar?.OnDisappearing();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLogo()
|
private void UpdateLogo()
|
||||||
@@ -64,7 +88,7 @@ namespace Bit.App.Pages
|
|||||||
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Close_Clicked(object sender, EventArgs e)
|
private void Cancel_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
@@ -124,6 +148,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task StartEnvironmentAsync()
|
private async Task StartEnvironmentAsync()
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
var page = new EnvironmentPage();
|
var page = new EnvironmentPage();
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,39 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class HomeViewModel : BaseViewModel
|
public class HomeViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IMessagingService _messagingService;
|
||||||
|
|
||||||
|
private bool _showCancelButton;
|
||||||
|
|
||||||
public HomeViewModel()
|
public HomeViewModel()
|
||||||
{
|
{
|
||||||
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
var logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.Bitwarden;
|
PageTitle = AppResources.Bitwarden;
|
||||||
|
|
||||||
|
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, logger)
|
||||||
|
{
|
||||||
|
AllowActiveAccountSelection = true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ShowCancelButton
|
||||||
|
{
|
||||||
|
get => _showCancelButton;
|
||||||
|
set => SetProperty(ref _showCancelButton, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
|
||||||
public Action StartLoginAction { get; set; }
|
public Action StartLoginAction { get; set; }
|
||||||
public Action StartRegisterAction { get; set; }
|
public Action StartRegisterAction { get; set; }
|
||||||
public Action StartSsoLoginAction { get; set; }
|
public Action StartSsoLoginAction { get; set; }
|
||||||
|
|||||||
@@ -7,12 +7,26 @@
|
|||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
x:DataType="pages:LockPageViewModel"
|
x:DataType="pages:LockPageViewModel"
|
||||||
|
x:Name="_page"
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
<ContentPage.BindingContext>
|
<ContentPage.BindingContext>
|
||||||
<pages:LockPageViewModel />
|
<pages:LockPageViewModel />
|
||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<controls:ExtendedToolbarItem
|
||||||
|
x:Name="_accountAvatar"
|
||||||
|
x:Key="accountAvatar"
|
||||||
|
IconImageSource="{Binding AvatarImageSource}"
|
||||||
|
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
@@ -25,119 +39,136 @@
|
|||||||
x:Name="_logOut"
|
x:Name="_logOut"
|
||||||
Clicked="LogOut_Clicked"
|
Clicked="LogOut_Clicked"
|
||||||
Order="Secondary"/>
|
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:IconButton
|
||||||
|
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:IconButton
|
||||||
|
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>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<AbsoluteLayout
|
||||||
</ContentPage.ToolbarItems>
|
x:Name="_absLayout"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand">
|
||||||
|
<ContentView
|
||||||
|
x:Name="_mainContent"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||||
|
</ContentView>
|
||||||
|
|
||||||
<ScrollView>
|
<controls:AccountSwitchingOverlayView
|
||||||
<StackLayout Spacing="20">
|
x:Name="_accountListOverlay"
|
||||||
<StackLayout StyleClass="box">
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
<Grid
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
StyleClass="box-row"
|
MainPage="{Binding Source={x:Reference _page}}"
|
||||||
IsVisible="{Binding PinLock}"
|
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
|
||||||
Padding="0, 10, 0, 0">
|
|
||||||
<Grid.RowDefinitions>
|
</AbsoluteLayout>
|
||||||
<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:IconButton
|
|
||||||
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:IconButton
|
|
||||||
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>
|
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using Bit.App.Models;
|
using System;
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -62,7 +61,14 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_appeared = true;
|
_appeared = true;
|
||||||
|
_mainContent.Content = _mainLayout;
|
||||||
|
|
||||||
|
_accountAvatar?.OnAppearing();
|
||||||
|
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
|
||||||
await _vm.InitAsync();
|
await _vm.InitAsync();
|
||||||
if (!_vm.BiometricLock)
|
if (!_vm.BiometricLock)
|
||||||
{
|
{
|
||||||
@@ -93,6 +99,23 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnBackButtonPressed()
|
||||||
|
{
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
_accountListOverlay.HideAsync().FireAndForget();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisappearing()
|
||||||
|
{
|
||||||
|
base.OnDisappearing();
|
||||||
|
|
||||||
|
_accountAvatar?.OnDisappearing();
|
||||||
|
}
|
||||||
|
|
||||||
private void Unlock_Clicked(object sender, EventArgs e)
|
private void Unlock_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
@@ -107,6 +130,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void LogOut_Clicked(object sender, EventArgs e)
|
private async void LogOut_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
await _vm.LogOutAsync();
|
await _vm.LogOutAsync();
|
||||||
@@ -123,6 +147,8 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void More_Clicked(object sender, System.EventArgs e)
|
private async void More_Clicked(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
|
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
@@ -10,9 +11,6 @@ using Bit.Core.Models.Domain;
|
|||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -23,14 +21,12 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStorageService _secureStorageService;
|
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IBiometricService _biometricService;
|
private readonly IBiometricService _biometricService;
|
||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private string _email;
|
private string _email;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
@@ -42,7 +38,8 @@ namespace Bit.App.Pages
|
|||||||
private string _biometricButtonText;
|
private string _biometricButtonText;
|
||||||
private string _loggedInAsText;
|
private string _loggedInAsText;
|
||||||
private string _lockedVerifyText;
|
private string _lockedVerifyText;
|
||||||
private Tuple<bool, bool> _pinSet;
|
private bool _isPinProtected;
|
||||||
|
private bool _isPinProtectedWithKey;
|
||||||
|
|
||||||
public LockPageViewModel()
|
public LockPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -51,18 +48,22 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.VerifyMasterPassword;
|
PageTitle = AppResources.VerifyMasterPassword;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
|
|
||||||
|
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
|
||||||
|
{
|
||||||
|
AllowAddAccountRow = true,
|
||||||
|
AllowActiveAccountSelection = true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowPassword
|
public bool ShowPassword
|
||||||
@@ -122,6 +123,8 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _lockedVerifyText, value);
|
set => SetProperty(ref _lockedVerifyText, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
|
||||||
|
|
||||||
public Command SubmitCommand { get; }
|
public Command SubmitCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
@@ -131,8 +134,9 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
PinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
||||||
|
_isPinProtectedWithKey;
|
||||||
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
||||||
|
|
||||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
@@ -142,16 +146,14 @@ namespace Bit.App.Pages
|
|||||||
await _vaultTimeoutService.LogOutAsync();
|
await _vaultTimeoutService.LogOutAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_email = await _userService.GetEmailAsync();
|
_email = await _stateService.GetEmailAsync();
|
||||||
if (string.IsNullOrWhiteSpace(_email))
|
if (string.IsNullOrWhiteSpace(_email))
|
||||||
{
|
{
|
||||||
await _vaultTimeoutService.LogOutAsync();
|
await _vaultTimeoutService.LogOutAsync();
|
||||||
#if !FDROID
|
_logger.Exception(new NullReferenceException("Email not found in storage"));
|
||||||
Crashes.TrackError(new NullReferenceException("Email not found in storage"));
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var webVault = _environmentService.GetWebVaultUrl();
|
var webVault = _environmentService.GetWebVaultUrl(true);
|
||||||
if (string.IsNullOrWhiteSpace(webVault))
|
if (string.IsNullOrWhiteSpace(webVault))
|
||||||
{
|
{
|
||||||
webVault = "https://bitwarden.com";
|
webVault = "https://bitwarden.com";
|
||||||
@@ -215,21 +217,21 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
var kdf = await _userService.GetKdfAsync();
|
var kdf = await _stateService.GetKdfTypeAsync();
|
||||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||||
|
|
||||||
if (PinLock)
|
if (PinLock)
|
||||||
{
|
{
|
||||||
var failed = true;
|
var failed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_pinSet.Item1)
|
if (_isPinProtected)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
||||||
_vaultTimeoutService.PinProtectedKey);
|
await _stateService.GetPinProtectedKeyAsync());
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
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);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
failed = decPin != Pin;
|
failed = decPin != Pin;
|
||||||
if (!failed)
|
if (!failed)
|
||||||
@@ -296,14 +298,14 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (passwordValid)
|
if (passwordValid)
|
||||||
{
|
{
|
||||||
if (_pinSet.Item1)
|
if (_isPinProtected)
|
||||||
{
|
{
|
||||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||||
}
|
}
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
@@ -369,7 +371,7 @@ namespace Bit.App.Pages
|
|||||||
page.MasterPasswordEntry.Focus();
|
page.MasterPasswordEntry.Focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_vaultTimeoutService.BiometricLocked = !success;
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
await DoContinueAsync();
|
await DoContinueAsync();
|
||||||
@@ -388,9 +390,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task DoContinueAsync()
|
private async Task DoContinueAsync()
|
||||||
{
|
{
|
||||||
_vaultTimeoutService.BiometricLocked = false;
|
await _stateService.SetBiometricLockedAsync(false);
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
|
||||||
_messagingService.Send("unlocked");
|
_messagingService.Send("unlocked");
|
||||||
UnlockedAction?.Invoke();
|
UnlockedAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,26 @@
|
|||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
x:DataType="pages:LoginPageViewModel"
|
x:DataType="pages:LoginPageViewModel"
|
||||||
|
x:Name="_page"
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
<ContentPage.BindingContext>
|
<ContentPage.BindingContext>
|
||||||
<pages:LoginPageViewModel />
|
<pages:LoginPageViewModel />
|
||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<controls:ExtendedToolbarItem
|
||||||
|
x:Name="_accountAvatar"
|
||||||
|
x:Key="accountAvatar"
|
||||||
|
IconImageSource="{Binding AvatarImageSource}"
|
||||||
|
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
@@ -24,69 +38,101 @@
|
|||||||
x:Key="getPasswordHint"
|
x:Key="getPasswordHint"
|
||||||
x:Name="_getPasswordHint"
|
x:Name="_getPasswordHint"
|
||||||
Clicked="Hint_Clicked"
|
Clicked="Hint_Clicked"
|
||||||
Order="Secondary"/>
|
Order="Secondary" />
|
||||||
|
<ToolbarItem Text="{u:I18n RemoveAccount}"
|
||||||
|
x:Key="removeAccount"
|
||||||
|
x:Name="_removeAccount"
|
||||||
|
Clicked="RemoveAccount_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">
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
|
<VisualState x:Name="Disabled">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Property="TextColor" Value="{DynamicResource MutedColor}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
|
</Entry>
|
||||||
|
</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:IconButton
|
||||||
|
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" />
|
||||||
|
<Button Text="{u:I18n Cancel}"
|
||||||
|
IsVisible="{Binding ShowCancelButton}"
|
||||||
|
Clicked="Cancel_Clicked" />
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
</ScrollView>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</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>
|
<controls:AccountSwitchingOverlayView
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
x:Name="_accountListOverlay"
|
||||||
</ContentPage.ToolbarItems>
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
<ScrollView>
|
MainPage="{Binding Source={x:Reference _page}}"
|
||||||
<StackLayout Spacing="20">
|
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
|
||||||
<StackLayout StyleClass="box">
|
</AbsoluteLayout>
|
||||||
<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:IconButton
|
|
||||||
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>
|
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
using Bit.App.Models;
|
using System;
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public partial class LoginPage : BaseContentPage
|
public partial class LoginPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly LoginPageViewModel _vm;
|
private readonly LoginPageViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
@@ -20,9 +17,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public LoginPage(string email = null, AppOptions appOptions = null)
|
public LoginPage(string email = null, AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as LoginPageViewModel;
|
_vm = BindingContext as LoginPageViewModel;
|
||||||
@@ -33,15 +27,19 @@ namespace Bit.App.Pages
|
|||||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
await _accountListOverlay.HideAsync();
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
|
if (!string.IsNullOrWhiteSpace(email))
|
||||||
|
{
|
||||||
|
_email.IsEnabled = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_vm.ShowCancelButton = true;
|
||||||
|
}
|
||||||
_vm.Email = email;
|
_vm.Email = email;
|
||||||
MasterPasswordEntry = _masterPassword;
|
MasterPasswordEntry = _masterPassword;
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
|
||||||
{
|
|
||||||
ToolbarItems.RemoveAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
_email.ReturnType = ReturnType.Next;
|
_email.ReturnType = ReturnType.Next;
|
||||||
_email.ReturnCommand = new Command(() => _masterPassword.Focus());
|
_email.ReturnCommand = new Command(() => _masterPassword.Focus());
|
||||||
@@ -54,6 +52,21 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
ToolbarItems.Add(_getPasswordHint);
|
ToolbarItems.Add(_getPasswordHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Device.RuntimePlatform == Device.Android && !_email.IsEnabled)
|
||||||
|
{
|
||||||
|
ToolbarItems.Add(_removeAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_appOptions?.IosExtension ?? false)
|
||||||
|
{
|
||||||
|
_vm.ShowCancelButton = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_appOptions?.HideAccountSwitcher ?? false)
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_accountAvatar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry MasterPasswordEntry { get; set; }
|
public Entry MasterPasswordEntry { get; set; }
|
||||||
@@ -61,6 +74,13 @@ namespace Bit.App.Pages
|
|||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
_mainContent.Content = _mainLayout;
|
||||||
|
_accountAvatar?.OnAppearing();
|
||||||
|
|
||||||
|
if (!_appOptions?.HideAccountSwitcher ?? false)
|
||||||
|
{
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
}
|
||||||
await _vm.InitAsync();
|
await _vm.InitAsync();
|
||||||
if (!_inputFocused)
|
if (!_inputFocused)
|
||||||
{
|
{
|
||||||
@@ -69,11 +89,28 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnBackButtonPressed()
|
||||||
|
{
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
_accountListOverlay.HideAsync().FireAndForget();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisappearing()
|
||||||
|
{
|
||||||
|
base.OnDisappearing();
|
||||||
|
|
||||||
|
_accountAvatar?.OnDisappearing();
|
||||||
|
}
|
||||||
|
|
||||||
private async void LogIn_Clicked(object sender, EventArgs e)
|
private async void LogIn_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
await _vm.LogInAsync();
|
await _vm.LogInAsync(true, _email.IsEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +122,16 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Close_Clicked(object sender, EventArgs e)
|
private async void RemoveAccount_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await _vm.RemoveAccountAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
@@ -95,18 +141,25 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void More_Clicked(object sender, System.EventArgs e)
|
private async void More_Clicked(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var selection = await DisplayActionSheet(AppResources.Options,
|
var buttons = _email.IsEnabled ? new[] { AppResources.GetPasswordHint }
|
||||||
AppResources.Cancel, null, AppResources.GetPasswordHint);
|
: new[] { AppResources.GetPasswordHint, AppResources.RemoveAccount };
|
||||||
|
var selection = await DisplayActionSheet(AppResources.Options,
|
||||||
|
AppResources.Cancel, null, buttons);
|
||||||
|
|
||||||
if (selection == AppResources.GetPasswordHint)
|
if (selection == AppResources.GetPasswordHint)
|
||||||
{
|
{
|
||||||
await Navigation.PushModalAsync(new NavigationPage(new HintPage()));
|
await Navigation.PushModalAsync(new NavigationPage(new HintPage()));
|
||||||
}
|
}
|
||||||
|
else if (selection == AppResources.RemoveAccount)
|
||||||
|
{
|
||||||
|
await _vm.RemoveAccountAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartTwoFactorAsync()
|
private async Task StartTwoFactorAsync()
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class LoginPageViewModel : CaptchaProtectedViewModel
|
public class LoginPageViewModel : CaptchaProtectedViewModel
|
||||||
{
|
{
|
||||||
private const string Keys_RememberedEmail = "rememberedEmail";
|
|
||||||
private const string Keys_RememberEmail = "rememberEmail";
|
|
||||||
|
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
|
private readonly IMessagingService _messagingService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
|
private bool _showCancelButton;
|
||||||
private string _email;
|
private string _email;
|
||||||
private string _masterPassword;
|
private string _masterPassword;
|
||||||
|
|
||||||
@@ -34,15 +34,22 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.Bitwarden;
|
PageTitle = AppResources.Bitwarden;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
LogInCommand = new Command(async () => await LogInAsync());
|
LogInCommand = new Command(async () => await LogInAsync());
|
||||||
|
|
||||||
|
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
|
||||||
|
{
|
||||||
|
AllowAddAccountRow = true,
|
||||||
|
AllowActiveAccountSelection = true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowPassword
|
public bool ShowPassword
|
||||||
@@ -55,6 +62,12 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ShowCancelButton
|
||||||
|
{
|
||||||
|
get => _showCancelButton;
|
||||||
|
set => SetProperty(ref _showCancelButton, value);
|
||||||
|
}
|
||||||
|
|
||||||
public string Email
|
public string Email
|
||||||
{
|
{
|
||||||
get => _email;
|
get => _email;
|
||||||
@@ -67,10 +80,11 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _masterPassword, value);
|
set => SetProperty(ref _masterPassword, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
|
||||||
|
|
||||||
public Command LogInCommand { get; }
|
public Command LogInCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public bool RememberEmail { get; set; }
|
|
||||||
public Action StartTwoFactorAction { get; set; }
|
public Action StartTwoFactorAction { get; set; }
|
||||||
public Action LogInSuccessAction { get; set; }
|
public Action LogInSuccessAction { get; set; }
|
||||||
public Action UpdateTempPasswordAction { get; set; }
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
@@ -85,13 +99,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Email))
|
if (string.IsNullOrWhiteSpace(Email))
|
||||||
{
|
{
|
||||||
Email = await _storageService.GetAsync<string>(Keys_RememberedEmail);
|
Email = await _stateService.GetRememberedEmailAsync();
|
||||||
}
|
}
|
||||||
var rememberEmail = await _storageService.GetAsync<bool?>(Keys_RememberEmail);
|
|
||||||
RememberEmail = rememberEmail.GetValueOrDefault(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogInAsync(bool showLoading = true)
|
public async Task LogInAsync(bool showLoading = true, bool checkForExistingAccount = false)
|
||||||
{
|
{
|
||||||
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||||
{
|
{
|
||||||
@@ -123,20 +135,27 @@ namespace Bit.App.Pages
|
|||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (checkForExistingAccount)
|
||||||
|
{
|
||||||
|
var userId = await _stateService.GetUserIdAsync(Email);
|
||||||
|
if (!string.IsNullOrWhiteSpace(userId))
|
||||||
|
{
|
||||||
|
var userEnvUrls = await _stateService.GetEnvironmentUrlsAsync(userId);
|
||||||
|
if (userEnvUrls?.Base == _environmentService.BaseUrl)
|
||||||
|
{
|
||||||
|
await PromptToSwitchToExistingAccountAsync(userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (showLoading)
|
if (showLoading)
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
||||||
if (RememberEmail)
|
await _stateService.SetRememberedEmailAsync(Email);
|
||||||
{
|
|
||||||
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
|
||||||
}
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
|
||||||
if (response.CaptchaNeeded)
|
if (response.CaptchaNeeded)
|
||||||
@@ -163,8 +182,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
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));
|
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||||
LogInSuccessAction?.Invoke();
|
LogInSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
@@ -189,5 +206,34 @@ namespace Bit.App.Pages
|
|||||||
entry.Focus();
|
entry.Focus();
|
||||||
entry.CursorPosition = String.IsNullOrEmpty(MasterPassword) ? 0 : MasterPassword.Length;
|
entry.CursorPosition = String.IsNullOrEmpty(MasterPassword) ? 0 : MasterPassword.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RemoveAccountAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.RemoveAccountConfirmation,
|
||||||
|
AppResources.RemoveAccount, AppResources.Yes, AppResources.Cancel);
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
_messagingService.Send("logout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PromptToSwitchToExistingAccountAsync(string userId)
|
||||||
|
{
|
||||||
|
var switchToAccount = await _platformUtilsService.ShowDialogAsync(
|
||||||
|
AppResources.SwitchToAlreadyAddedAccountConfirmation,
|
||||||
|
AppResources.AccountAlreadyAdded, AppResources.Yes, AppResources.Cancel);
|
||||||
|
if (switchToAccount)
|
||||||
|
{
|
||||||
|
await _stateService.SetActiveUserAsync(userId);
|
||||||
|
_messagingService.Send("switchedAccount");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class LoginSsoPage : BaseContentPage
|
public partial class LoginSsoPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly LoginSsoPageViewModel _vm;
|
private readonly LoginSsoPageViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
@@ -20,10 +18,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public LoginSsoPage(AppOptions appOptions = null)
|
public LoginSsoPage(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as LoginSsoPageViewModel;
|
_vm = BindingContext as LoginSsoPageViewModel;
|
||||||
@@ -36,7 +31,6 @@ namespace Bit.App.Pages
|
|||||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -16,16 +15,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class LoginSsoPageViewModel : BaseViewModel
|
public class LoginSsoPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private const string Keys_RememberedOrgIdentifier = "rememberedOrgIdentifier";
|
|
||||||
private const string Keys_RememberOrgIdentifier = "rememberOrgIdentifier";
|
|
||||||
|
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
@@ -40,7 +35,6 @@ namespace Bit.App.Pages
|
|||||||
_passwordGenerationService =
|
_passwordGenerationService =
|
||||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
|
||||||
@@ -55,7 +49,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Command LogInCommand { get; }
|
public Command LogInCommand { get; }
|
||||||
public bool RememberOrgIdentifier { get; set; }
|
|
||||||
public Action StartTwoFactorAction { get; set; }
|
public Action StartTwoFactorAction { get; set; }
|
||||||
public Action StartSetPasswordAction { get; set; }
|
public Action StartSetPasswordAction { get; set; }
|
||||||
public Action SsoAuthSuccessAction { get; set; }
|
public Action SsoAuthSuccessAction { get; set; }
|
||||||
@@ -66,10 +59,8 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(OrgIdentifier))
|
if (string.IsNullOrWhiteSpace(OrgIdentifier))
|
||||||
{
|
{
|
||||||
OrgIdentifier = await _storageService.GetAsync<string>(Keys_RememberedOrgIdentifier);
|
OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
|
||||||
}
|
}
|
||||||
var rememberOrgIdentifier = await _storageService.GetAsync<bool?>(Keys_RememberOrgIdentifier);
|
|
||||||
RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogInAsync()
|
public async Task LogInAsync()
|
||||||
@@ -170,14 +161,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri, orgId);
|
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri, orgId);
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
if (RememberOrgIdentifier)
|
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
||||||
{
|
|
||||||
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _storageService.RemoveAsync(Keys_RememberedOrgIdentifier);
|
|
||||||
}
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (response.TwoFactor)
|
if (response.TwoFactor)
|
||||||
{
|
{
|
||||||
@@ -193,8 +177,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
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));
|
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||||
SsoAuthSuccessAction?.Invoke();
|
SsoAuthSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Bit.Core.Abstractions;
|
using System;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -8,22 +6,18 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class RegisterPage : BaseContentPage
|
public partial class RegisterPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly RegisterPageViewModel _vm;
|
private readonly RegisterPageViewModel _vm;
|
||||||
|
|
||||||
private bool _inputFocused;
|
private bool _inputFocused;
|
||||||
|
|
||||||
public RegisterPage(HomePage homePage)
|
public RegisterPage(HomePage homePage)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as RegisterPageViewModel;
|
_vm = BindingContext as RegisterPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
|
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
MasterPasswordEntry = _masterPassword;
|
MasterPasswordEntry = _masterPassword;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Bit.Core.Abstractions;
|
using System;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
@@ -10,14 +8,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class SetPasswordPage : BaseContentPage
|
public partial class SetPasswordPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly SetPasswordPageViewModel _vm;
|
private readonly SetPasswordPageViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
|
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as SetPasswordPageViewModel;
|
_vm = BindingContext as SetPasswordPageViewModel;
|
||||||
@@ -26,7 +21,6 @@ namespace Bit.App.Pages
|
|||||||
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
|
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
_vm.OrgIdentifier = orgIdentifier;
|
_vm.OrgIdentifier = orgIdentifier;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using Bit.Core.Models.Request;
|
|||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -24,7 +23,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
@@ -41,7 +40,7 @@ namespace Bit.App.Pages
|
|||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
_passwordGenerationService =
|
_passwordGenerationService =
|
||||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
@@ -160,7 +159,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
var kdf = KdfType.PBKDF2_SHA256;
|
var kdf = KdfType.PBKDF2_SHA256;
|
||||||
var kdfIterations = 100000;
|
var kdfIterations = 100000;
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||||
@@ -197,8 +196,8 @@ namespace Bit.App.Pages
|
|||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
// Set Password and relevant information
|
// Set Password and relevant information
|
||||||
await _apiService.SetPasswordAsync(request);
|
await _apiService.SetPasswordAsync(request);
|
||||||
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
await _stateService.SetKdfTypeAsync(kdf);
|
||||||
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
await _stateService.SetKdfIterationsAsync(kdfIterations);
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetKeyAsync(key);
|
||||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||||
@@ -217,7 +216,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
ResetPasswordKey = encryptedKey.EncryptedString
|
ResetPasswordKey = encryptedKey.EncryptedString
|
||||||
};
|
};
|
||||||
var userId = await _userService.GetUserIdAsync();
|
var userId = await _stateService.GetActiveUserIdAsync();
|
||||||
// Enroll user
|
// Enroll user
|
||||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||||
}
|
}
|
||||||
@@ -290,7 +289,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||||
{
|
{
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
List<string> userInput = null;
|
List<string> userInput = null;
|
||||||
var atPosition = email.IndexOf('@');
|
var atPosition = email.IndexOf('@');
|
||||||
if (atPosition > -1)
|
if (atPosition > -1)
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
private TwoFactorPageViewModel _vm;
|
private TwoFactorPageViewModel _vm;
|
||||||
@@ -30,10 +28,8 @@ namespace Bit.App.Pages
|
|||||||
_authingWithSso = authingWithSso ?? false;
|
_authingWithSso = authingWithSso ?? false;
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_orgIdentifier = orgIdentifier;
|
_orgIdentifier = orgIdentifier;
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
|
||||||
_vm = BindingContext as TwoFactorPageViewModel;
|
_vm = BindingContext as TwoFactorPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.StartSetPasswordAction = () =>
|
_vm.StartSetPasswordAction = () =>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
@@ -18,18 +17,18 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class TwoFactorPageViewModel : BaseViewModel
|
public class TwoFactorPageViewModel : CaptchaProtectedViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
|
private readonly II18nService _i18nService;
|
||||||
|
|
||||||
private TwoFactorProviderType? _selectedProviderType;
|
private TwoFactorProviderType? _selectedProviderType;
|
||||||
private string _totpInstruction;
|
private string _totpInstruction;
|
||||||
@@ -43,13 +42,13 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
|
|
||||||
PageTitle = AppResources.TwoStepLogin;
|
PageTitle = AppResources.TwoStepLogin;
|
||||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
@@ -114,6 +113,11 @@ namespace Bit.App.Pages
|
|||||||
public Action StartSetPasswordAction { get; set; }
|
public Action StartSetPasswordAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
public Action UpdateTempPasswordAction { get; set; }
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
|
|
||||||
|
protected override II18nService i18nService => _i18nService;
|
||||||
|
protected override IEnvironmentService environmentService => _environmentService;
|
||||||
|
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||||
|
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
|
||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
@@ -288,11 +292,24 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
||||||
}
|
}
|
||||||
var result = await _authService.LogInTwoFactorAsync(SelectedProviderType.Value, Token, Remember);
|
var result = await _authService.LogInTwoFactorAsync(SelectedProviderType.Value, Token, _captchaToken, Remember);
|
||||||
|
|
||||||
|
if (result.CaptchaNeeded)
|
||||||
|
{
|
||||||
|
if (await HandleCaptchaAsync(result.CaptchaSiteKey))
|
||||||
|
{
|
||||||
|
await SubmitAsync(false);
|
||||||
|
_captchaToken = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_captchaToken = null;
|
||||||
|
|
||||||
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
_messagingService.Send("listenYubiKeyOTP", false);
|
_messagingService.Send("listenYubiKeyOTP", false);
|
||||||
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
||||||
|
|
||||||
if (_authingWithSso && result.ResetMasterPassword)
|
if (_authingWithSso && result.ResetMasterPassword)
|
||||||
{
|
{
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
@@ -303,13 +320,12 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
|
||||||
TwoFactorAuthSuccessAction?.Invoke();
|
TwoFactorAuthSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
|
_captchaToken = null;
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (e?.Error != null)
|
if (e?.Error != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ namespace Bit.App.Pages
|
|||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
|
||||||
// Service Use
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
|
|
||||||
// Binding
|
// Binding
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve details for key generation
|
// Retrieve details for key generation
|
||||||
var kdf = await _userService.GetKdfAsync();
|
var kdf = await _stateService.GetKdfTypeAsync();
|
||||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
|
||||||
// Create new key and hash new password
|
// Create new key and hash new password
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
#if !FDROID
|
using Bit.Core.Exceptions;
|
||||||
using Microsoft.AppCenter.Crashes;
|
using Bit.Core.Utilities;
|
||||||
#endif
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -24,6 +21,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IUserVerificationService _userVerificationService;
|
private readonly IUserVerificationService _userVerificationService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper;
|
private readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private string _secret, _mainActionText, _sendCodeStatus;
|
private string _secret, _mainActionText, _sendCodeStatus;
|
||||||
@@ -35,6 +33,7 @@ namespace Bit.App.Pages
|
|||||||
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>("userVerificationService");
|
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>("userVerificationService");
|
||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_verificationActionsFlowHelper = ServiceContainer.Resolve<IVerificationActionsFlowHelper>("verificationActionsFlowHelper");
|
_verificationActionsFlowHelper = ServiceContainer.Resolve<IVerificationActionsFlowHelper>("verificationActionsFlowHelper");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.VerificationCode;
|
PageTitle = AppResources.VerificationCode;
|
||||||
|
|
||||||
@@ -118,9 +117,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
SendCodeStatus = AppResources.AnErrorOccurredWhileSendingAVerificationCodeToYourEmailPleaseTryAgain;
|
SendCodeStatus = AppResources.AnErrorOccurredWhileSendingAVerificationCodeToYourEmailPleaseTryAgain;
|
||||||
}
|
}
|
||||||
@@ -171,9 +168,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Bit.Core;
|
using System;
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.PlatformConfiguration;
|
using Xamarin.Forms.PlatformConfiguration;
|
||||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||||
@@ -13,7 +13,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class BaseContentPage : ContentPage
|
public class BaseContentPage : ContentPage
|
||||||
{
|
{
|
||||||
private IStorageService _storageService;
|
private IStateService _stateService;
|
||||||
private IDeviceActionService _deviceActionService;
|
private IDeviceActionService _deviceActionService;
|
||||||
|
|
||||||
protected int ShowModalAnimationDelay = 400;
|
protected int ShowModalAnimationDelay = 400;
|
||||||
@@ -32,16 +32,16 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public bool IsThemeDirty { get; set; }
|
public bool IsThemeDirty { get; set; }
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
|
||||||
if (IsThemeDirty)
|
if (IsThemeDirty)
|
||||||
{
|
{
|
||||||
UpdateOnThemeChanged();
|
UpdateOnThemeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveActivity();
|
await SaveActivityAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DoOnce(Action action = null, int milliseconds = 1000)
|
public bool DoOnce(Action action = null, int milliseconds = 1000)
|
||||||
@@ -56,6 +56,12 @@ namespace Bit.App.Pages
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Task UpdateOnThemeChanged()
|
||||||
|
{
|
||||||
|
IsThemeDirty = false;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
protected void SetActivityIndicator(ContentView targetView = null)
|
protected void SetActivityIndicator(ContentView targetView = null)
|
||||||
{
|
{
|
||||||
var indicator = new ActivityIndicator
|
var indicator = new ActivityIndicator
|
||||||
@@ -114,11 +120,25 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task<bool> ShowAccountSwitcherAsync()
|
||||||
|
{
|
||||||
|
return await _stateService.GetActiveUserIdAsync() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<AvatarImageSource> GetAvatarImageSourceAsync(bool useCurrentActiveAccount = true)
|
||||||
|
{
|
||||||
|
if (useCurrentActiveAccount)
|
||||||
|
{
|
||||||
|
return new AvatarImageSource(await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
|
||||||
|
}
|
||||||
|
return new AvatarImageSource();
|
||||||
|
}
|
||||||
|
|
||||||
private void SetServices()
|
private void SetServices()
|
||||||
{
|
{
|
||||||
if (_storageService == null)
|
if (_stateService == null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
}
|
}
|
||||||
if (_deviceActionService == null)
|
if (_deviceActionService == null)
|
||||||
{
|
{
|
||||||
@@ -126,16 +146,10 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveActivity()
|
private async Task SaveActivityAsync()
|
||||||
{
|
{
|
||||||
SetServices();
|
SetServices();
|
||||||
_storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Task UpdateOnThemeChanged()
|
|
||||||
{
|
|
||||||
IsThemeDirty = false;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Utilities;
|
using Bit.App.Controls;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -6,6 +7,7 @@ namespace Bit.App.Pages
|
|||||||
public abstract class BaseViewModel : ExtendedViewModel
|
public abstract class BaseViewModel : ExtendedViewModel
|
||||||
{
|
{
|
||||||
private string _pageTitle = string.Empty;
|
private string _pageTitle = string.Empty;
|
||||||
|
private AvatarImageSource _avatar;
|
||||||
|
|
||||||
public string PageTitle
|
public string PageTitle
|
||||||
{
|
{
|
||||||
@@ -13,6 +15,12 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _pageTitle, value);
|
set => SetProperty(ref _pageTitle, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AvatarImageSource AvatarImageSource
|
||||||
|
{
|
||||||
|
get => _avatar ?? new AvatarImageSource();
|
||||||
|
set => SetProperty(ref _avatar, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ContentPage Page { get; set; }
|
public ContentPage Page { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,11 @@ namespace Bit.App.Pages
|
|||||||
captchaRequiredText = AppResources.CaptchaRequired,
|
captchaRequiredText = AppResources.CaptchaRequired,
|
||||||
});
|
});
|
||||||
|
|
||||||
var url = environmentService.GetWebVaultUrl();
|
var url = environmentService.GetWebVaultUrl() +
|
||||||
if (url == null)
|
"/captcha-mobile-connector.html?" +
|
||||||
{
|
"data=" + data +
|
||||||
url = "https://vault.bitwarden.com";
|
"&parent=" + Uri.EscapeDataString(callbackUri) +
|
||||||
}
|
"&v=1";
|
||||||
url += "/captcha-mobile-connector.html?" + "data=" + data +
|
|
||||||
"&parent=" + Uri.EscapeDataString(callbackUri) + "&v=1";
|
|
||||||
|
|
||||||
WebAuthenticatorResult authResult = null;
|
WebAuthenticatorResult authResult = null;
|
||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ using Bit.App.Resources;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -16,6 +13,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IClipboardService _clipboardService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
|
|
||||||
@@ -24,6 +22,7 @@ namespace Bit.App.Pages
|
|||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.PasswordHistory;
|
PageTitle = AppResources.PasswordHistory;
|
||||||
History = new ExtendedObservableCollection<GeneratedPasswordHistory>();
|
History = new ExtendedObservableCollection<GeneratedPasswordHistory>();
|
||||||
@@ -70,9 +69,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,251 +36,255 @@
|
|||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ScrollView Padding="0, 0, 0, 20">
|
<!--WORKAROUND: Wrapped in a ContentView to fix bottom screen but when using large font size on Android.
|
||||||
<StackLayout Spacing="0" Padding="0">
|
Check when https://github.com/xamarin/Xamarin.Forms/pull/15076 is released that may fix this without wrapping
|
||||||
<StackLayout StyleClass="box">
|
in ContentView.-->
|
||||||
<Grid IsVisible="{Binding IsPolicyInEffect}"
|
<ContentView>
|
||||||
Margin="0, 12, 0, 0"
|
<ScrollView Padding="0, 0, 0, 20">
|
||||||
RowSpacing="0"
|
<StackLayout Spacing="0" Padding="0">
|
||||||
ColumnSpacing="0">
|
<StackLayout StyleClass="box">
|
||||||
<Grid.RowDefinitions>
|
<Grid IsVisible="{Binding IsPolicyInEffect}"
|
||||||
<RowDefinition Height="Auto" />
|
Margin="0, 12, 0, 0"
|
||||||
<RowDefinition Height="*" />
|
RowSpacing="0"
|
||||||
</Grid.RowDefinitions>
|
ColumnSpacing="0">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<RowDefinition Height="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Frame Padding="10"
|
<Grid.ColumnDefinitions>
|
||||||
Margin="0"
|
<ColumnDefinition Width="*" />
|
||||||
HasShadow="False"
|
<ColumnDefinition Width="Auto" />
|
||||||
BackgroundColor="Transparent"
|
</Grid.ColumnDefinitions>
|
||||||
BorderColor="Accent">
|
<Frame Padding="10"
|
||||||
<Label
|
Margin="0"
|
||||||
Text="{u:I18n PasswordGeneratorPolicyInEffect}"
|
HasShadow="False"
|
||||||
StyleClass="text-muted, text-sm, text-bold"
|
BackgroundColor="Transparent"
|
||||||
HorizontalTextAlignment="Center" />
|
BorderColor="Accent">
|
||||||
</Frame>
|
<Label
|
||||||
</Grid>
|
Text="{u:I18n PasswordGeneratorPolicyInEffect}"
|
||||||
<controls:MonoLabel
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
x:Name="lblPassword"
|
HorizontalTextAlignment="Center" />
|
||||||
StyleClass="text-lg, text-html"
|
</Frame>
|
||||||
Text="{Binding ColoredPassword, Mode=OneWay}"
|
</Grid>
|
||||||
Margin="0, 20"
|
<controls:MonoLabel
|
||||||
HorizontalTextAlignment="Center"
|
x:Name="lblPassword"
|
||||||
HorizontalOptions="CenterAndExpand"
|
StyleClass="text-lg, text-html"
|
||||||
LineBreakMode="CharacterWrap" />
|
Text="{Binding ColoredPassword, Mode=OneWay}"
|
||||||
<Button Text="{u:I18n RegeneratePassword}"
|
Margin="0, 20"
|
||||||
StyleClass="btn-primary"
|
HorizontalTextAlignment="Center"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
Clicked="Regenerate_Clicked"></Button>
|
LineBreakMode="CharacterWrap" />
|
||||||
<Button Text="{u:I18n CopyPassword}"
|
<Button Text="{u:I18n RegeneratePassword}"
|
||||||
HorizontalOptions="FillAndExpand"
|
StyleClass="btn-primary"
|
||||||
Clicked="Copy_Clicked"></Button>
|
|
||||||
</StackLayout>
|
|
||||||
<StackLayout StyleClass="box">
|
|
||||||
<StackLayout StyleClass="box-row-header">
|
|
||||||
<Label Text="{u:I18n Options, Header=True}"
|
|
||||||
StyleClass="box-header, box-header-platform" />
|
|
||||||
</StackLayout>
|
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n Type}"
|
|
||||||
StyleClass="box-label" />
|
|
||||||
<Picker
|
|
||||||
x:Name="_typePicker"
|
|
||||||
ItemsSource="{Binding TypeOptions, Mode=OneTime}"
|
|
||||||
SelectedIndex="{Binding TypeSelectedIndex}"
|
|
||||||
StyleClass="box-value" />
|
|
||||||
</StackLayout>
|
|
||||||
<StackLayout Spacing="0"
|
|
||||||
Padding="0"
|
|
||||||
IsVisible="{Binding IsPassword, Converter={StaticResource inverseBool}}">
|
|
||||||
<StackLayout StyleClass="box-row, box-row-stepper">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n NumberOfWords}"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
VerticalTextAlignment="Center" />
|
|
||||||
<Label
|
|
||||||
Text="{Binding NumWords}"
|
|
||||||
StyleClass="box-sub-label"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
HorizontalTextAlignment="End"
|
Clicked="Regenerate_Clicked"></Button>
|
||||||
VerticalOptions="FillAndExpand"
|
<Button Text="{u:I18n CopyPassword}"
|
||||||
VerticalTextAlignment="Center" />
|
HorizontalOptions="FillAndExpand"
|
||||||
<controls:ExtendedStepper
|
Clicked="Copy_Clicked"></Button>
|
||||||
Value="{Binding NumWords}"
|
</StackLayout>
|
||||||
Maximum="20"
|
<StackLayout StyleClass="box">
|
||||||
Minimum="3"
|
<StackLayout StyleClass="box-row-header">
|
||||||
Increment="1" />
|
<Label Text="{u:I18n Options, Header=True}"
|
||||||
|
StyleClass="box-header, box-header-platform" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n WordSeparator}"
|
Text="{u:I18n Type}"
|
||||||
StyleClass="box-label" />
|
StyleClass="box-label" />
|
||||||
<Entry
|
<Picker
|
||||||
Text="{Binding WordSeparator}"
|
x:Name="_typePicker"
|
||||||
IsSpellCheckEnabled="False"
|
ItemsSource="{Binding TypeOptions, Mode=OneTime}"
|
||||||
IsTextPredictionEnabled="False"
|
SelectedIndex="{Binding TypeSelectedIndex}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout Spacing="0"
|
||||||
<Label
|
Padding="0"
|
||||||
Text="{u:I18n Capitalize}"
|
IsVisible="{Binding IsPassword, Converter={StaticResource inverseBool}}">
|
||||||
StyleClass="box-label-regular"
|
<StackLayout StyleClass="box-row, box-row-stepper">
|
||||||
HorizontalOptions="StartAndExpand" />
|
<Label
|
||||||
<Switch
|
Text="{u:I18n NumberOfWords}"
|
||||||
IsToggled="{Binding Capitalize}"
|
StyleClass="box-label-regular"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.Capitalize,
|
VerticalOptions="FillAndExpand"
|
||||||
Converter={StaticResource inverseBool}}"
|
VerticalTextAlignment="Center" />
|
||||||
StyleClass="box-value"
|
<Label
|
||||||
HorizontalOptions="End" />
|
Text="{Binding NumWords}"
|
||||||
|
StyleClass="box-sub-label"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
HorizontalTextAlignment="End"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
VerticalTextAlignment="Center" />
|
||||||
|
<controls:ExtendedStepper
|
||||||
|
Value="{Binding NumWords}"
|
||||||
|
Maximum="20"
|
||||||
|
Minimum="3"
|
||||||
|
Increment="1" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n WordSeparator}"
|
||||||
|
StyleClass="box-label" />
|
||||||
|
<Entry
|
||||||
|
Text="{Binding WordSeparator}"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
IsTextPredictionEnabled="False"
|
||||||
|
StyleClass="box-value" />
|
||||||
|
</StackLayout>
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n Capitalize}"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding Capitalize}"
|
||||||
|
IsEnabled="{Binding EnforcedPolicyOptions.Capitalize,
|
||||||
|
Converter={StaticResource inverseBool}}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n IncludeNumber}"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding IncludeNumber}"
|
||||||
|
IsEnabled="{Binding EnforcedPolicyOptions.IncludeNumber,
|
||||||
|
Converter={StaticResource inverseBool}}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding IsPassword}">
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-slider">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n IncludeNumber}"
|
Text="{u:I18n Length}"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand" />
|
VerticalOptions="CenterAndExpand" />
|
||||||
<Switch
|
<Label
|
||||||
IsToggled="{Binding IncludeNumber}"
|
Text="{Binding Length}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.IncludeNumber,
|
StyleClass="box-sub-label"
|
||||||
Converter={StaticResource inverseBool}}"
|
VerticalOptions="CenterAndExpand"
|
||||||
StyleClass="box-value"
|
HorizontalTextAlignment="End"
|
||||||
HorizontalOptions="End" />
|
WidthRequest="50" />
|
||||||
|
<controls:ExtendedSlider
|
||||||
|
DragCompleted="LengthSlider_DragCompleted"
|
||||||
|
Value="{Binding Length}"
|
||||||
|
AutomationProperties.HelpText="{Binding Length}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
VerticalOptions="CenterAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
Maximum="128"
|
||||||
|
Minimum="5" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="A-Z"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding Uppercase}"
|
||||||
|
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
||||||
|
Converter={StaticResource inverseBool}}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="a-z"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding Lowercase}"
|
||||||
|
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
||||||
|
Converter={StaticResource inverseBool}}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="0-9"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding Number}"
|
||||||
|
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
||||||
|
Converter={StaticResource inverseBool}}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="!@#$%^&*"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding Special}"
|
||||||
|
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
||||||
|
Converter={StaticResource inverseBool}}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-stepper">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MinNumbers}"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
VerticalTextAlignment="Center" />
|
||||||
|
<Label
|
||||||
|
Text="{Binding MinNumber}"
|
||||||
|
StyleClass="box-sub-label"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
HorizontalTextAlignment="End"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
VerticalTextAlignment="Center" />
|
||||||
|
<controls:ExtendedStepper
|
||||||
|
Value="{Binding MinNumber}"
|
||||||
|
Maximum="5"
|
||||||
|
Minimum="0"
|
||||||
|
Increment="1" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-stepper">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MinSpecial}"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
VerticalTextAlignment="Center" />
|
||||||
|
<Label
|
||||||
|
Text="{Binding MinSpecial}"
|
||||||
|
StyleClass="box-sub-label"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
HorizontalTextAlignment="End"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
VerticalTextAlignment="Center" />
|
||||||
|
<controls:ExtendedStepper
|
||||||
|
Value="{Binding MinSpecial}"
|
||||||
|
Maximum="5"
|
||||||
|
Minimum="0"
|
||||||
|
Increment="1" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n AvoidAmbiguousCharacters}"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding AvoidAmbiguous}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="box-row-separator" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding IsPassword}">
|
|
||||||
<StackLayout StyleClass="box-row, box-row-slider">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n Length}"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
VerticalOptions="CenterAndExpand" />
|
|
||||||
<Label
|
|
||||||
Text="{Binding Length}"
|
|
||||||
StyleClass="box-sub-label"
|
|
||||||
WidthRequest="30"
|
|
||||||
VerticalOptions="CenterAndExpand"
|
|
||||||
HorizontalTextAlignment="End" />
|
|
||||||
<controls:ExtendedSlider
|
|
||||||
DragCompleted="LengthSlider_DragCompleted"
|
|
||||||
Value="{Binding Length}"
|
|
||||||
AutomationProperties.HelpText="{Binding Length}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
VerticalOptions="CenterAndExpand"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
Maximum="128"
|
|
||||||
Minimum="5" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
|
||||||
<Label
|
|
||||||
Text="A-Z"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
HorizontalOptions="StartAndExpand" />
|
|
||||||
<Switch
|
|
||||||
IsToggled="{Binding Uppercase}"
|
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
|
||||||
Converter={StaticResource inverseBool}}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
HorizontalOptions="End" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
|
||||||
<Label
|
|
||||||
Text="a-z"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
HorizontalOptions="StartAndExpand" />
|
|
||||||
<Switch
|
|
||||||
IsToggled="{Binding Lowercase}"
|
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
|
||||||
Converter={StaticResource inverseBool}}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
HorizontalOptions="End" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
|
||||||
<Label
|
|
||||||
Text="0-9"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
HorizontalOptions="StartAndExpand" />
|
|
||||||
<Switch
|
|
||||||
IsToggled="{Binding Number}"
|
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
|
||||||
Converter={StaticResource inverseBool}}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
HorizontalOptions="End" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
|
||||||
<Label
|
|
||||||
Text="!@#$%^&*"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
HorizontalOptions="StartAndExpand" />
|
|
||||||
<Switch
|
|
||||||
IsToggled="{Binding Special}"
|
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
|
||||||
Converter={StaticResource inverseBool}}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
HorizontalOptions="End" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-stepper">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n MinNumbers}"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
VerticalTextAlignment="Center" />
|
|
||||||
<Label
|
|
||||||
Text="{Binding MinNumber}"
|
|
||||||
StyleClass="box-sub-label"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
HorizontalTextAlignment="End"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
VerticalTextAlignment="Center" />
|
|
||||||
<controls:ExtendedStepper
|
|
||||||
Value="{Binding MinNumber}"
|
|
||||||
Maximum="5"
|
|
||||||
Minimum="0"
|
|
||||||
Increment="1" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-stepper">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n MinSpecial}"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
VerticalTextAlignment="Center" />
|
|
||||||
<Label
|
|
||||||
Text="{Binding MinSpecial}"
|
|
||||||
StyleClass="box-sub-label"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
HorizontalTextAlignment="End"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
VerticalTextAlignment="Center" />
|
|
||||||
<controls:ExtendedStepper
|
|
||||||
Value="{Binding MinSpecial}"
|
|
||||||
Maximum="5"
|
|
||||||
Minimum="0"
|
|
||||||
Increment="1" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n AvoidAmbiguousCharacters}"
|
|
||||||
StyleClass="box-label-regular"
|
|
||||||
HorizontalOptions="StartAndExpand" />
|
|
||||||
<Switch
|
|
||||||
IsToggled="{Binding AvoidAmbiguous}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
HorizontalOptions="End" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
</StackLayout>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</ScrollView>
|
||||||
</ScrollView>
|
</ContentView>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ using Bit.App.Utilities;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.PlatformConfiguration;
|
using Xamarin.Forms.PlatformConfiguration;
|
||||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||||
@@ -21,6 +18,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||||
|
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
private SendAddEditPageViewModel _vm;
|
private SendAddEditPageViewModel _vm;
|
||||||
@@ -55,9 +53,10 @@ namespace Bit.App.Pages
|
|||||||
_vm.SegmentedButtonFontSize = 13;
|
_vm.SegmentedButtonFontSize = 13;
|
||||||
_vm.SegmentedButtonMargins = new Thickness(0, 10, 0, 0);
|
_vm.SegmentedButtonMargins = new Thickness(0, 10, 0, 0);
|
||||||
_vm.EditorMargins = new Thickness(0, 5, 0, 0);
|
_vm.EditorMargins = new Thickness(0, 5, 0, 0);
|
||||||
_btnOptions.WidthRequest = 70;
|
// Review this when https://github.com/bitwarden/mobile/pull/1454 workaround can be reverted
|
||||||
_btnOptionsDown.WidthRequest = 30;
|
//_btnOptions.WidthRequest = 70;
|
||||||
_btnOptionsUp.WidthRequest = 30;
|
//_btnOptionsDown.WidthRequest = 30;
|
||||||
|
//_btnOptionsUp.WidthRequest = 30;
|
||||||
}
|
}
|
||||||
else if (Device.RuntimePlatform == Device.iOS)
|
else if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
@@ -131,9 +130,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Value.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
await CloseAsync();
|
await CloseAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -23,8 +20,9 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ISendService _sendService;
|
private readonly ISendService _sendService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private bool _sendEnabled;
|
private bool _sendEnabled;
|
||||||
private bool _canAccessPremium;
|
private bool _canAccessPremium;
|
||||||
private bool _emailVerified;
|
private bool _emailVerified;
|
||||||
@@ -56,8 +54,10 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
|
||||||
TypeOptions = new List<KeyValuePair<string, SendType>>
|
TypeOptions = new List<KeyValuePair<string, SendType>>
|
||||||
@@ -235,8 +235,8 @@ namespace Bit.App.Pages
|
|||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
||||||
_canAccessPremium = await _userService.CanAccessPremiumAsync();
|
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
_emailVerified = await _userService.GetEmailVerifiedAsync();
|
_emailVerified = await _stateService.GetEmailVerifiedAsync();
|
||||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||||
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
||||||
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
||||||
@@ -455,9 +455,7 @@ namespace Bit.App.Pages
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
#if !FDROID
|
_logger.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
|
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
|
||||||
HorizontalOptions="Start"
|
HorizontalOptions="Start"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
StyleClass="list-icon, list-icon-platform">
|
StyleClass="list-icon, list-icon-platform"
|
||||||
|
ShouldUpdateFontSizeDynamicallyForAccesibility="True">
|
||||||
<controls:IconLabel.Effects>
|
<controls:IconLabel.Effects>
|
||||||
<effects:FixedSizeEffect />
|
<effects:FixedSizeEffect />
|
||||||
</controls:IconLabel.Effects>
|
</controls:IconLabel.Effects>
|
||||||
|
|||||||
@@ -33,21 +33,19 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private readonly ISendService _sendService;
|
private readonly ISendService _sendService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
|
|
||||||
public SendGroupingsPageViewModel()
|
public SendGroupingsPageViewModel()
|
||||||
{
|
{
|
||||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
|
|
||||||
Loading = true;
|
Loading = true;
|
||||||
PageTitle = AppResources.Send;
|
PageTitle = AppResources.Send;
|
||||||
@@ -117,7 +115,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var authed = await _userService.IsAuthenticatedAsync();
|
var authed = await _stateService.IsAuthenticatedAsync();
|
||||||
if (!authed)
|
if (!authed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -126,7 +124,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||||
{
|
{
|
||||||
SyncRefreshing = true;
|
SyncRefreshing = true;
|
||||||
await _syncService.FullSyncAsync(false);
|
await _syncService.FullSyncAsync(false);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Services;
|
using Bit.App.Services;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ namespace Bit.App.Pages
|
|||||||
public class AutofillServicesPageViewModel : BaseViewModel
|
public class AutofillServicesPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly MobileI18nService _i18nService;
|
private readonly MobileI18nService _i18nService;
|
||||||
|
|
||||||
private bool _autofillServiceToggled;
|
private bool _autofillServiceToggled;
|
||||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
|||||||
public AutofillServicesPageViewModel()
|
public AutofillServicesPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
||||||
PageTitle = AppResources.AutofillServices;
|
PageTitle = AppResources.AutofillServices;
|
||||||
}
|
}
|
||||||
@@ -152,7 +151,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
InlineAutofillToggled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||||
_inited = true;
|
_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +201,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.InlineAutofillEnabledKey, InlineAutofillToggled);
|
await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -26,6 +23,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly IUserVerificationService _userVerificationService;
|
private readonly IUserVerificationService _userVerificationService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private int _fileFormatSelectedIndex;
|
private int _fileFormatSelectedIndex;
|
||||||
private string _exportWarningMessage;
|
private string _exportWarningMessage;
|
||||||
@@ -48,6 +46,7 @@ namespace Bit.App.Pages
|
|||||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>("userVerificationService");
|
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>("userVerificationService");
|
||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.ExportVault;
|
PageTitle = AppResources.ExportVault;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -189,9 +188,7 @@ namespace Bit.App.Pages
|
|||||||
ClearResult();
|
ClearResult();
|
||||||
await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure"));
|
await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure"));
|
||||||
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
|
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
|
||||||
#if !FDROID
|
_logger.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class ExtensionPageViewModel : BaseViewModel
|
public class ExtensionPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private const string StartedKey = "appExtensionStarted";
|
|
||||||
private const string ActivatedKey = "appExtensionActivated";
|
|
||||||
|
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
|
||||||
|
|
||||||
private bool _started;
|
private bool _started;
|
||||||
private bool _activated;
|
private bool _activated;
|
||||||
@@ -20,8 +15,6 @@ namespace Bit.App.Pages
|
|||||||
public ExtensionPageViewModel()
|
public ExtensionPageViewModel()
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
|
||||||
PageTitle = AppResources.AppExtension;
|
PageTitle = AppResources.AppExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,10 +45,8 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
var started = await _storageService.GetAsync<bool?>(StartedKey);
|
Started = false;
|
||||||
var activated = await _storageService.GetAsync<bool?>(ActivatedKey);
|
Activated = false;
|
||||||
Started = started.GetValueOrDefault();
|
|
||||||
Activated = activated.GetValueOrDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowExtension()
|
public void ShowExtension()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
@@ -13,9 +12,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class OptionsPageViewModel : BaseViewModel
|
public class OptionsPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly ITotpService _totpService;
|
private readonly ITotpService _totpService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
@@ -34,9 +30,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public OptionsPageViewModel()
|
public OptionsPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
@@ -166,19 +159,17 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
AutofillDisableSavePrompt = (await _storageService.GetAsync<bool?>(
|
AutofillDisableSavePrompt = (await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
|
||||||
Constants.AutofillDisableSavePromptKey)).GetValueOrDefault();
|
var blacklistedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||||
var blacklistedUrisList = await _storageService.GetAsync<List<string>>(
|
|
||||||
Constants.AutofillBlacklistedUrisKey);
|
|
||||||
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
|
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
|
||||||
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
|
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
|
||||||
DisableFavicon = (await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey)).GetValueOrDefault();
|
DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
var theme = await _storageService.GetAsync<string>(Constants.ThemeKey);
|
var theme = await _stateService.GetThemeAsync();
|
||||||
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
|
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
|
||||||
var defaultUriMatch = await _storageService.GetAsync<int?>(Constants.DefaultUriMatch);
|
var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
|
||||||
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
|
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
|
||||||
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
|
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
|
||||||
var clearClipboard = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
var clearClipboard = await _stateService.GetClearClipboardAsync();
|
||||||
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
|
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
|
||||||
_inited = true;
|
_inited = true;
|
||||||
}
|
}
|
||||||
@@ -187,7 +178,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.DisableAutoTotpCopyKey, DisableAutoTotpCopy);
|
await _stateService.SetDisableAutoTotpCopyAsync(DisableAutoTotpCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,8 +186,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
await _stateService.SetDisableFaviconAsync(DisableFavicon);
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +194,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited && ClearClipboardSelectedIndex > -1)
|
if (_inited && ClearClipboardSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.ClearClipboardKey,
|
await _stateService.SetClearClipboardAsync(ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||||
ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +203,8 @@ namespace Bit.App.Pages
|
|||||||
if (_inited && ThemeSelectedIndex > -1)
|
if (_inited && ThemeSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
var theme = ThemeOptions[ThemeSelectedIndex].Key;
|
var theme = ThemeOptions[ThemeSelectedIndex].Key;
|
||||||
await _storageService.SaveAsync(Constants.ThemeKey, theme);
|
await _stateService.SetThemeAsync(theme);
|
||||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Application.Current.Resources);
|
ThemeManager.SetTheme(Application.Current.Resources);
|
||||||
_messagingService.Send("updatedTheme");
|
_messagingService.Send("updatedTheme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,8 +213,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited && UriMatchSelectedIndex > -1)
|
if (_inited && UriMatchSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.DefaultUriMatch,
|
await _stateService.SetDefaultUriMatchAsync((int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||||
(int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +221,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.AutofillDisableSavePromptKey, AutofillDisableSavePrompt);
|
await _stateService.SetAutofillDisableSavePromptAsync(AutofillDisableSavePrompt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +231,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
|
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Constants.AutofillBlacklistedUrisKey);
|
await _stateService.SetAutofillBlacklistedUrisAsync(null);
|
||||||
AutofillBlacklistedUris = null;
|
AutofillBlacklistedUris = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -265,7 +253,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
urisList.Add(cleanedUri);
|
urisList.Add(cleanedUri);
|
||||||
}
|
}
|
||||||
await _storageService.SaveAsync(Constants.AutofillBlacklistedUrisKey, urisList);
|
await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
|
||||||
AutofillBlacklistedUris = string.Join(", ", urisList);
|
AutofillBlacklistedUris = string.Join(", ", urisList);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -19,12 +18,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IBiometricService _biometricService;
|
private readonly IBiometricService _biometricService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
@@ -56,11 +54,11 @@ namespace Bit.App.Pages
|
|||||||
new KeyValuePair<string, int?>(AppResources.Never, null),
|
new KeyValuePair<string, int?>(AppResources.Never, null),
|
||||||
new KeyValuePair<string, int?>(AppResources.Custom, CustomVaultTimeoutValue),
|
new KeyValuePair<string, int?>(AppResources.Custom, CustomVaultTimeoutValue),
|
||||||
};
|
};
|
||||||
private List<KeyValuePair<string, string>> _vaultTimeoutActions =
|
private List<KeyValuePair<string, VaultTimeoutAction>> _vaultTimeoutActions =
|
||||||
new List<KeyValuePair<string, string>>
|
new List<KeyValuePair<string, VaultTimeoutAction>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(AppResources.Lock, "lock"),
|
new KeyValuePair<string, VaultTimeoutAction>(AppResources.Lock, VaultTimeoutAction.Lock),
|
||||||
new KeyValuePair<string, string>(AppResources.LogOut, "logOut"),
|
new KeyValuePair<string, VaultTimeoutAction>(AppResources.LogOut, VaultTimeoutAction.Logout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private Policy _vaultTimeoutPolicy;
|
private Policy _vaultTimeoutPolicy;
|
||||||
@@ -70,12 +68,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
@@ -113,7 +110,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
|
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
|
||||||
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
|
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
|
||||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey) ?? "lock";
|
var action = await _stateService.GetVaultTimeoutActionAsync() ?? VaultTimeoutAction.Lock;
|
||||||
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
|
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
|
||||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
_pin = pinSet.Item1 || pinSet.Item2;
|
_pin = pinSet.Item1 || pinSet.Item2;
|
||||||
@@ -137,10 +134,10 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
var pushNotificationsRegistered = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService").IsRegisteredForPush;
|
var pushNotificationsRegistered = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService").IsRegisteredForPush;
|
||||||
var pnServerRegDate = await _storageService.GetAsync<DateTime>(Constants.PushLastRegistrationDateKey);
|
var pnServerRegDate = await _stateService.GetPushLastRegistrationDateAsync();
|
||||||
var pnServerError = await _storageService.GetAsync<string>(Constants.PushInstallationRegistrationError);
|
var pnServerError = await _stateService.GetPushInstallationRegistrationErrorAsync();
|
||||||
|
|
||||||
var pnServerRegDateMessage = default(DateTime) == pnServerRegDate ? "-" : $"{pnServerRegDate.ToShortDateString()}-{pnServerRegDate.ToShortTimeString()} UTC";
|
var pnServerRegDateMessage = default(DateTime) == pnServerRegDate ? "-" : $"{pnServerRegDate.GetValueOrDefault().ToShortDateString()}-{pnServerRegDate.GetValueOrDefault().ToShortTimeString()} UTC";
|
||||||
var errorMessage = string.IsNullOrEmpty(pnServerError) ? string.Empty : $"Push Notifications Server Registration error: {pnServerError}";
|
var errorMessage = string.IsNullOrEmpty(pnServerError) ? string.Empty : $"Push Notifications Server Registration error: {pnServerError}";
|
||||||
|
|
||||||
var text = string.Format("© Bitwarden Inc. 2015-{0}\n\n{1}\nPush Notifications registered:{2}\nPush Notifications Server Last Date :{3}\n{4}", DateTime.Now.Year, debugText, pushNotificationsRegistered, pnServerRegDateMessage, errorMessage);
|
var text = string.Format("© Bitwarden Inc. 2015-{0}\n\n{1}\nPush Notifications registered:{2}\nPush Notifications Server Last Date :{3}\n{4}", DateTime.Now.Year, debugText, pushNotificationsRegistered, pnServerRegDateMessage, errorMessage);
|
||||||
@@ -166,7 +163,7 @@ namespace Bit.App.Pages
|
|||||||
List<string> fingerprint;
|
List<string> fingerprint;
|
||||||
try
|
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.")
|
catch (Exception e) when (e.Message == "No public key available.")
|
||||||
{
|
{
|
||||||
@@ -194,12 +191,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public void WebVault()
|
public void WebVault()
|
||||||
{
|
{
|
||||||
var url = _environmentService.GetWebVaultUrl();
|
_platformUtilsService.LaunchUri(_environmentService.GetWebVaultUrl());
|
||||||
if (url == null)
|
|
||||||
{
|
|
||||||
url = "https://vault.bitwarden.com";
|
|
||||||
}
|
|
||||||
_platformUtilsService.LaunchUri(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ShareAsync()
|
public async Task ShareAsync()
|
||||||
@@ -218,7 +210,7 @@ namespace Bit.App.Pages
|
|||||||
AppResources.TwoStepLogin, AppResources.Yes, AppResources.Cancel);
|
AppResources.TwoStepLogin, AppResources.Yes, AppResources.Cancel);
|
||||||
if (confirmed)
|
if (confirmed)
|
||||||
{
|
{
|
||||||
_platformUtilsService.LaunchUri("https://bitwarden.com/help/setup-two-step-login/");
|
_platformUtilsService.LaunchUri($"{_environmentService.GetWebVaultUrl()}/#/settings");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +220,7 @@ namespace Bit.App.Pages
|
|||||||
AppResources.ChangeMasterPassword, AppResources.Yes, AppResources.Cancel);
|
AppResources.ChangeMasterPassword, AppResources.Yes, AppResources.Cancel);
|
||||||
if (confirmed)
|
if (confirmed)
|
||||||
{
|
{
|
||||||
_platformUtilsService.LaunchUri("https://bitwarden.com/help/master-password/#change-your-master-password");
|
_platformUtilsService.LaunchUri($"{_environmentService.GetWebVaultUrl()}/#/settings");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,9 +336,9 @@ namespace Bit.App.Pages
|
|||||||
AppResources.Yes, AppResources.No);
|
AppResources.Yes, AppResources.No);
|
||||||
}
|
}
|
||||||
|
|
||||||
var kdf = await _userService.GetKdfAsync();
|
var kdf = await _stateService.GetKdfTypeAsync();
|
||||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
||||||
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
||||||
kdfIterations.GetValueOrDefault(5000));
|
kdfIterations.GetValueOrDefault(5000));
|
||||||
@@ -356,12 +348,12 @@ namespace Bit.App.Pages
|
|||||||
if (masterPassOnRestart)
|
if (masterPassOnRestart)
|
||||||
{
|
{
|
||||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||||
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString);
|
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||||
_vaultTimeoutService.PinProtectedKey = pinProtectedKey;
|
await _stateService.SetPinProtectedKeyAsync(pinProtectedKey);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString);
|
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -396,13 +388,13 @@ namespace Bit.App.Pages
|
|||||||
if (_biometric)
|
if (_biometric)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync();
|
await _biometricService.SetupBiometricAsync();
|
||||||
await _storageService.SaveAsync(Constants.BiometricUnlockKey, true);
|
await _stateService.SetBiometricUnlockAsync(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Constants.BiometricUnlockKey);
|
await _stateService.SetBiometricUnlockAsync(null);
|
||||||
}
|
}
|
||||||
_vaultTimeoutService.BiometricLocked = false;
|
await _stateService.SetBiometricLockedAsync(false);
|
||||||
await _cryptoService.ToggleKeyAsync();
|
await _cryptoService.ToggleKeyAsync();
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
@@ -575,7 +567,7 @@ namespace Bit.App.Pages
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetVaultTimeoutActionFromKey(string key)
|
private VaultTimeoutAction GetVaultTimeoutActionFromKey(string key)
|
||||||
{
|
{
|
||||||
return _vaultTimeoutActions.FirstOrDefault(o => o.Key == key).Value;
|
return _vaultTimeoutActions.FirstOrDefault(o => o.Key == key).Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -12,7 +11,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ namespace Bit.App.Pages
|
|||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
await SetLastSyncAsync();
|
await SetLastSyncAsync();
|
||||||
EnableSyncOnRefresh = await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey);
|
EnableSyncOnRefresh = await _stateService.GetSyncOnRefreshAsync();
|
||||||
_inited = true;
|
_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.SyncOnRefreshKey, _syncOnRefresh);
|
await _stateService.SetSyncOnRefreshAsync(_syncOnRefresh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ namespace Bit.App.Pages
|
|||||||
var settingsPage = new NavigationPage(new SettingsPage(this))
|
var settingsPage = new NavigationPage(new SettingsPage(this))
|
||||||
{
|
{
|
||||||
Title = AppResources.Settings,
|
Title = AppResources.Settings,
|
||||||
IconImageSource = "cog.png"
|
IconImageSource = "cog_settings.png"
|
||||||
};
|
};
|
||||||
Children.Add(settingsPage);
|
Children.Add(settingsPage);
|
||||||
|
|
||||||
@@ -102,8 +103,13 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (CurrentPage is NavigationPage navPage)
|
if (CurrentPage is NavigationPage navPage)
|
||||||
{
|
{
|
||||||
|
if (_groupingsPage?.RootPage is GroupingsPage groupingsPage)
|
||||||
|
{
|
||||||
|
await groupingsPage.HideAccountSwitchingOverlayAsync();
|
||||||
|
}
|
||||||
|
|
||||||
_messagingService.Send("updatedTheme");
|
_messagingService.Send("updatedTheme");
|
||||||
if (navPage.RootPage is GroupingsPage groupingsPage)
|
if (navPage.RootPage is GroupingsPage)
|
||||||
{
|
{
|
||||||
// Load something?
|
// Load something?
|
||||||
}
|
}
|
||||||
@@ -117,5 +123,13 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnPageReselected()
|
||||||
|
{
|
||||||
|
if (_groupingsPage?.RootPage is GroupingsPage groupingsPage)
|
||||||
|
{
|
||||||
|
groupingsPage.HideAccountSwitchingOverlayAsync().FireAndForget();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,7 +338,7 @@
|
|||||||
<Entry
|
<Entry
|
||||||
x:Name="_identityFirstNameEntry"
|
x:Name="_identityFirstNameEntry"
|
||||||
Text="{Binding Cipher.Identity.FirstName}"
|
Text="{Binding Cipher.Identity.FirstName}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value,capitalize-word-input"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
<Label
|
<Label
|
||||||
@@ -347,7 +347,7 @@
|
|||||||
<Entry
|
<Entry
|
||||||
x:Name="_identityMiddleNameEntry"
|
x:Name="_identityMiddleNameEntry"
|
||||||
Text="{Binding Cipher.Identity.MiddleName}"
|
Text="{Binding Cipher.Identity.MiddleName}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value,capitalize-word-input" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
<Label
|
<Label
|
||||||
@@ -356,7 +356,7 @@
|
|||||||
<Entry
|
<Entry
|
||||||
x:Name="_identityLastNameEntry"
|
x:Name="_identityLastNameEntry"
|
||||||
Text="{Binding Cipher.Identity.LastName}"
|
Text="{Binding Cipher.Identity.LastName}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value,capitalize-word-input" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
<Label
|
<Label
|
||||||
@@ -420,6 +420,7 @@
|
|||||||
<Entry
|
<Entry
|
||||||
x:Name="_identityPhoneEntry"
|
x:Name="_identityPhoneEntry"
|
||||||
Text="{Binding Cipher.Identity.Phone}"
|
Text="{Binding Cipher.Identity.Phone}"
|
||||||
|
Keyboard="Telephone"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
@@ -456,7 +457,7 @@
|
|||||||
<Entry
|
<Entry
|
||||||
x:Name="_identityCityEntry"
|
x:Name="_identityCityEntry"
|
||||||
Text="{Binding Cipher.Identity.City}"
|
Text="{Binding Cipher.Identity.City}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value,capitalize-sentence-input" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
<Label
|
<Label
|
||||||
@@ -465,7 +466,7 @@
|
|||||||
<Entry
|
<Entry
|
||||||
x:Name="_identityStateEntry"
|
x:Name="_identityStateEntry"
|
||||||
Text="{Binding Cipher.Identity.State}"
|
Text="{Binding Cipher.Identity.State}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value,capitalize-sentence-input" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
<Label
|
<Label
|
||||||
@@ -483,7 +484,7 @@
|
|||||||
<Entry
|
<Entry
|
||||||
x:Name="_identityCountryEntry"
|
x:Name="_identityCountryEntry"
|
||||||
Text="{Binding Cipher.Identity.Country}"
|
Text="{Binding Cipher.Identity.Country}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value,capitalize-sentence-input" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
|
|||||||
public partial class AddEditPage : BaseContentPage
|
public partial class AddEditPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
@@ -38,7 +37,7 @@ namespace Bit.App.Pages
|
|||||||
bool cloneMode = false,
|
bool cloneMode = false,
|
||||||
ViewPage viewPage = null)
|
ViewPage viewPage = null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
@@ -333,10 +332,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var addLoginShown = await _storageService.GetAsync<bool?>(Constants.AddSitePromptShownKey);
|
var addLoginShown = await _stateService.GetAddSitePromptShownAsync();
|
||||||
if (_vm.Cipher.Type == CipherType.Login && !_fromAutofill && !addLoginShown.GetValueOrDefault())
|
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 (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
if (_deviceActionService.SystemMajorVersion() < 12)
|
if (_deviceActionService.SystemMajorVersion() < 12)
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Controls;
|
|
||||||
using Bit.Core;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
#if !FDROID
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -25,12 +22,15 @@ namespace Bit.App.Pages
|
|||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
private readonly ICollectionService _collectionService;
|
private readonly ICollectionService _collectionService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IAuditService _auditService;
|
private readonly IAuditService _auditService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private bool _showNotesSeparator;
|
private bool _showNotesSeparator;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
@@ -67,19 +67,22 @@ namespace Bit.App.Pages
|
|||||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Exact, AppResources.Exact),
|
new KeyValuePair<UriMatchType?, string>(UriMatchType.Exact, AppResources.Exact),
|
||||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never)
|
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never)
|
||||||
};
|
};
|
||||||
|
|
||||||
public AddEditPageViewModel()
|
public AddEditPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
_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");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
GeneratePasswordCommand = new Command(GeneratePassword);
|
GeneratePasswordCommand = new Command(GeneratePassword);
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
||||||
@@ -305,9 +308,9 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task<bool> LoadAsync(AppOptions appOptions = null)
|
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));
|
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))
|
foreach (var org in orgs.OrderBy(o => o.Name))
|
||||||
{
|
{
|
||||||
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
|
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
|
||||||
@@ -536,9 +539,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch(Exception genex)
|
catch(Exception genex)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Exception(genex);
|
||||||
Crashes.TrackError(genex);
|
|
||||||
#endif
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IVaultTimeoutService _timeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private Cipher _cipherDomain;
|
private Cipher _cipherDomain;
|
||||||
@@ -33,8 +33,8 @@ namespace Bit.App.Pages
|
|||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_timeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
Attachments = new ExtendedObservableCollection<AttachmentView>();
|
Attachments = new ExtendedObservableCollection<AttachmentView>();
|
||||||
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
|
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
|
||||||
PageTitle = AppResources.Attachments;
|
PageTitle = AppResources.Attachments;
|
||||||
@@ -66,7 +66,7 @@ namespace Bit.App.Pages
|
|||||||
Cipher = await _cipherDomain.DecryptAsync();
|
Cipher = await _cipherDomain.DecryptAsync();
|
||||||
LoadAttachments();
|
LoadAttachments();
|
||||||
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
||||||
var canAccessPremium = await _userService.CanAccessPremiumAsync();
|
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
||||||
if (!_canAccessAttachments)
|
if (!_canAccessAttachments)
|
||||||
{
|
{
|
||||||
@@ -140,7 +140,7 @@ namespace Bit.App.Pages
|
|||||||
// Prevent Android from locking if vault timeout set to "immediate"
|
// Prevent Android from locking if vault timeout set to "immediate"
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
{
|
{
|
||||||
_timeoutService.DelayLockAndLogoutMs = 60000;
|
_vaultTimeoutService.DelayLockAndLogoutMs = 60000;
|
||||||
}
|
}
|
||||||
await _deviceActionService.SelectFileAsync();
|
await _deviceActionService.SelectFileAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,8 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@@ -89,8 +87,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task LoadAsync()
|
public async Task LoadAsync()
|
||||||
{
|
{
|
||||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
.GetValueOrDefault();
|
|
||||||
ShowList = false;
|
ShowList = false;
|
||||||
var groupedItems = new List<GroupingsPageListGroup>();
|
var groupedItems = new List<GroupingsPageListGroup>();
|
||||||
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
|
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
|
||||||
|
|||||||
@@ -76,8 +76,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
.GetValueOrDefault();
|
|
||||||
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
||||||
{
|
{
|
||||||
Search((Page as CiphersPage).SearchBar.Text, 200);
|
Search((Page as CiphersPage).SearchBar.Text, 200);
|
||||||
|
|||||||
@@ -15,6 +15,15 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
|
<controls:ExtendedToolbarItem
|
||||||
|
x:Name="_accountAvatar"
|
||||||
|
IconImageSource="{Binding AvatarImageSource}"
|
||||||
|
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
<ToolbarItem Icon="search.png" Clicked="Search_Clicked"
|
<ToolbarItem Icon="search.png" Clicked="Search_Clicked"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Search}" />
|
AutomationProperties.Name="{u:I18n Search}" />
|
||||||
@@ -48,9 +57,10 @@
|
|||||||
<controls:ExtendedStackLayout Orientation="Horizontal"
|
<controls:ExtendedStackLayout Orientation="Horizontal"
|
||||||
StyleClass="list-row, list-row-platform">
|
StyleClass="list-row, list-row-platform">
|
||||||
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
|
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
|
||||||
HorizontalOptions="Start"
|
HorizontalOptions="Start"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
StyleClass="list-icon, list-icon-platform">
|
StyleClass="list-icon, list-icon-platform"
|
||||||
|
ShouldUpdateFontSizeDynamicallyForAccesibility="True">
|
||||||
<controls:IconLabel.Effects>
|
<controls:IconLabel.Effects>
|
||||||
<effects:FixedSizeEffect />
|
<effects:FixedSizeEffect />
|
||||||
</controls:IconLabel.Effects>
|
</controls:IconLabel.Effects>
|
||||||
@@ -135,6 +145,8 @@
|
|||||||
AbsoluteLayout.LayoutFlags="All"
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||||
</ContentView>
|
</ContentView>
|
||||||
|
|
||||||
|
<!-- Android FAB -->
|
||||||
<Button
|
<Button
|
||||||
x:Name="_fab"
|
x:Name="_fab"
|
||||||
Image="plus.png"
|
Image="plus.png"
|
||||||
@@ -148,6 +160,14 @@
|
|||||||
<effects:FabShadowEffect />
|
<effects:FabShadowEffect />
|
||||||
</Button.Effects>
|
</Button.Effects>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<controls:AccountSwitchingOverlayView
|
||||||
|
x:Name="_accountListOverlay"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
MainPage="{Binding Source={x:Reference _page}}"
|
||||||
|
MainFab="{Binding Source={x:Reference _fab}, Path=.}"
|
||||||
|
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
|
||||||
</AbsoluteLayout>
|
</AbsoluteLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
using Bit.App.Models;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IPushNotificationService _pushNotificationService;
|
private readonly IPushNotificationService _pushNotificationService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
@@ -37,7 +36,7 @@ namespace Bit.App.Pages
|
|||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
@@ -70,6 +69,10 @@ namespace Bit.App.Pages
|
|||||||
_absLayout.Children.Remove(_fab);
|
_absLayout.Children.Remove(_fab);
|
||||||
ToolbarItems.Remove(_addItem);
|
ToolbarItems.Remove(_addItem);
|
||||||
}
|
}
|
||||||
|
if (!mainPage)
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_accountAvatar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
@@ -80,6 +83,12 @@ namespace Bit.App.Pages
|
|||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_accountAvatar?.OnAppearing();
|
||||||
|
if (_vm.MainPage)
|
||||||
|
{
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
}
|
||||||
|
|
||||||
_broadcasterService.Subscribe(_pageName, async (message) =>
|
_broadcasterService.Subscribe(_pageName, async (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "syncStarted")
|
if (message.Command == "syncStarted")
|
||||||
@@ -100,7 +109,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var migratedFromV1 = await _storageService.GetAsync<bool?>(Constants.MigratedFromV1);
|
|
||||||
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
||||||
{
|
{
|
||||||
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
||||||
@@ -123,18 +131,6 @@ namespace Bit.App.Pages
|
|||||||
await _vm.LoadAsync();
|
await _vm.LoadAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Forced sync if for some reason we have no data after a v1 migration
|
|
||||||
if (_vm.MainPage && !_syncService.SyncInProgress && migratedFromV1.GetValueOrDefault() &&
|
|
||||||
!_vm.HasCiphers &&
|
|
||||||
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
|
|
||||||
{
|
|
||||||
var triedV1ReSync = await _storageService.GetAsync<bool?>(Constants.TriedV1Resync);
|
|
||||||
if (!triedV1ReSync.GetValueOrDefault())
|
|
||||||
{
|
|
||||||
await _storageService.SaveAsync(Constants.TriedV1Resync, true);
|
|
||||||
await _syncService.FullSyncAsync(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await ShowPreviousPageAsync();
|
await ShowPreviousPageAsync();
|
||||||
AdjustToolbar();
|
AdjustToolbar();
|
||||||
}, _mainContent);
|
}, _mainContent);
|
||||||
@@ -145,14 +141,14 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push registration
|
// Push registration
|
||||||
var lastPushRegistration = await _storageService.GetAsync<DateTime?>(Constants.PushLastRegistrationDateKey);
|
var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
|
||||||
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
|
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
var pushPromptShow = await _storageService.GetAsync<bool?>(Constants.PushInitialPromptShownKey);
|
var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
|
||||||
if (!pushPromptShow.GetValueOrDefault(false))
|
if (!pushPromptShow.GetValueOrDefault(false))
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.PushInitialPromptShownKey, true);
|
await _stateService.SetPushInitialPromptShownAsync(true);
|
||||||
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
|
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
|
||||||
AppResources.OkGotIt);
|
AppResources.OkGotIt);
|
||||||
}
|
}
|
||||||
@@ -168,30 +164,26 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _pushNotificationService.RegisterAsync();
|
await _pushNotificationService.RegisterAsync();
|
||||||
}
|
}
|
||||||
if (!_deviceActionService.AutofillAccessibilityServiceRunning()
|
|
||||||
&& !_deviceActionService.AutofillServiceEnabled())
|
|
||||||
{
|
|
||||||
if (migratedFromV1.GetValueOrDefault())
|
|
||||||
{
|
|
||||||
var migratedFromV1AutofillPromptShown = await _storageService.GetAsync<bool?>(
|
|
||||||
Constants.MigratedFromV1AutofillPromptShown);
|
|
||||||
if (!migratedFromV1AutofillPromptShown.GetValueOrDefault())
|
|
||||||
{
|
|
||||||
await DisplayAlert(AppResources.Autofill,
|
|
||||||
AppResources.AutofillServiceNotEnabled, AppResources.Ok);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await _storageService.SaveAsync(Constants.MigratedFromV1AutofillPromptShown, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnBackButtonPressed()
|
||||||
|
{
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
_accountListOverlay.HideAsync().FireAndForget();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
{
|
{
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
_broadcasterService.Unsubscribe(_pageName);
|
_broadcasterService.Unsubscribe(_pageName);
|
||||||
_vm.DisableRefreshing();
|
_vm.DisableRefreshing();
|
||||||
|
_accountAvatar?.OnDisappearing();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
@@ -230,6 +222,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void Search_Clicked(object sender, EventArgs e)
|
private async void Search_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
var page = new CiphersPage(_vm.Filter, _vm.FolderId != null, _vm.CollectionId != null,
|
var page = new CiphersPage(_vm.Filter, _vm.FolderId != null, _vm.CollectionId != null,
|
||||||
@@ -240,21 +233,31 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void Sync_Clicked(object sender, EventArgs e)
|
private async void Sync_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
await _vm.SyncAsync();
|
await _vm.SyncAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Lock_Clicked(object sender, EventArgs e)
|
private async void Lock_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
await _vaultTimeoutService.LockAsync(true, true);
|
await _vaultTimeoutService.LockAsync(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Exit_Clicked(object sender, EventArgs e)
|
private async void Exit_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
await _vm.ExitAsync();
|
await _vm.ExitAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void AddButton_Clicked(object sender, EventArgs e)
|
private async void AddButton_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
var skipAction = _accountListOverlay.IsVisible && Device.RuntimePlatform == Device.Android;
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
|
if (skipAction)
|
||||||
|
{
|
||||||
|
// Account list in the process of closing via tapping on invisible FAB, skip this attempt
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!_vm.Deleted && DoOnce())
|
if (!_vm.Deleted && DoOnce())
|
||||||
{
|
{
|
||||||
var page = new AddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId);
|
var page = new AddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId);
|
||||||
@@ -268,6 +271,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
if (_previousPage.Page == "view" && !string.IsNullOrWhiteSpace(_previousPage.CipherId))
|
if (_previousPage.Page == "view" && !string.IsNullOrWhiteSpace(_previousPage.CipherId))
|
||||||
{
|
{
|
||||||
await Navigation.PushModalAsync(new NavigationPage(new ViewPage(_previousPage.CipherId)));
|
await Navigation.PushModalAsync(new NavigationPage(new ViewPage(_previousPage.CipherId)));
|
||||||
@@ -284,5 +288,10 @@ namespace Bit.App.Pages
|
|||||||
_addItem.IsEnabled = !_vm.Deleted;
|
_addItem.IsEnabled = !_vm.Deleted;
|
||||||
_addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
|
_addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task HideAccountSwitchingOverlayAsync()
|
||||||
|
{
|
||||||
|
await _accountListOverlay.HideAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -40,14 +40,13 @@ namespace Bit.App.Pages
|
|||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
private readonly ICollectionService _collectionService;
|
private readonly ICollectionService _collectionService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public GroupingsPageViewModel()
|
public GroupingsPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -55,14 +54,13 @@ namespace Bit.App.Pages
|
|||||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
Loading = true;
|
Loading = true;
|
||||||
PageTitle = AppResources.MyVault;
|
PageTitle = AppResources.MyVault;
|
||||||
@@ -73,6 +71,11 @@ namespace Bit.App.Pages
|
|||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
});
|
});
|
||||||
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
||||||
|
|
||||||
|
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
|
||||||
|
{
|
||||||
|
AllowAddAccountRow = true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MainPage { get; set; }
|
public bool MainPage { get; set; }
|
||||||
@@ -81,7 +84,6 @@ namespace Bit.App.Pages
|
|||||||
public string CollectionId { get; set; }
|
public string CollectionId { get; set; }
|
||||||
public Func<CipherView, bool> Filter { get; set; }
|
public Func<CipherView, bool> Filter { get; set; }
|
||||||
public bool Deleted { get; set; }
|
public bool Deleted { get; set; }
|
||||||
|
|
||||||
public bool HasCiphers { get; set; }
|
public bool HasCiphers { get; set; }
|
||||||
public bool HasFolders { get; set; }
|
public bool HasFolders { get; set; }
|
||||||
public bool HasCollections { get; set; }
|
public bool HasCollections { get; set; }
|
||||||
@@ -140,6 +142,9 @@ namespace Bit.App.Pages
|
|||||||
get => _websiteIconsEnabled;
|
get => _websiteIconsEnabled;
|
||||||
set => SetProperty(ref _websiteIconsEnabled, value);
|
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
|
||||||
|
|
||||||
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
|
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
|
||||||
public Command RefreshCommand { get; set; }
|
public Command RefreshCommand { get; set; }
|
||||||
public Command<CipherView> CipherOptionsCommand { get; set; }
|
public Command<CipherView> CipherOptionsCommand { get; set; }
|
||||||
@@ -151,7 +156,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var authed = await _userService.IsAuthenticatedAsync();
|
var authed = await _stateService.IsAuthenticatedAsync();
|
||||||
if (!authed)
|
if (!authed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -160,7 +165,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||||
{
|
{
|
||||||
SyncRefreshing = true;
|
SyncRefreshing = true;
|
||||||
await _syncService.FullSyncAsync(false);
|
await _syncService.FullSyncAsync(false);
|
||||||
@@ -176,8 +181,7 @@ namespace Bit.App.Pages
|
|||||||
var groupedItems = new List<GroupingsPageListGroup>();
|
var groupedItems = new List<GroupingsPageListGroup>();
|
||||||
var page = Page as GroupingsPage;
|
var page = Page as GroupingsPage;
|
||||||
|
|
||||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
.GetValueOrDefault();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LoadDataAsync();
|
await LoadDataAsync();
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AppCenter.Crashes;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -14,6 +15,8 @@ namespace Bit.App.Pages
|
|||||||
private CancellationTokenSource _autofocusCts;
|
private CancellationTokenSource _autofocusCts;
|
||||||
private Task _continuousAutofocusTask;
|
private Task _continuousAutofocusTask;
|
||||||
|
|
||||||
|
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||||
|
|
||||||
public ScanPage(Action<string> callback)
|
public ScanPage(Action<string> callback)
|
||||||
{
|
{
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
@@ -61,9 +64,7 @@ namespace Bit.App.Pages
|
|||||||
catch (TaskCanceledException) { }
|
catch (TaskCanceledException) { }
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
#if !FDROID
|
_logger.Value.Exception(ex);
|
||||||
Crashes.TrackError(ex);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}, autofocusCts.Token);
|
}, autofocusCts.Token);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly ICollectionService _collectionService;
|
private readonly ICollectionService _collectionService;
|
||||||
private readonly IUserService _userService;
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private int _organizationSelectedIndex;
|
private int _organizationSelectedIndex;
|
||||||
@@ -28,7 +28,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||||
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
||||||
@@ -67,7 +67,7 @@ namespace Bit.App.Pages
|
|||||||
var allCollections = await _collectionService.GetAllDecryptedAsync();
|
var allCollections = await _collectionService.GetAllDecryptedAsync();
|
||||||
_writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList();
|
_writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList();
|
||||||
|
|
||||||
var orgs = await _userService.GetAllOrganizationAsync();
|
var orgs = await _organizationService.GetAllAsync();
|
||||||
OrganizationOptions = orgs.OrderBy(o => o.Name)
|
OrganizationOptions = orgs.OrderBy(o => o.Name)
|
||||||
.Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed)
|
.Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed)
|
||||||
.Select(o => new KeyValuePair<string, string>(o.Name, o.Id)).ToList();
|
.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 _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
|
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
|
||||||
(await _userService.GetOrganizationAsync(OrganizationId)).Name);
|
(await _organizationService.GetAsync(OrganizationId)).Name);
|
||||||
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
||||||
await Page.Navigation.PopModalAsync();
|
await Page.Navigation.PopModalAsync();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -579,8 +579,8 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0">
|
Grid.Column="0">
|
||||||
<controls:MonoLabel
|
<controls:MonoLabel
|
||||||
Text="{Binding ValueText, Mode=OneWay}"
|
Text="{Binding ColoredHiddenValue, Mode=OneWay}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value, text-html"
|
||||||
IsVisible="{Binding ShowHiddenValue}" />
|
IsVisible="{Binding ShowHiddenValue}" />
|
||||||
<controls:MonoLabel
|
<controls:MonoLabel
|
||||||
Text="{Binding Field.MaskedValue, Mode=OneWay}"
|
Text="{Binding Field.MaskedValue, Mode=OneWay}"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -10,10 +10,6 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -23,7 +19,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ITotpService _totpService;
|
private readonly ITotpService _totpService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IAuditService _auditService;
|
private readonly IAuditService _auditService;
|
||||||
@@ -53,7 +49,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||||
@@ -253,7 +249,7 @@ namespace Bit.App.Pages
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Cipher = await cipher.DecryptAsync();
|
Cipher = await cipher.DecryptAsync();
|
||||||
CanAccessPremium = await _userService.CanAccessPremiumAsync();
|
CanAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
||||||
|
|
||||||
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
||||||
@@ -772,6 +768,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FormattedString ColoredHiddenValue => PasswordFormatter.FormatPassword(_field.Value);
|
||||||
|
|
||||||
public Command ToggleHiddenValueCommand { get; set; }
|
public Command ToggleHiddenValueCommand { get; set; }
|
||||||
|
|
||||||
public string ShowHiddenValueIcon => _showHiddenValue ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowHiddenValueIcon => _showHiddenValue ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
|
|||||||
78
src/App/Resources/AppResources.Designer.cs
generated
78
src/App/Resources/AppResources.Designer.cs
generated
@@ -311,6 +311,30 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string RemoveAccount {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveAccount", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RemoveAccountConfirmation {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveAccountConfirmation", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountAlreadyAdded {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountAlreadyAdded", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SwitchToAlreadyAddedAccountConfirmation {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SwitchToAlreadyAddedAccountConfirmation", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string MasterPassword {
|
public static string MasterPassword {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("MasterPassword", resourceCulture);
|
return ResourceManager.GetString("MasterPassword", resourceCulture);
|
||||||
@@ -3719,6 +3743,54 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string AddAccount {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AddAccount", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountUnlocked {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountUnlocked", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountLocked {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountLocked", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountLoggedOut {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountLoggedOut", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountSwitchedAutomatically {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountSwitchedAutomatically", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountLockedSuccessfully {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountLockedSuccessfully", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountLoggedOutSuccessfully {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountLoggedOutSuccessfully", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AccountRemovedSuccessfully {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AccountRemovedSuccessfully", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string DeleteAccount {
|
public static string DeleteAccount {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("DeleteAccount", resourceCulture);
|
return ResourceManager.GetString("DeleteAccount", resourceCulture);
|
||||||
@@ -3755,6 +3827,12 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string RequestOTP {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RequestOTP", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string SendCode {
|
public static string SendCode {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("SendCode", resourceCulture);
|
return ResourceManager.GetString("SendCode", resourceCulture);
|
||||||
|
|||||||
@@ -275,6 +275,18 @@
|
|||||||
<data name="LogoutConfirmation" xml:space="preserve">
|
<data name="LogoutConfirmation" xml:space="preserve">
|
||||||
<value>Is u seker u wil uitteken?</value>
|
<value>Is u seker u wil uitteken?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveAccount" xml:space="preserve">
|
||||||
|
<value>Remove Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Are you sure you want to remove this account?</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountAlreadyAdded" xml:space="preserve">
|
||||||
|
<value>Account Already Added</value>
|
||||||
|
</data>
|
||||||
|
<data name="SwitchToAlreadyAddedAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Would you like to switch to it now?</value>
|
||||||
|
</data>
|
||||||
<data name="MasterPassword" xml:space="preserve">
|
<data name="MasterPassword" xml:space="preserve">
|
||||||
<value>Hoofwagwoord</value>
|
<value>Hoofwagwoord</value>
|
||||||
<comment>Label for a master password.</comment>
|
<comment>Label for a master password.</comment>
|
||||||
@@ -2092,6 +2104,21 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>Een of meer organisasiebeleide verhoed u om u persoonlike kluis uit te stuur.</value>
|
<value>Een of meer organisasiebeleide verhoed u om u persoonlike kluis uit te stuur.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AddAccount" xml:space="preserve">
|
||||||
|
<value>Add Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountUnlocked" xml:space="preserve">
|
||||||
|
<value>Unlocked</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLocked" xml:space="preserve">
|
||||||
|
<value>Locked</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLoggedOut" xml:space="preserve">
|
||||||
|
<value>Logged Out</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountSwitchedAutomatically" xml:space="preserve">
|
||||||
|
<value>Switched to next available account</value>
|
||||||
|
</data>
|
||||||
<data name="DeleteAccount" xml:space="preserve">
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
<value>Skrap rekening</value>
|
<value>Skrap rekening</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -275,6 +275,18 @@
|
|||||||
<data name="LogoutConfirmation" xml:space="preserve">
|
<data name="LogoutConfirmation" xml:space="preserve">
|
||||||
<value>Çıxış etmək istədiyinizə əminsiniz?</value>
|
<value>Çıxış etmək istədiyinizə əminsiniz?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveAccount" xml:space="preserve">
|
||||||
|
<value>Remove Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Are you sure you want to remove this account?</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountAlreadyAdded" xml:space="preserve">
|
||||||
|
<value>Account Already Added</value>
|
||||||
|
</data>
|
||||||
|
<data name="SwitchToAlreadyAddedAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Would you like to switch to it now?</value>
|
||||||
|
</data>
|
||||||
<data name="MasterPassword" xml:space="preserve">
|
<data name="MasterPassword" xml:space="preserve">
|
||||||
<value>Ana parol</value>
|
<value>Ana parol</value>
|
||||||
<comment>Label for a master password.</comment>
|
<comment>Label for a master password.</comment>
|
||||||
@@ -2092,6 +2104,21 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<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>
|
<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>
|
||||||
|
<data name="AddAccount" xml:space="preserve">
|
||||||
|
<value>Add Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountUnlocked" xml:space="preserve">
|
||||||
|
<value>Unlocked</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLocked" xml:space="preserve">
|
||||||
|
<value>Locked</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLoggedOut" xml:space="preserve">
|
||||||
|
<value>Logged Out</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountSwitchedAutomatically" xml:space="preserve">
|
||||||
|
<value>Switched to next available account</value>
|
||||||
|
</data>
|
||||||
<data name="DeleteAccount" xml:space="preserve">
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
<value>Hesabı sil</value>
|
<value>Hesabı sil</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -275,6 +275,18 @@
|
|||||||
<data name="LogoutConfirmation" xml:space="preserve">
|
<data name="LogoutConfirmation" xml:space="preserve">
|
||||||
<value>Вы ўпэўнены, што хочаце выйсці?</value>
|
<value>Вы ўпэўнены, што хочаце выйсці?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveAccount" xml:space="preserve">
|
||||||
|
<value>Remove Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Are you sure you want to remove this account?</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountAlreadyAdded" xml:space="preserve">
|
||||||
|
<value>Account Already Added</value>
|
||||||
|
</data>
|
||||||
|
<data name="SwitchToAlreadyAddedAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Would you like to switch to it now?</value>
|
||||||
|
</data>
|
||||||
<data name="MasterPassword" xml:space="preserve">
|
<data name="MasterPassword" xml:space="preserve">
|
||||||
<value>Асноўны пароль</value>
|
<value>Асноўны пароль</value>
|
||||||
<comment>Label for a master password.</comment>
|
<comment>Label for a master password.</comment>
|
||||||
@@ -2092,6 +2104,21 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AddAccount" xml:space="preserve">
|
||||||
|
<value>Add Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountUnlocked" xml:space="preserve">
|
||||||
|
<value>Unlocked</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLocked" xml:space="preserve">
|
||||||
|
<value>Locked</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLoggedOut" xml:space="preserve">
|
||||||
|
<value>Logged Out</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountSwitchedAutomatically" xml:space="preserve">
|
||||||
|
<value>Switched to next available account</value>
|
||||||
|
</data>
|
||||||
<data name="DeleteAccount" xml:space="preserve">
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
<value>Delete Account</value>
|
<value>Delete Account</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -275,6 +275,18 @@
|
|||||||
<data name="LogoutConfirmation" xml:space="preserve">
|
<data name="LogoutConfirmation" xml:space="preserve">
|
||||||
<value>Сигурни ли сте, че искате да се отпишете?</value>
|
<value>Сигурни ли сте, че искате да се отпишете?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveAccount" xml:space="preserve">
|
||||||
|
<value>Премахване на регистрацията</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Наистина ли искате да премахнете тази регистрация?</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountAlreadyAdded" xml:space="preserve">
|
||||||
|
<value>Регистрацията вече е добавена</value>
|
||||||
|
</data>
|
||||||
|
<data name="SwitchToAlreadyAddedAccountConfirmation" xml:space="preserve">
|
||||||
|
<value>Искате ли да превключите към нея сега?</value>
|
||||||
|
</data>
|
||||||
<data name="MasterPassword" xml:space="preserve">
|
<data name="MasterPassword" xml:space="preserve">
|
||||||
<value>Главна парола</value>
|
<value>Главна парола</value>
|
||||||
<comment>Label for a master password.</comment>
|
<comment>Label for a master password.</comment>
|
||||||
@@ -2093,6 +2105,21 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>Една или повече от настройките на организацията Ви не позволяват да изнасяте личния си трезор.</value>
|
<value>Една или повече от настройките на организацията Ви не позволяват да изнасяте личния си трезор.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AddAccount" xml:space="preserve">
|
||||||
|
<value>Добавяне на регистрация</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountUnlocked" xml:space="preserve">
|
||||||
|
<value>Отключено</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLocked" xml:space="preserve">
|
||||||
|
<value>Заключено</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountLoggedOut" xml:space="preserve">
|
||||||
|
<value>Отписано</value>
|
||||||
|
</data>
|
||||||
|
<data name="AccountSwitchedAutomatically" xml:space="preserve">
|
||||||
|
<value>Превключено към следващата налична регистрация</value>
|
||||||
|
</data>
|
||||||
<data name="DeleteAccount" xml:space="preserve">
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
<value>Изтриване на регистрацията</value>
|
<value>Изтриване на регистрацията</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user