mirror of
https://github.com/bitwarden/mobile
synced 2025-12-18 01:03:24 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cc1a501a5 | ||
|
|
b0a8694801 | ||
|
|
b9c1ab7c1d | ||
|
|
4d96b091f7 | ||
|
|
88fee155db | ||
|
|
f930028920 |
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -21,8 +21,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Testing requirements
|
||||||
|
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Before you submit
|
## Before you submit
|
||||||
- [ ] I have checked for formatting errors (`dotnet tool run dotnet-format --check`) (required)
|
|
||||||
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
|
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
|
||||||
- [ ] This change requires a **documentation update** (notify the documentation team)
|
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||||
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||||
|
|||||||
27
.github/workflows/build.yml
vendored
27
.github/workflows/build.yml
vendored
@@ -60,11 +60,6 @@ jobs:
|
|||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
needs: setup
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Setup NuGet
|
|
||||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
|
||||||
with:
|
|
||||||
nuget-version: 5.9.0
|
|
||||||
|
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||||
|
|
||||||
@@ -214,11 +209,6 @@ jobs:
|
|||||||
name: F-Droid Build
|
name: F-Droid Build
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
steps:
|
steps:
|
||||||
- name: Setup NuGet
|
|
||||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
|
||||||
with:
|
|
||||||
nuget-version: 5.9.0
|
|
||||||
|
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||||
|
|
||||||
@@ -314,6 +304,18 @@ jobs:
|
|||||||
|
|
||||||
$xml.Save($androidPath);
|
$xml.Save($androidPath);
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Uninstall from App.csproj"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($appPath);
|
||||||
|
|
||||||
|
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
||||||
|
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||||
|
|
||||||
|
$xml.Save($appPath);
|
||||||
|
|
||||||
Write-Output "########################################"
|
Write-Output "########################################"
|
||||||
Write-Output "##### Uninstall from Core.csproj"
|
Write-Output "##### Uninstall from Core.csproj"
|
||||||
Write-Output "########################################"
|
Write-Output "########################################"
|
||||||
@@ -378,11 +380,6 @@ jobs:
|
|||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
needs: setup
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Setup NuGet
|
|
||||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
|
||||||
with:
|
|
||||||
nuget-version: 5.9.0
|
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help | grep Version
|
nuget help | grep Version
|
||||||
|
|||||||
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@@ -34,13 +34,29 @@ jobs:
|
|||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||||
|
|
||||||
- name: Check Release Version
|
- name: Retrieve Mobile release version
|
||||||
id: version
|
id: retrieve-mobile-version
|
||||||
uses: bitwarden/gh-actions/release-version-check@8f055ef543c7433c967a1b9b04a0f230923233bb
|
run: |
|
||||||
with:
|
ver=$(sed -E -n '/^<manifest/s/^.*[ ]android:versionName="([^"]+)".*$/\1/p' \
|
||||||
release-type: ${{ github.event.inputs.release_type }}
|
./src/Android/Properties/AndroidManifest.xml | tr -d '"')
|
||||||
project-type: xamarin
|
echo "::set-output name=mobile_version::${ver}"
|
||||||
file: src/Android/Properties/AndroidManifest.xml
|
shell: bash
|
||||||
|
|
||||||
|
- name: Check to make sure Mobile release version has been bumped
|
||||||
|
if: ${{ github.event.inputs.release_type == 'Initial Release' }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
latest_ver=$(hub release -L 1 -f '%T')
|
||||||
|
latest_ver=${latest_ver:1}
|
||||||
|
echo "Latest version: $latest_ver"
|
||||||
|
ver=${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
|
echo "Version: $ver"
|
||||||
|
if [ "$latest_ver" = "$ver" ]; then
|
||||||
|
echo "Version has not been bumped!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Get branch name
|
- name: Get branch name
|
||||||
id: branch
|
id: branch
|
||||||
@@ -67,8 +83,8 @@ jobs:
|
|||||||
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||||
./Bitwarden iOS.zip"
|
./Bitwarden iOS.zip"
|
||||||
commit: ${{ github.sha }}
|
commit: ${{ github.sha }}
|
||||||
tag: v${{ steps.version.outputs.version }}
|
tag: v${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
name: Version ${{ steps.version.outputs.version }}
|
name: Version ${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
body: "<insert release notes here>"
|
body: "<insert release notes here>"
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
draft: true
|
draft: true
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UiTests", "src\UiTests\UiTests.csproj", "{23FB637B-1705-485F-9464-078FCAF361A8}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||||
@@ -446,6 +448,36 @@ Global
|
|||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -464,6 +496,7 @@ Global
|
|||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
|
{23FB637B-1705-485F-9464-078FCAF361A8} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||||
|
|||||||
@@ -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.3</Version>
|
<Version>1.7.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>122.0.0</Version>
|
<Version>122.0.0</Version>
|
||||||
@@ -145,12 +145,12 @@
|
|||||||
<Compile Include="Tiles\GeneratorTileService.cs" />
|
<Compile Include="Tiles\GeneratorTileService.cs" />
|
||||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
<Compile Include="Tiles\MyVaultTileService.cs" />
|
||||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
<Compile Include="Utilities\AndroidHelpers.cs" />
|
||||||
|
<Compile Include="Utilities\AppCenterHelper.cs" />
|
||||||
<Compile Include="Utilities\ThemeHelpers.cs" />
|
<Compile Include="Utilities\ThemeHelpers.cs" />
|
||||||
<Compile Include="WebAuthCallbackActivity.cs" />
|
<Compile Include="WebAuthCallbackActivity.cs" />
|
||||||
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||||
<Compile Include="Services\ClipboardService.cs" />
|
<Compile Include="Services\ClipboardService.cs" />
|
||||||
<Compile Include="Utilities\IntentExtensions.cs" />
|
<Compile Include="Utilities\IntentExtensions.cs" />
|
||||||
<Compile Include="Renderers\CustomPageRenderer.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
||||||
|
|||||||
@@ -64,12 +64,15 @@ namespace Bit.Droid
|
|||||||
Intent?.Validate();
|
Intent?.Validate();
|
||||||
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
if (!CoreHelpers.InDebugMode())
|
//if (!CoreHelpers.InDebugMode())
|
||||||
{
|
//{
|
||||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
// Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||||
}
|
//}
|
||||||
|
|
||||||
ServiceContainer.Resolve<ILogger>("logger").InitAsync();
|
#if !DEBUG && !FDROID
|
||||||
|
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||||
|
var appCenterTask = appCenterHelper.InitAsync();
|
||||||
|
#endif
|
||||||
|
|
||||||
var toplayout = Window?.DecorView?.RootView;
|
var toplayout = Window?.DecorView?.RootView;
|
||||||
if (toplayout != null)
|
if (toplayout != null)
|
||||||
@@ -82,7 +85,6 @@ namespace Bit.Droid
|
|||||||
_appOptions = GetOptions();
|
_appOptions = GetOptions();
|
||||||
LoadApplication(new App.App(_appOptions));
|
LoadApplication(new App.App(_appOptions));
|
||||||
|
|
||||||
|
|
||||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "startEventTimer")
|
if (message.Command == "startEventTimer")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Droid.Services;
|
using Bit.Droid.Services;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Plugin.CurrentActivity;
|
using Plugin.CurrentActivity;
|
||||||
using Plugin.Fingerprint;
|
using Plugin.Fingerprint;
|
||||||
using Xamarin.Android.Net;
|
using Xamarin.Android.Net;
|
||||||
@@ -19,7 +20,6 @@ using System.Net.Http;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.App.Pages;
|
using Bit.App.Pages;
|
||||||
using Bit.App.Utilities.AccountManagement;
|
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Android.Gms.Security;
|
using Android.Gms.Security;
|
||||||
#endif
|
#endif
|
||||||
@@ -62,15 +62,6 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"));
|
ServiceContainer.Resolve<ICryptoService>("cryptoService"));
|
||||||
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
|
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
|
||||||
|
|
||||||
var accountsManager = new AccountsManager(
|
|
||||||
ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"),
|
|
||||||
ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"),
|
|
||||||
ServiceContainer.Resolve<IStorageService>("secureStorageService"),
|
|
||||||
ServiceContainer.Resolve<IStateService>("stateService"),
|
|
||||||
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
|
|
||||||
ServiceContainer.Resolve<IAuthService>("authService"));
|
|
||||||
ServiceContainer.Register<IAccountsManager>("accountsManager", accountsManager);
|
|
||||||
}
|
}
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||||
@@ -130,14 +121,13 @@ 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 stateService = new StateService(mobileStorageService, secureStorageService, messagingService);
|
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||||
var stateMigrationService =
|
var stateMigrationService =
|
||||||
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
||||||
var clipboardService = new ClipboardService(stateService);
|
var deviceActionService = new DeviceActionService(stateService, messagingService,
|
||||||
var deviceActionService = new DeviceActionService(clipboardService, stateService, messagingService,
|
|
||||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||||
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(stateService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
@@ -152,7 +142,7 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||||
ServiceContainer.Register<IStateService>("stateService", stateService);
|
ServiceContainer.Register<IStateService>("stateService", stateService);
|
||||||
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
|
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
|
||||||
ServiceContainer.Register<IClipboardService>("clipboardService", clipboardService);
|
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);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2022.6.0" 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.18.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using AndroidX.AppCompat.Widget;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Droid.Renderers;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))]
|
|
||||||
namespace Bit.Droid.Renderers
|
|
||||||
{
|
|
||||||
public class CustomPageRenderer : PageRenderer
|
|
||||||
{
|
|
||||||
public CustomPageRenderer(Context context) : base(context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
|
|
||||||
{
|
|
||||||
base.OnElementChanged(e);
|
|
||||||
|
|
||||||
Activity context = (Activity)this.Context;
|
|
||||||
var toolbar = context.FindViewById<Toolbar>(Resource.Id.toolbar);
|
|
||||||
if(toolbar != null)
|
|
||||||
{
|
|
||||||
toolbar.NavigationContentDescription = AppResources.TapToGoBack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,5 +2,4 @@
|
|||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -2,5 +2,4 @@
|
|||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -3,10 +3,11 @@ using System.Threading.Tasks;
|
|||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Security.Keystore;
|
using Android.Security.Keystore;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Java.Security;
|
using Java.Security;
|
||||||
using Javax.Crypto;
|
using Javax.Crypto;
|
||||||
|
#if !FDROID
|
||||||
|
using Microsoft.AppCenter.Crashes;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Bit.Droid.Services
|
namespace Bit.Droid.Services
|
||||||
{
|
{
|
||||||
@@ -73,7 +74,9 @@ namespace Bit.Droid.Services
|
|||||||
catch (InvalidKeyException e)
|
catch (InvalidKeyException e)
|
||||||
{
|
{
|
||||||
// Fallback for old bitwarden users without a key
|
// Fallback for old bitwarden users without a key
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
#if !FDROID
|
||||||
|
Crashes.TrackError(e);
|
||||||
|
#endif
|
||||||
CreateKey();
|
CreateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +101,9 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
// Catch silently to allow biometrics to function on devices that are in a state where key generation
|
// Catch silently to allow biometrics to function on devices that are in a state where key generation
|
||||||
// is not functioning
|
// is not functioning
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
#if !FDROID
|
||||||
|
Crashes.TrackError(e);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Droid.Receivers;
|
using Bit.Droid.Receivers;
|
||||||
using Plugin.CurrentActivity;
|
using Plugin.CurrentActivity;
|
||||||
@@ -26,41 +26,13 @@ namespace Bit.Droid.Services
|
|||||||
PendingIntentFlags.UpdateCurrent));
|
PendingIntentFlags.UpdateCurrent));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CopyTextAsync(string text, int expiresInMs = -1, bool isSensitive = true)
|
public async Task CopyTextAsync(string text, int expiresInMs = -1)
|
||||||
{
|
|
||||||
// Xamarin.Essentials.Clipboard currently doesn't support the IS_SENSITIVE flag for API 33+
|
|
||||||
if ((int)Build.VERSION.SdkInt < 33)
|
|
||||||
{
|
{
|
||||||
await Clipboard.SetTextAsync(text);
|
await Clipboard.SetTextAsync(text);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CopyToClipboard(text, isSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
await ClearClipboardAlarmAsync(expiresInMs);
|
await ClearClipboardAlarmAsync(expiresInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsCopyNotificationHandledByPlatform()
|
|
||||||
{
|
|
||||||
// Android 13+ provides built-in notification when text is copied to the clipboard
|
|
||||||
return (int)Build.VERSION.SdkInt >= 33;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CopyToClipboard(string text, bool isSensitive = true)
|
|
||||||
{
|
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
||||||
var clipboardManager = activity.GetSystemService(
|
|
||||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
|
||||||
var clipData = ClipData.NewPlainText("bitwarden", text);
|
|
||||||
if (isSensitive)
|
|
||||||
{
|
|
||||||
clipData.Description.Extras ??= new PersistableBundle();
|
|
||||||
clipData.Description.Extras.PutBoolean("android.content.extra.IS_SENSITIVE", true);
|
|
||||||
}
|
|
||||||
clipboardManager.PrimaryClip = clipData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ClearClipboardAlarmAsync(int expiresInMs = -1)
|
private async Task ClearClipboardAlarmAsync(int expiresInMs = -1)
|
||||||
{
|
{
|
||||||
var clearMs = expiresInMs;
|
var clearMs = expiresInMs;
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
public class DeviceActionService : IDeviceActionService
|
public class DeviceActionService : IDeviceActionService
|
||||||
{
|
{
|
||||||
private readonly IClipboardService _clipboardService;
|
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
@@ -48,13 +47,11 @@ namespace Bit.Droid.Services
|
|||||||
private string _userAgent;
|
private string _userAgent;
|
||||||
|
|
||||||
public DeviceActionService(
|
public DeviceActionService(
|
||||||
IClipboardService clipboardService,
|
|
||||||
IStateService stateService,
|
IStateService stateService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IBroadcasterService broadcasterService,
|
IBroadcasterService broadcasterService,
|
||||||
Func<IEventService> eventServiceFunc)
|
Func<IEventService> eventServiceFunc)
|
||||||
{
|
{
|
||||||
_clipboardService = clipboardService;
|
|
||||||
_stateService = stateService;
|
_stateService = stateService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_broadcasterService = broadcasterService;
|
_broadcasterService = broadcasterService;
|
||||||
@@ -932,12 +929,20 @@ namespace Bit.Droid.Services
|
|||||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||||
if (totp != null)
|
if (totp != null)
|
||||||
{
|
{
|
||||||
await _clipboardService.CopyTextAsync(totp);
|
CopyToClipboard(totp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CopyToClipboard(string text)
|
||||||
|
{
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
var clipboardManager = activity.GetSystemService(
|
||||||
|
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||||
|
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", text);
|
||||||
|
}
|
||||||
|
|
||||||
public float GetSystemFontSizeScale()
|
public float GetSystemFontSizeScale()
|
||||||
{
|
{
|
||||||
var activity = CrossCurrentActivity.Current?.Activity as MainActivity;
|
var activity = CrossCurrentActivity.Current?.Activity as MainActivity;
|
||||||
|
|||||||
58
src/Android/Utilities/AppCenterHelper.cs
Normal file
58
src/Android/Utilities/AppCenterHelper.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#if !FDROID
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AppCenter;
|
||||||
|
using Microsoft.AppCenter.Crashes;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Bit.Droid.Utilities
|
||||||
|
{
|
||||||
|
public class AppCenterHelper
|
||||||
|
{
|
||||||
|
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
||||||
|
|
||||||
|
private readonly IAppIdService _appIdService;
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
|
private string _userId;
|
||||||
|
private string _appId;
|
||||||
|
|
||||||
|
public AppCenterHelper(
|
||||||
|
IAppIdService appIdService,
|
||||||
|
IStateService stateService)
|
||||||
|
{
|
||||||
|
_appIdService = appIdService;
|
||||||
|
_stateService = stateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitAsync()
|
||||||
|
{
|
||||||
|
_userId = await _stateService.GetActiveUserIdAsync();
|
||||||
|
_appId = await _appIdService.GetAppIdAsync();
|
||||||
|
|
||||||
|
AppCenter.Start(AppSecret, typeof(Crashes));
|
||||||
|
AppCenter.SetUserId(_userId);
|
||||||
|
|
||||||
|
Crashes.GetErrorAttachments = (ErrorReport report) =>
|
||||||
|
{
|
||||||
|
return new ErrorAttachmentLog[]
|
||||||
|
{
|
||||||
|
ErrorAttachmentLog.AttachmentWithText(Description, "crshdesc.txt"),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Description
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
AppId = _appId,
|
||||||
|
UserId = _userId
|
||||||
|
}, Formatting.Indented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Models;
|
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
|
||||||
{
|
|
||||||
public interface IAccountsManager
|
|
||||||
{
|
|
||||||
void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost);
|
|
||||||
Task NavigateOnAccountChangeAsync(bool? isAuthed = null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
|
||||||
{
|
|
||||||
public interface INavigationParams { }
|
|
||||||
|
|
||||||
public interface IAccountsManagerHost
|
|
||||||
{
|
|
||||||
Task SetPreviousPageInfoAsync();
|
|
||||||
void Navigate(NavigationTarget navTarget, INavigationParams navParams = null);
|
|
||||||
Task UpdateThemeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,12 +13,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.5" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||||
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||||
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
||||||
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.2" />
|
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.1" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.3" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.2" />
|
||||||
<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.2478" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2401" />
|
||||||
<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" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -128,7 +129,6 @@
|
|||||||
<Folder Include="Resources\" />
|
<Folder Include="Resources\" />
|
||||||
<Folder Include="Behaviors\" />
|
<Folder Include="Behaviors\" />
|
||||||
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
||||||
<Folder Include="Utilities\AccountManagement\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -421,6 +421,5 @@
|
|||||||
<None Remove="Behaviors\" />
|
<None Remove="Behaviors\" />
|
||||||
<None Remove="Xamarin.CommunityToolkit" />
|
<None Remove="Xamarin.CommunityToolkit" />
|
||||||
<None Remove="Controls\AccountSwitchingOverlay\" />
|
<None Remove="Controls\AccountSwitchingOverlay\" />
|
||||||
<None Remove="Utilities\AccountManagement\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ 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.App.Utilities.AccountManagement;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
@@ -17,7 +16,7 @@ using Xamarin.Forms.Xaml;
|
|||||||
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
||||||
namespace Bit.App
|
namespace Bit.App
|
||||||
{
|
{
|
||||||
public partial class App : Application, IAccountsManagerHost
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
@@ -28,7 +27,6 @@ namespace Bit.App
|
|||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly IStorageService _secureStorageService;
|
private readonly IStorageService _secureStorageService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IAccountsManager _accountsManager;
|
|
||||||
|
|
||||||
private static bool _isResumed;
|
private static bool _isResumed;
|
||||||
|
|
||||||
@@ -49,9 +47,6 @@ namespace Bit.App
|
|||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
|
|
||||||
|
|
||||||
_accountsManager.Init(() => Options, this);
|
|
||||||
|
|
||||||
Bootstrap();
|
Bootstrap();
|
||||||
_broadcasterService.Subscribe(nameof(App), async (message) =>
|
_broadcasterService.Subscribe(nameof(App), async (message) =>
|
||||||
@@ -76,6 +71,30 @@ namespace Bit.App
|
|||||||
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (message.Command == "locked")
|
||||||
|
{
|
||||||
|
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")
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.LockAsync(true);
|
||||||
|
}
|
||||||
|
else if (message.Command == "logout")
|
||||||
|
{
|
||||||
|
var extras = message.Data as Tuple<string, bool, bool>;
|
||||||
|
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")
|
||||||
|
{
|
||||||
|
// Clean up old migrated key if they ever log out.
|
||||||
|
await _secureStorageService.RemoveAsync("oldKey");
|
||||||
|
}
|
||||||
else if (message.Command == "resumed")
|
else if (message.Command == "resumed")
|
||||||
{
|
{
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
@@ -90,10 +109,22 @@ 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);
|
||||||
await _accountsManager.NavigateOnAccountChangeAsync();
|
await SetMainPageAsync();
|
||||||
}
|
}
|
||||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||||
message.Command == "popAllAndGoToTabMyVault" ||
|
message.Command == "popAllAndGoToTabMyVault" ||
|
||||||
@@ -137,6 +168,7 @@ namespace Bit.App
|
|||||||
new NavigationPage(new RemoveMasterPasswordPage()));
|
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +234,6 @@ namespace Bit.App
|
|||||||
|
|
||||||
private async Task ResumedAsync()
|
private async Task ResumedAsync()
|
||||||
{
|
{
|
||||||
await _stateService.CheckExtensionActiveUserAndSwitchIfNeededAsync();
|
|
||||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
_messagingService.Send("startEventTimer");
|
_messagingService.Send("startEventTimer");
|
||||||
await UpdateThemeAsync();
|
await UpdateThemeAsync();
|
||||||
@@ -232,6 +263,102 @@ namespace Bit.App
|
|||||||
new System.Globalization.UmAlQuraCalendar();
|
new System.Globalization.UmAlQuraCalendar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LogOutAsync(string userId, bool userInitiated, bool expired)
|
||||||
|
{
|
||||||
|
await AppHelpers.LogOutAsync(userId, userInitiated);
|
||||||
|
await SetMainPageAsync();
|
||||||
|
_authService.LogOut(() =>
|
||||||
|
{
|
||||||
|
if (expired)
|
||||||
|
{
|
||||||
|
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
var authed = await _stateService.IsAuthenticatedAsync();
|
||||||
|
if (authed)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
|
||||||
|
}
|
||||||
|
else if (Options.Uri != null)
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
||||||
|
}
|
||||||
|
else if (Options.CreateSend != null)
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Current.MainPage = new TabsPage(Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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 _stateService.GetLastFileCacheClearAsync();
|
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
||||||
@@ -293,7 +420,7 @@ namespace Bit.App
|
|||||||
UpdateThemeAsync();
|
UpdateThemeAsync();
|
||||||
};
|
};
|
||||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
var mainPageTask = _accountsManager.NavigateOnAccountChangeAsync();
|
var mainPageTask = SetMainPageAsync();
|
||||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,8 +441,23 @@ namespace Bit.App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetPreviousPageInfoAsync()
|
private async Task LockedAsync(string userId, bool userInitiated)
|
||||||
{
|
{
|
||||||
|
if (!await _stateService.IsActiveAccountAsync(userId))
|
||||||
|
{
|
||||||
|
_platformUtilsService.ShowToast("info", null, AppResources.AccountLockedSuccessfully);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var autoPromptBiometric = !userInitiated;
|
||||||
|
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||||
|
{
|
||||||
|
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||||
|
if (vaultTimeout == 0)
|
||||||
|
{
|
||||||
|
autoPromptBiometric = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
PreviousPageInfo lastPageBeforeLock = null;
|
PreviousPageInfo lastPageBeforeLock = null;
|
||||||
if (Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
|
if (Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -341,44 +483,8 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||||
}
|
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||||
|
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||||
public void Navigate(NavigationTarget navTarget, INavigationParams navParams)
|
|
||||||
{
|
|
||||||
switch (navTarget)
|
|
||||||
{
|
|
||||||
case NavigationTarget.HomeLogin:
|
|
||||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
|
||||||
break;
|
|
||||||
case NavigationTarget.Login:
|
|
||||||
if (navParams is LoginNavigationParams loginParams)
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NavigationTarget.Lock:
|
|
||||||
if (navParams is LockNavigationParams lockParams)
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NavigationTarget.Home:
|
|
||||||
Current.MainPage = new TabsPage(Options);
|
|
||||||
break;
|
|
||||||
case NavigationTarget.AddEditCipher:
|
|
||||||
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
|
|
||||||
break;
|
|
||||||
case NavigationTarget.AutofillCiphers:
|
|
||||||
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
|
||||||
break;
|
|
||||||
case NavigationTarget.SendAddEdit:
|
|
||||||
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70;
|
public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70;
|
||||||
|
|
||||||
public bool LongPressAccountEnabled { get; set; } = true;
|
|
||||||
|
|
||||||
public Action AfterHide { get; set; }
|
|
||||||
|
|
||||||
public async Task ToggleVisibilityAsync()
|
public async Task ToggleVisibilityAsync()
|
||||||
{
|
{
|
||||||
if (IsVisible)
|
if (IsVisible)
|
||||||
@@ -139,8 +135,6 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
// remove overlay
|
// remove overlay
|
||||||
IsVisible = false;
|
IsVisible = false;
|
||||||
|
|
||||||
AfterHide?.Invoke();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +167,7 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
private async Task LongPressAccountAsync(AccountViewCellViewModel item)
|
private async Task LongPressAccountAsync(AccountViewCellViewModel item)
|
||||||
{
|
{
|
||||||
if (!LongPressAccountEnabled || !item.IsAccount)
|
if (!item.IsAccount)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,28 +45,23 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
public ICommand LongPressAccountCommand { get; }
|
public ICommand LongPressAccountCommand { get; }
|
||||||
|
|
||||||
public bool FromIOSExtension { get; set; }
|
|
||||||
|
|
||||||
private async Task SelectAccountAsync(AccountViewCellViewModel item)
|
private async Task SelectAccountAsync(AccountViewCellViewModel item)
|
||||||
{
|
{
|
||||||
if (!item.AccountView.IsAccount)
|
if (item.AccountView.IsAccount)
|
||||||
{
|
{
|
||||||
_messagingService.Send(AccountsManagerMessageCommands.ADD_ACCOUNT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!item.AccountView.IsActive)
|
if (!item.AccountView.IsActive)
|
||||||
{
|
{
|
||||||
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
_messagingService.Send("switchedAccount");
|
||||||
if (FromIOSExtension)
|
|
||||||
{
|
|
||||||
await _stateService.SaveExtensionActiveUserIdToStorageAsync(item.AccountView.UserId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (AllowActiveAccountSelection)
|
else if (AllowActiveAccountSelection)
|
||||||
{
|
{
|
||||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
_messagingService.Send("switchedAccount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_messagingService.Send("addAccount");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@
|
|||||||
<controls:MiButton
|
<controls:MiButton
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
Text=""
|
||||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
Clicked="MoreButton_Clicked"
|
Clicked="MoreButton_Clicked"
|
||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
public class ExtendedCollectionView : CollectionView
|
public class ExtendedCollectionView : CollectionView
|
||||||
{
|
{
|
||||||
public string ExtraDataForLogging { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
<controls:MiButton
|
<controls:MiButton
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
Text=""
|
||||||
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
||||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
Clicked="MoreButton_Clicked"
|
Clicked="MoreButton_Clicked"
|
||||||
|
|||||||
@@ -46,11 +46,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new[]
|
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
||||||
{
|
|
||||||
nameof(ShowPasswordIcon),
|
|
||||||
nameof(PasswordVisibilityAccessibilityText)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPolicyInEffect
|
public bool IsPolicyInEffect
|
||||||
@@ -72,7 +68,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
public string ConfirmMasterPassword { get; set; }
|
public string ConfirmMasterPassword { get; set; }
|
||||||
public string Hint { get; set; }
|
public string Hint { get; set; }
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" />
|
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" AutomationId="save_button"/>
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
@@ -34,7 +34,8 @@
|
|||||||
Placeholder="ex. https://bitwarden.company.com"
|
Placeholder="ex. https://bitwarden.company.com"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding SubmitCommand}" />
|
ReturnCommand="{Binding SubmitCommand}"
|
||||||
|
AutomationId="server_input"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n SelfHostedEnvironmentFooter}"
|
Text="{u:I18n SelfHostedEnvironmentFooter}"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
Priority="-1"
|
Priority="-1"
|
||||||
UseOriginalImage="True"
|
UseOriginalImage="True"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Account}" />
|
AutomationProperties.Name="{u:I18n Account}"/>
|
||||||
<ToolbarItem
|
<ToolbarItem
|
||||||
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
@@ -37,15 +37,18 @@
|
|||||||
<Image
|
<Image
|
||||||
x:Name="_logo"
|
x:Name="_logo"
|
||||||
Source="logo.png"
|
Source="logo.png"
|
||||||
VerticalOptions="Center" />
|
VerticalOptions="Center"
|
||||||
|
AutomationId="logo_image"
|
||||||
|
/>
|
||||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||||
StyleClass="text-lg"
|
StyleClass="text-lg"
|
||||||
HorizontalTextAlignment="Center">
|
HorizontalTextAlignment="Center"/>
|
||||||
</Label>
|
|
||||||
<StackLayout Spacing="5">
|
<StackLayout Spacing="5">
|
||||||
<Button Text="{u:I18n LogIn}"
|
<Button Text="{u:I18n LogIn}"
|
||||||
StyleClass="btn-primary"
|
StyleClass="btn-primary"
|
||||||
Clicked="LogIn_Clicked" />
|
Clicked="LogIn_Clicked"
|
||||||
|
AutomationId="homepage_login_button"/>
|
||||||
<Button Text="{u:I18n CreateAccount}"
|
<Button Text="{u:I18n CreateAccount}"
|
||||||
Clicked="Register_Clicked" />
|
Clicked="Register_Clicked" />
|
||||||
<Button Text="{u:I18n LogInSso}"
|
<Button Text="{u:I18n LogInSso}"
|
||||||
|
|||||||
@@ -80,8 +80,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="_passwordGrid"
|
x:Name="_passwordGrid"
|
||||||
@@ -120,8 +119,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon),
|
nameof(ShowPasswordIcon)
|
||||||
nameof(PasswordVisibilityAccessibilityText),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +128,6 @@ namespace Bit.App.Pages
|
|||||||
public Command SubmitCommand { get; }
|
public Command SubmitCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
public string Pin { get; set; }
|
public string Pin { get; set; }
|
||||||
public Action UnlockedAction { get; set; }
|
public Action UnlockedAction { get; set; }
|
||||||
|
|||||||
@@ -56,7 +56,8 @@
|
|||||||
x:Name="_email"
|
x:Name="_email"
|
||||||
Text="{Binding Email}"
|
Text="{Binding Email}"
|
||||||
Keyboard="Email"
|
Keyboard="Email"
|
||||||
StyleClass="box-value">
|
StyleClass="box-value"
|
||||||
|
AutomationId="email_input">
|
||||||
<VisualStateManager.VisualStateGroups>
|
<VisualStateManager.VisualStateGroups>
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
<VisualState x:Name="Disabled">
|
<VisualState x:Name="Disabled">
|
||||||
@@ -92,7 +93,8 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding LogInCommand}" />
|
ReturnCommand="{Binding LogInCommand}"
|
||||||
|
AutomationId="password_input"/>
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
Text="{Binding ShowPasswordIcon}"
|
Text="{Binding ShowPasswordIcon}"
|
||||||
@@ -101,17 +103,18 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout Padding="10, 0">
|
<StackLayout Padding="10, 0">
|
||||||
<Button Text="{u:I18n LogIn}"
|
<Button Text="{u:I18n LogIn}"
|
||||||
StyleClass="btn-primary"
|
StyleClass="btn-primary"
|
||||||
Clicked="LogIn_Clicked" />
|
Clicked="LogIn_Clicked"
|
||||||
|
AutomationId="loginpage_login_button"/>
|
||||||
<Button Text="{u:I18n Cancel}"
|
<Button Text="{u:I18n Cancel}"
|
||||||
IsVisible="{Binding ShowCancelButton}"
|
IsVisible="{Binding ShowCancelButton}"
|
||||||
Clicked="Cancel_Clicked" />
|
Clicked="Cancel_Clicked"
|
||||||
|
AutomationId="cancel_button"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon),
|
nameof(ShowPasswordIcon)
|
||||||
nameof(PasswordVisibilityAccessibilityText)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +85,6 @@ namespace Bit.App.Pages
|
|||||||
public Command LogInCommand { get; }
|
public Command LogInCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
public 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; }
|
||||||
|
|||||||
@@ -81,12 +81,10 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||||
string ssoToken;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await _apiService.PreValidateSso(OrgIdentifier);
|
await _apiService.PreValidateSso(OrgIdentifier);
|
||||||
ssoToken = response.Token;
|
|
||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
@@ -114,8 +112,7 @@ namespace Bit.App.Pages
|
|||||||
"response_type=code&scope=api%20offline_access&" +
|
"response_type=code&scope=api%20offline_access&" +
|
||||||
"state=" + state + "&code_challenge=" + codeChallenge + "&" +
|
"state=" + state + "&code_challenge=" + codeChallenge + "&" +
|
||||||
"code_challenge_method=S256&response_mode=query&" +
|
"code_challenge_method=S256&response_mode=query&" +
|
||||||
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier) + "&" +
|
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier);
|
||||||
"ssoToken=" + Uri.EscapeDataString(ssoToken);
|
|
||||||
|
|
||||||
WebAuthenticatorResult authResult = null;
|
WebAuthenticatorResult authResult = null;
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -68,8 +68,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n MasterPasswordDescription}"
|
Text="{u:I18n MasterPasswordDescription}"
|
||||||
@@ -107,8 +106,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon),
|
nameof(ShowPasswordIcon)
|
||||||
nameof(PasswordVisibilityAccessibilityText)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +73,6 @@ namespace Bit.App.Pages
|
|||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public Command ToggleConfirmPasswordCommand { get; }
|
public Command ToggleConfirmPasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
|
|||||||
@@ -107,8 +107,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n MasterPasswordDescription}"
|
Text="{u:I18n MasterPasswordDescription}"
|
||||||
@@ -146,8 +145,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
|
|||||||
@@ -55,11 +55,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new[]
|
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
||||||
{
|
|
||||||
nameof(ShowPasswordIcon),
|
|
||||||
nameof(PasswordVisibilityAccessibilityText)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPolicyInEffect
|
public bool IsPolicyInEffect
|
||||||
@@ -90,7 +86,6 @@ namespace Bit.App.Pages
|
|||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public Command ToggleConfirmPasswordCommand { get; }
|
public Command ToggleConfirmPasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
public string ConfirmMasterPassword { get; set; }
|
public string ConfirmMasterPassword { get; set; }
|
||||||
public string Hint { get; set; }
|
public string Hint { get; set; }
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ namespace Bit.App.Pages
|
|||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
private readonly IAppIdService _appIdService;
|
|
||||||
|
|
||||||
private TwoFactorProviderType? _selectedProviderType;
|
private TwoFactorProviderType? _selectedProviderType;
|
||||||
private string _totpInstruction;
|
private string _totpInstruction;
|
||||||
@@ -50,7 +49,6 @@ namespace Bit.App.Pages
|
|||||||
_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");
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
|
||||||
|
|
||||||
PageTitle = AppResources.TwoStepLogin;
|
PageTitle = AppResources.TwoStepLogin;
|
||||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
@@ -382,8 +380,7 @@ namespace Bit.App.Pages
|
|||||||
var request = new TwoFactorEmailRequest
|
var request = new TwoFactorEmailRequest
|
||||||
{
|
{
|
||||||
Email = _authService.Email,
|
Email = _authService.Email,
|
||||||
MasterPasswordHash = _authService.MasterPasswordHash,
|
MasterPasswordHash = _authService.MasterPasswordHash
|
||||||
DeviceIdentifier = await _appIdService.GetAppIdAsync()
|
|
||||||
};
|
};
|
||||||
await _apiService.PostTwoFactorEmailAsync(request);
|
await _apiService.PostTwoFactorEmailAsync(request);
|
||||||
if (showLoading)
|
if (showLoading)
|
||||||
|
|||||||
@@ -105,8 +105,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
@@ -141,8 +140,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ namespace Bit.App.Pages
|
|||||||
VerticalOptions = LayoutOptions.CenterAndExpand,
|
VerticalOptions = LayoutOptions.CenterAndExpand,
|
||||||
HorizontalOptions = LayoutOptions.Center,
|
HorizontalOptions = LayoutOptions.Center,
|
||||||
Color = ThemeManager.GetResourceColor("PrimaryColor"),
|
Color = ThemeManager.GetResourceColor("PrimaryColor"),
|
||||||
|
AutomationId = "activity_indicator"
|
||||||
};
|
};
|
||||||
if (targetView != null)
|
if (targetView != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,14 +37,11 @@ namespace Bit.App.Pages
|
|||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// PrefersEphemeralWebBrowserSession should be false to allow access to the hCaptcha accessibility
|
|
||||||
// cookie set in the default browser
|
|
||||||
// https://www.hcaptcha.com/accessibility
|
|
||||||
var options = new WebAuthenticatorOptions
|
var options = new WebAuthenticatorOptions
|
||||||
{
|
{
|
||||||
Url = new Uri(url),
|
Url = new Uri(url),
|
||||||
CallbackUrl = new Uri(callbackUri),
|
CallbackUrl = new Uri(callbackUri),
|
||||||
PrefersEphemeralWebBrowserSession = false,
|
PrefersEphemeralWebBrowserSession = true,
|
||||||
};
|
};
|
||||||
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,7 @@
|
|||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||||
ItemsSource="{Binding History}"
|
ItemsSource="{Binding History}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform">
|
||||||
ExtraDataForLogging="Generator History Page">
|
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
||||||
<Grid
|
<Grid
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ namespace Bit.App.Pages
|
|||||||
private async void CopyAsync(GeneratedPasswordHistory ph)
|
private async void CopyAsync(GeneratedPasswordHistory ph)
|
||||||
{
|
{
|
||||||
await _clipboardService.CopyTextAsync(ph.Password);
|
await _clipboardService.CopyTextAsync(ph.Password);
|
||||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
_platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateOnThemeChanged()
|
public async Task UpdateOnThemeChanged()
|
||||||
|
|||||||
@@ -183,68 +183,52 @@
|
|||||||
<Label
|
<Label
|
||||||
Text="A-Z"
|
Text="A-Z"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand"
|
HorizontalOptions="StartAndExpand" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Uppercase}"
|
IsToggled="{Binding Uppercase}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End"
|
HorizontalOptions="End" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="a-z"
|
Text="a-z"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand"
|
HorizontalOptions="StartAndExpand" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}"/>
|
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Lowercase}"
|
IsToggled="{Binding Lowercase}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End"
|
HorizontalOptions="End" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}" />
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="0-9"
|
Text="0-9"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand"
|
HorizontalOptions="StartAndExpand" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Number}"
|
IsToggled="{Binding Number}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End"
|
HorizontalOptions="End" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="!@#$%^&*"
|
Text="!@#$%^&*"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand"
|
HorizontalOptions="StartAndExpand" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Special}"
|
IsToggled="{Binding Special}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End"
|
HorizontalOptions="End" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-stepper">
|
<StackLayout StyleClass="box-row, box-row-stepper">
|
||||||
@@ -293,7 +277,7 @@
|
|||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand" />
|
HorizontalOptions="StartAndExpand" />
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding AvoidAmbiguousChars}"
|
IsToggled="{Binding AvoidAmbiguous}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Bit.App.Utilities;
|
|||||||
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;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -22,7 +23,7 @@ namespace Bit.App.Pages
|
|||||||
private bool _lowercase;
|
private bool _lowercase;
|
||||||
private bool _number;
|
private bool _number;
|
||||||
private bool _special;
|
private bool _special;
|
||||||
private bool _allowAmbiguousChars;
|
private bool _avoidAmbiguous;
|
||||||
private int _minNumber;
|
private int _minNumber;
|
||||||
private int _minSpecial;
|
private int _minSpecial;
|
||||||
private int _length = 5;
|
private int _length = 5;
|
||||||
@@ -129,29 +130,19 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowAmbiguousChars
|
public bool AvoidAmbiguous
|
||||||
{
|
{
|
||||||
get => _allowAmbiguousChars;
|
get => _avoidAmbiguous;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _allowAmbiguousChars, value,
|
if (SetProperty(ref _avoidAmbiguous, value))
|
||||||
additionalPropertyNames: new string[]
|
|
||||||
{
|
{
|
||||||
nameof(AvoidAmbiguousChars)
|
_options.Ambiguous = !value;
|
||||||
}))
|
|
||||||
{
|
|
||||||
_options.AllowAmbiguousChar = value;
|
|
||||||
var task = SaveOptionsAsync();
|
var task = SaveOptionsAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AvoidAmbiguousChars
|
|
||||||
{
|
|
||||||
get => !AllowAmbiguousChars;
|
|
||||||
set => AllowAmbiguousChars = !value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int MinNumber
|
public int MinNumber
|
||||||
{
|
{
|
||||||
get => _minNumber;
|
get => _minNumber;
|
||||||
@@ -318,12 +309,13 @@ namespace Bit.App.Pages
|
|||||||
public async Task CopyAsync()
|
public async Task CopyAsync()
|
||||||
{
|
{
|
||||||
await _clipboardService.CopyTextAsync(Password);
|
await _clipboardService.CopyTextAsync(Password);
|
||||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
_platformUtilsService.ShowToast("success", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadFromOptions()
|
private void LoadFromOptions()
|
||||||
{
|
{
|
||||||
AllowAmbiguousChars = _options.AllowAmbiguousChar.GetValueOrDefault();
|
AvoidAmbiguous = !_options.Ambiguous.GetValueOrDefault();
|
||||||
TypeSelectedIndex = _options.Type == "passphrase" ? 1 : 0;
|
TypeSelectedIndex = _options.Type == "passphrase" ? 1 : 0;
|
||||||
IsPassword = TypeSelectedIndex == 0;
|
IsPassword = TypeSelectedIndex == 0;
|
||||||
MinNumber = _options.MinNumber.GetValueOrDefault();
|
MinNumber = _options.MinNumber.GetValueOrDefault();
|
||||||
@@ -341,7 +333,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private void SetOptions()
|
private void SetOptions()
|
||||||
{
|
{
|
||||||
_options.AllowAmbiguousChar = AllowAmbiguousChars;
|
_options.Ambiguous = !AvoidAmbiguous;
|
||||||
_options.Type = TypeSelectedIndex == 1 ? "passphrase" : "password";
|
_options.Type = TypeSelectedIndex == 1 ? "passphrase" : "password";
|
||||||
_options.MinNumber = MinNumber;
|
_options.MinNumber = MinNumber;
|
||||||
_options.MinSpecial = MinSpecial;
|
_options.MinSpecial = MinSpecial;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
<pages:BaseContentPage
|
<pages:BaseContentPage
|
||||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
|
||||||
x:Class="Bit.App.Pages.SendAddEditPage"
|
x:Class="Bit.App.Pages.SendAddEditPage"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
@@ -122,7 +121,6 @@
|
|||||||
Clicked="FileType_Clicked"
|
Clicked="FileType_Clicked"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n File}"
|
AutomationProperties.Name="{u:I18n File}"
|
||||||
AutomationProperties.HelpText="{Binding FileTypeAccessibilityLabel}"
|
|
||||||
Grid.Column="0">
|
Grid.Column="0">
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -134,7 +132,6 @@
|
|||||||
Clicked="TextType_Clicked"
|
Clicked="TextType_Clicked"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Text}"
|
AutomationProperties.Name="{u:I18n Text}"
|
||||||
AutomationProperties.HelpText="{Binding TextTypeAccessibilityLabel}"
|
|
||||||
Grid.Column="1">
|
Grid.Column="1">
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -253,31 +250,28 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Spacing="0"
|
Spacing="0">
|
||||||
xct:TouchEffect.Command="{Binding ToggleOptionsCommand}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{Binding OptionsAccessilibityText}">
|
|
||||||
<Button
|
<Button
|
||||||
Text="{u:I18n Options}"
|
Text="{u:I18n Options}"
|
||||||
x:Name="_btnOptions"
|
x:Name="_btnOptions"
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{DynamicResource PrimaryColor}"
|
TextColor="{DynamicResource PrimaryColor}"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
AutomationProperties.IsInAccessibleTree="False"/>
|
Clicked="ToggleOptions_Clicked"/>
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
x:Name="_btnOptionsUp"
|
x:Name="_btnOptionsUp"
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
|
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{DynamicResource PrimaryColor}"
|
TextColor="{DynamicResource PrimaryColor}"
|
||||||
IsVisible="{Binding ShowOptions}"
|
Clicked="ToggleOptions_Clicked"
|
||||||
AutomationProperties.IsInAccessibleTree="False"/>
|
IsVisible="{Binding ShowOptions}" />
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
x:Name="_btnOptionsDown"
|
x:Name="_btnOptionsDown"
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
|
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{DynamicResource PrimaryColor}"
|
TextColor="{DynamicResource PrimaryColor}"
|
||||||
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}"
|
Clicked="ToggleOptions_Clicked"
|
||||||
AutomationProperties.IsInAccessibleTree="False"/>
|
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout IsVisible="{Binding ShowOptions}">
|
<StackLayout IsVisible="{Binding ShowOptions}">
|
||||||
<StackLayout
|
<StackLayout
|
||||||
@@ -444,8 +438,7 @@
|
|||||||
Command="{Binding TogglePasswordCommand}"
|
Command="{Binding TogglePasswordCommand}"
|
||||||
Margin="10,0,0,0"
|
Margin="10,0,0,0"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n PasswordInfo}"
|
Text="{u:I18n PasswordInfo}"
|
||||||
|
|||||||
@@ -209,6 +209,11 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleOptions_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_vm.ToggleOptions();
|
||||||
|
}
|
||||||
|
|
||||||
private void ClearExpirationDate_Clicked(object sender, EventArgs e)
|
private void ClearExpirationDate_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
nameof(IsText),
|
nameof(IsText),
|
||||||
nameof(IsFile),
|
nameof(IsFile),
|
||||||
nameof(FileTypeAccessibilityLabel),
|
|
||||||
nameof(TextTypeAccessibilityLabel)
|
|
||||||
};
|
};
|
||||||
private bool _disableHideEmail;
|
private bool _disableHideEmail;
|
||||||
private bool _sendOptionsPolicyInEffect;
|
private bool _sendOptionsPolicyInEffect;
|
||||||
@@ -61,7 +59,6 @@ namespace Bit.App.Pages
|
|||||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
ToggleOptionsCommand = new Command(ToggleOptions);
|
|
||||||
|
|
||||||
TypeOptions = new List<KeyValuePair<string, SendType>>
|
TypeOptions = new List<KeyValuePair<string, SendType>>
|
||||||
{
|
{
|
||||||
@@ -92,7 +89,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Command TogglePasswordCommand { get; set; }
|
public Command TogglePasswordCommand { get; set; }
|
||||||
public Command ToggleOptionsCommand { get; set; }
|
|
||||||
public string SendId { get; set; }
|
public string SendId { get; set; }
|
||||||
public int SegmentedButtonHeight { get; set; }
|
public int SegmentedButtonHeight { get; set; }
|
||||||
public int SegmentedButtonFontSize { get; set; }
|
public int SegmentedButtonFontSize { get; set; }
|
||||||
@@ -106,7 +102,6 @@ namespace Bit.App.Pages
|
|||||||
public bool DisableHideEmailControl { get; set; }
|
public bool DisableHideEmailControl { get; set; }
|
||||||
public bool IsAddFromShare { get; set; }
|
public bool IsAddFromShare { get; set; }
|
||||||
public string ShareOnSaveText => CopyInsteadOfShareAfterSaving ? AppResources.CopySendLinkOnSave : AppResources.ShareOnSave;
|
public string ShareOnSaveText => CopyInsteadOfShareAfterSaving ? AppResources.CopySendLinkOnSave : AppResources.ShareOnSave;
|
||||||
public string OptionsAccessilibityText => ShowOptions ? AppResources.OptionsExpanded : AppResources.OptionsCollapsed;
|
|
||||||
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
|
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
|
||||||
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
|
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
|
||||||
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
||||||
@@ -139,11 +134,7 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowOptions
|
public bool ShowOptions
|
||||||
{
|
{
|
||||||
get => _showOptions;
|
get => _showOptions;
|
||||||
set => SetProperty(ref _showOptions, value,
|
set => SetProperty(ref _showOptions, value);
|
||||||
additionalPropertyNames: new[]
|
|
||||||
{
|
|
||||||
nameof(OptionsAccessilibityText)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
public int ExpirationDateTypeSelectedIndex
|
public int ExpirationDateTypeSelectedIndex
|
||||||
{
|
{
|
||||||
@@ -220,8 +211,7 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new[]
|
additionalPropertyNames: new[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon),
|
nameof(ShowPasswordIcon)
|
||||||
nameof(PasswordVisibilityAccessibilityText)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public bool DisableHideEmail
|
public bool DisableHideEmail
|
||||||
@@ -241,9 +231,6 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
|
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
|
||||||
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
|
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
public string FileTypeAccessibilityLabel => IsFile ? AppResources.FileTypeIsSelected : AppResources.FileTypeIsNotSelected;
|
|
||||||
public string TextTypeAccessibilityLabel => IsText ? AppResources.TextTypeIsSelected : AppResources.TextTypeIsNotSelected;
|
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Pages.SendGroupingsPage"
|
x:Class="Bit.App.Pages.SendGroupingsPage"
|
||||||
@@ -138,8 +138,7 @@
|
|||||||
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform" />
|
||||||
ExtraDataForLogging="Send Groupings Page" />
|
|
||||||
</RefreshView>
|
</RefreshView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<pages:BaseContentPage
|
<pages:BaseContentPage
|
||||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
@@ -66,8 +66,7 @@
|
|||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform">
|
||||||
ExtraDataForLogging="Sends Page">
|
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:SendView">
|
<DataTemplate x:DataType="views:SendView">
|
||||||
<controls:SendViewCell
|
<controls:SendViewCell
|
||||||
|
|||||||
@@ -105,7 +105,6 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
|
||||||
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n ConfirmYourIdentity}"
|
Text="{u:I18n ConfirmYourIdentity}"
|
||||||
|
|||||||
@@ -109,11 +109,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[] { nameof(ShowPasswordIcon) });
|
||||||
{
|
|
||||||
nameof(ShowPasswordIcon),
|
|
||||||
nameof(PasswordVisibilityAccessibilityText),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UseOTPVerification
|
public bool UseOTPVerification
|
||||||
@@ -143,7 +139,6 @@ namespace Bit.App.Pages
|
|||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
|
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
|
|
||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,8 +38,7 @@
|
|||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform">
|
||||||
ExtraDataForLogging="Folders Page">
|
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:FolderView">
|
<DataTemplate x:DataType="views:FolderView">
|
||||||
<controls:ExtendedStackLayout
|
<controls:ExtendedStackLayout
|
||||||
|
|||||||
@@ -113,7 +113,6 @@
|
|||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform" />
|
||||||
ExtraDataForLogging="Settings Page" />
|
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -167,10 +167,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _vm.UpdatePinAsync();
|
await _vm.UpdatePinAsync();
|
||||||
}
|
}
|
||||||
else if (item.Name == AppResources.SubmitCrashLogs)
|
|
||||||
{
|
|
||||||
await _vm.LoggerReportingAsync();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var biometricName = AppResources.Biometrics;
|
var biometricName = AppResources.Biometrics;
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ using Bit.App.Resources;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
using ZXing.Client.Result;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -29,7 +29,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly IClipboardService _clipboardService;
|
private readonly IClipboardService _clipboardService;
|
||||||
private readonly ILogger _loggerService;
|
|
||||||
private const int CustomVaultTimeoutValue = -100;
|
private const int CustomVaultTimeoutValue = -100;
|
||||||
|
|
||||||
private bool _supportsBiometric;
|
private bool _supportsBiometric;
|
||||||
@@ -39,7 +39,6 @@ namespace Bit.App.Pages
|
|||||||
private string _vaultTimeoutDisplayValue;
|
private string _vaultTimeoutDisplayValue;
|
||||||
private string _vaultTimeoutActionDisplayValue;
|
private string _vaultTimeoutActionDisplayValue;
|
||||||
private bool _showChangeMasterPassword;
|
private bool _showChangeMasterPassword;
|
||||||
private bool _reportLoggingEnabled;
|
|
||||||
|
|
||||||
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
||||||
new List<KeyValuePair<string, int?>>
|
new List<KeyValuePair<string, int?>>
|
||||||
@@ -80,7 +79,6 @@ namespace Bit.App.Pages
|
|||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
_loggerService = ServiceContainer.Resolve<ILogger>("logger");
|
|
||||||
|
|
||||||
GroupedItems = new ObservableRangeCollection<ISettingsPageListItem>();
|
GroupedItems = new ObservableRangeCollection<ISettingsPageListItem>();
|
||||||
PageTitle = AppResources.Settings;
|
PageTitle = AppResources.Settings;
|
||||||
@@ -125,7 +123,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
||||||
!await _keyConnectorService.GetUsesKeyConnector();
|
!await _keyConnectorService.GetUsesKeyConnector();
|
||||||
_reportLoggingEnabled = await _loggerService.IsEnabled();
|
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,26 +286,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoggerReportingAsync()
|
|
||||||
{
|
|
||||||
var options = new[]
|
|
||||||
{
|
|
||||||
CreateSelectableOption(AppResources.Yes, _reportLoggingEnabled),
|
|
||||||
CreateSelectableOption(AppResources.No, !_reportLoggingEnabled),
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection = await Page.DisplayActionSheet(AppResources.SubmitCrashLogsDescription, AppResources.Cancel, null, options);
|
|
||||||
|
|
||||||
if (selection == null || selection == AppResources.Cancel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _loggerService.SetEnabled(CompareSelection(selection, AppResources.Yes));
|
|
||||||
_reportLoggingEnabled = await _loggerService.IsEnabled();
|
|
||||||
BuildList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task VaultTimeoutActionAsync()
|
public async Task VaultTimeoutActionAsync()
|
||||||
{
|
{
|
||||||
var options = _vaultTimeoutActions.Select(o =>
|
var options = _vaultTimeoutActions.Select(o =>
|
||||||
@@ -516,19 +494,11 @@ namespace Bit.App.Pages
|
|||||||
toolsItems.Add(new SettingsPageListItem { Name = AppResources.LearnOrg });
|
toolsItems.Add(new SettingsPageListItem { Name = AppResources.LearnOrg });
|
||||||
toolsItems.Add(new SettingsPageListItem { Name = AppResources.WebVault });
|
toolsItems.Add(new SettingsPageListItem { Name = AppResources.WebVault });
|
||||||
}
|
}
|
||||||
|
|
||||||
var otherItems = new List<SettingsPageListItem>
|
var otherItems = new List<SettingsPageListItem>
|
||||||
{
|
{
|
||||||
new SettingsPageListItem { Name = AppResources.Options },
|
new SettingsPageListItem { Name = AppResources.Options },
|
||||||
new SettingsPageListItem { Name = AppResources.About },
|
new SettingsPageListItem { Name = AppResources.About },
|
||||||
new SettingsPageListItem { Name = AppResources.HelpAndFeedback },
|
new SettingsPageListItem { Name = AppResources.HelpAndFeedback },
|
||||||
#if !FDROID
|
|
||||||
new SettingsPageListItem
|
|
||||||
{
|
|
||||||
Name = AppResources.SubmitCrashLogs,
|
|
||||||
SubLabel = _reportLoggingEnabled ? AppResources.Enabled : AppResources.Disabled,
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
new SettingsPageListItem { Name = AppResources.RateTheApp },
|
new SettingsPageListItem { Name = AppResources.RateTheApp },
|
||||||
new SettingsPageListItem { Name = AppResources.DeleteAccount }
|
new SettingsPageListItem { Name = AppResources.DeleteAccount }
|
||||||
};
|
};
|
||||||
@@ -606,9 +576,5 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return _vaultTimeouts.FirstOrDefault(o => o.Key == key).Value;
|
return _vaultTimeouts.FirstOrDefault(o => o.Key == key).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option;
|
|
||||||
|
|
||||||
private bool CompareSelection(string selection, string compareTo) => selection == compareTo || selection == $"✓ {compareTo}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using Bit.App.Effects;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Effects;
|
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
@@ -12,10 +10,8 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class TabsPage : TabbedPage
|
public class TabsPage : TabbedPage
|
||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
|
||||||
|
|
||||||
private NavigationPage _groupingsPage;
|
private NavigationPage _groupingsPage;
|
||||||
private NavigationPage _sendGroupingsPage;
|
private NavigationPage _sendGroupingsPage;
|
||||||
@@ -23,7 +19,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
||||||
{
|
{
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
@@ -83,26 +78,12 @@ namespace Bit.App.Pages
|
|||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
_broadcasterService.Subscribe(nameof(TabsPage), async (message) =>
|
|
||||||
{
|
|
||||||
if (message.Command == "syncCompleted")
|
|
||||||
{
|
|
||||||
Device.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await UpdateVaultButtonTitleAsync();
|
|
||||||
if (await _keyConnectorService.UserNeedsMigration())
|
if (await _keyConnectorService.UserNeedsMigration())
|
||||||
{
|
{
|
||||||
_messagingService.Send("convertAccountToKeyConnector");
|
_messagingService.Send("convertAccountToKeyConnector");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
|
||||||
{
|
|
||||||
base.OnDisappearing();
|
|
||||||
_broadcasterService.Unsubscribe(nameof(TabsPage));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetToVaultPage()
|
public void ResetToVaultPage()
|
||||||
{
|
{
|
||||||
CurrentPage = _groupingsPage;
|
CurrentPage = _groupingsPage;
|
||||||
@@ -150,19 +131,5 @@ namespace Bit.App.Pages
|
|||||||
groupingsPage.HideAccountSwitchingOverlayAsync().FireAndForget();
|
groupingsPage.HideAccountSwitchingOverlayAsync().FireAndForget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateVaultButtonTitleAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
|
||||||
var isShowingVaultFilter = await policyService.ShouldShowVaultFilterAsync();
|
|
||||||
_groupingsPage.Title = isShowingVaultFilter ? AppResources.Vaults : AppResources.MyVault;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Value.Exception(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,6 @@
|
|||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
|
||||||
IsVisible="{Binding Cipher.ViewPassword}" />
|
IsVisible="{Binding Cipher.ViewPassword}" />
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ namespace Bit.App.Pages
|
|||||||
CipherType? type = null,
|
CipherType? type = null,
|
||||||
string folderId = null,
|
string folderId = null,
|
||||||
string collectionId = null,
|
string collectionId = null,
|
||||||
string organizationId = null,
|
|
||||||
string name = null,
|
string name = null,
|
||||||
string uri = null,
|
string uri = null,
|
||||||
bool fromAutofill = false,
|
bool fromAutofill = false,
|
||||||
@@ -52,7 +51,6 @@ namespace Bit.App.Pages
|
|||||||
_vm.CipherId = cipherId;
|
_vm.CipherId = cipherId;
|
||||||
_vm.FolderId = folderId == "none" ? null : folderId;
|
_vm.FolderId = folderId == "none" ? null : folderId;
|
||||||
_vm.CollectionIds = collectionId != null ? new HashSet<string>(new List<string> { collectionId }) : null;
|
_vm.CollectionIds = collectionId != null ? new HashSet<string>(new List<string> { collectionId }) : null;
|
||||||
_vm.OrganizationId = organizationId;
|
|
||||||
_vm.Type = type;
|
_vm.Type = type;
|
||||||
_vm.DefaultName = name ?? appOptions?.SaveName;
|
_vm.DefaultName = name ?? appOptions?.SaveName;
|
||||||
_vm.DefaultUri = uri ?? appOptions?.Uri;
|
_vm.DefaultUri = uri ?? appOptions?.Uri;
|
||||||
|
|||||||
@@ -249,8 +249,7 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon),
|
nameof(ShowPasswordIcon)
|
||||||
nameof(PasswordVisibilityAccessibilityText)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public bool ShowCardNumber
|
public bool ShowCardNumber
|
||||||
@@ -299,7 +298,6 @@ namespace Bit.App.Pages
|
|||||||
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
|
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
|
||||||
public bool AllowPersonal { get; set; }
|
public bool AllowPersonal { get; set; }
|
||||||
public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None;
|
public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -85,8 +85,7 @@
|
|||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform" />
|
||||||
ExtraDataForLogging="Autofill Ciphers Page" />
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -33,9 +33,7 @@
|
|||||||
Text=""
|
Text=""
|
||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
Clicked="BackButton_Clicked"
|
Clicked="BackButton_Clicked"
|
||||||
x:Name="_backButton"
|
x:Name="_backButton" />
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n TapToGoBack}"/>
|
|
||||||
<controls:ExtendedSearchBar
|
<controls:ExtendedSearchBar
|
||||||
x:Name="_searchBar"
|
x:Name="_searchBar"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
@@ -49,31 +47,6 @@
|
|||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<StackLayout x:Name="_mainLayout" Spacing="0" Padding="0">
|
<StackLayout x:Name="_mainLayout" Spacing="0" Padding="0">
|
||||||
<StackLayout
|
|
||||||
IsVisible="{Binding ShowVaultFilter}"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
Margin="0,5,0,0">
|
|
||||||
<Label
|
|
||||||
Text="{Binding VaultFilterDescription}"
|
|
||||||
LineBreakMode="TailTruncation"
|
|
||||||
Margin="10,0"
|
|
||||||
StyleClass="text-md, text-muted"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
HorizontalOptions="StartAndExpand"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Filter}" />
|
|
||||||
<controls:MiButton
|
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
|
||||||
StyleClass="list-row-button-text, list-row-button-platform"
|
|
||||||
Command="{Binding VaultFilterCommand}"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
HorizontalOptions="End"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Filter}" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="box-row-separator" />
|
|
||||||
|
|
||||||
<controls:IconLabel IsVisible="{Binding ShowSearchDirection}"
|
<controls:IconLabel IsVisible="{Binding ShowSearchDirection}"
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.Search}}"
|
Text="{Binding Source={x:Static core:BitwardenIcons.Search}}"
|
||||||
StyleClass="text-muted"
|
StyleClass="text-muted"
|
||||||
@@ -93,8 +66,7 @@
|
|||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform">
|
||||||
ExtraDataForLogging="Ciphers Page">
|
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:CipherView">
|
<DataTemplate x:DataType="views:CipherView">
|
||||||
<controls:CipherViewCell
|
<controls:CipherViewCell
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ namespace Bit.App.Pages
|
|||||||
private CiphersPageViewModel _vm;
|
private CiphersPageViewModel _vm;
|
||||||
private bool _hasFocused;
|
private bool _hasFocused;
|
||||||
|
|
||||||
public CiphersPage(Func<CipherView, bool> filter, string pageTitle = null, string vaultFilterSelection = null,
|
public CiphersPage(Func<CipherView, bool> filter, string pageTitle = null, string autofillUrl = null, bool deleted = false)
|
||||||
string autofillUrl = null, bool deleted = false)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as CiphersPageViewModel;
|
_vm = BindingContext as CiphersPageViewModel;
|
||||||
@@ -34,7 +33,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_vm.PageTitle = AppResources.SearchVault;
|
_vm.PageTitle = AppResources.SearchVault;
|
||||||
}
|
}
|
||||||
_vm.VaultFilterDescription = vaultFilterSelection;
|
|
||||||
|
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class CiphersPageViewModel : VaultFilterViewModel
|
public class CiphersPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
@@ -23,10 +23,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
private readonly IOrganizationService _organizationService;
|
|
||||||
private readonly IPolicyService _policyService;
|
|
||||||
private CancellationTokenSource _searchCancellationTokenSource;
|
private CancellationTokenSource _searchCancellationTokenSource;
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
private bool _showList;
|
private bool _showList;
|
||||||
@@ -40,9 +37,6 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
|
||||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
|
||||||
|
|
||||||
Ciphers = new ExtendedObservableCollection<CipherView>();
|
Ciphers = new ExtendedObservableCollection<CipherView>();
|
||||||
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
||||||
@@ -54,11 +48,6 @@ namespace Bit.App.Pages
|
|||||||
public string AutofillUrl { get; set; }
|
public string AutofillUrl { get; set; }
|
||||||
public bool Deleted { get; set; }
|
public bool Deleted { get; set; }
|
||||||
|
|
||||||
protected override ICipherService cipherService => _cipherService;
|
|
||||||
protected override IPolicyService policyService => _policyService;
|
|
||||||
protected override IOrganizationService organizationService => _organizationService;
|
|
||||||
protected override ILogger logger => _logger;
|
|
||||||
|
|
||||||
public bool ShowNoData
|
public bool ShowNoData
|
||||||
{
|
{
|
||||||
get => _showNoData;
|
get => _showNoData;
|
||||||
@@ -87,9 +76,11 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
await InitVaultFilterAsync(true);
|
|
||||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
PerformSearchIfPopulated();
|
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
||||||
|
{
|
||||||
|
Search((Page as CiphersPage).SearchBar.Text, 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Search(string searchText, int? timeout = null)
|
public void Search(string searchText, int? timeout = null)
|
||||||
@@ -116,9 +107,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var vaultFilteredCiphers = await GetAllCiphersAsync();
|
|
||||||
ciphers = await _searchService.SearchCiphersAsync(searchText,
|
ciphers = await _searchService.SearchCiphersAsync(searchText,
|
||||||
Filter ?? (c => c.IsDeleted == Deleted), vaultFilteredCiphers, cts.Token);
|
Filter ?? (c => c.IsDeleted == Deleted), null, cts.Token);
|
||||||
cts.Token.ThrowIfCancellationRequested();
|
cts.Token.ThrowIfCancellationRequested();
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -202,19 +192,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PerformSearchIfPopulated()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
|
||||||
{
|
|
||||||
Search((Page as CiphersPage).SearchBar.Text, 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnVaultFilterSelectedAsync()
|
|
||||||
{
|
|
||||||
PerformSearchIfPopulated();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void CipherOptionsAsync(CipherView cipher)
|
private async void CipherOptionsAsync(CipherView cipher)
|
||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
xmlns:effects="clr-namespace:Bit.App.Effects"
|
xmlns:effects="clr-namespace:Bit.App.Effects"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
|
||||||
x:DataType="pages:GroupingsPageViewModel"
|
x:DataType="pages:GroupingsPageViewModel"
|
||||||
Title="{Binding PageTitle}"
|
Title="{Binding PageTitle}"
|
||||||
x:Name="_page">
|
x:Name="_page">
|
||||||
@@ -107,30 +106,6 @@
|
|||||||
GroupTemplate="{StaticResource groupTemplate}" />
|
GroupTemplate="{StaticResource groupTemplate}" />
|
||||||
|
|
||||||
<StackLayout x:Key="mainLayout" x:Name="_mainLayout">
|
<StackLayout x:Key="mainLayout" x:Name="_mainLayout">
|
||||||
<StackLayout
|
|
||||||
IsVisible="{Binding ShowVaultFilter}"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
Margin="0,5,0,0">
|
|
||||||
<Label
|
|
||||||
Text="{Binding VaultFilterDescription}"
|
|
||||||
LineBreakMode="TailTruncation"
|
|
||||||
Margin="10,0"
|
|
||||||
StyleClass="text-md, text-muted"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
HorizontalOptions="StartAndExpand"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Filter}" />
|
|
||||||
<controls:MiButton
|
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
|
||||||
StyleClass="list-row-button-text, list-row-button-platform"
|
|
||||||
Command="{Binding VaultFilterCommand}"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
HorizontalOptions="End"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Filter}" />
|
|
||||||
</StackLayout>
|
|
||||||
|
|
||||||
<StackLayout
|
<StackLayout
|
||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
Padding="20, 0"
|
Padding="20, 0"
|
||||||
@@ -155,8 +130,7 @@
|
|||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform" />
|
||||||
ExtraDataForLogging="Groupings Page" />
|
|
||||||
</RefreshView>
|
</RefreshView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ namespace Bit.App.Pages
|
|||||||
private PreviousPageInfo _previousPage;
|
private PreviousPageInfo _previousPage;
|
||||||
|
|
||||||
public GroupingsPage(bool mainPage, CipherType? type = null, string folderId = null,
|
public GroupingsPage(bool mainPage, CipherType? type = null, string folderId = null,
|
||||||
string collectionId = null, string pageTitle = null, string vaultFilterSelection = null,
|
string collectionId = null, string pageTitle = null, PreviousPageInfo previousPage = null,
|
||||||
PreviousPageInfo previousPage = null, bool deleted = false)
|
bool deleted = false)
|
||||||
{
|
{
|
||||||
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -52,10 +52,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_vm.PageTitle = pageTitle;
|
_vm.PageTitle = pageTitle;
|
||||||
}
|
}
|
||||||
if (vaultFilterSelection != null)
|
|
||||||
{
|
|
||||||
_vm.VaultFilterDescription = vaultFilterSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
@@ -263,7 +259,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (!_vm.Deleted && DoOnce())
|
if (!_vm.Deleted && DoOnce())
|
||||||
{
|
{
|
||||||
var page = new AddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId, _vm.GetVaultFilterOrgId());
|
var page = new AddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class GroupingsPageViewModel : VaultFilterViewModel
|
public class GroupingsPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private const int NoFolderListSize = 100;
|
private const int NoFolderListSize = 100;
|
||||||
|
|
||||||
@@ -46,8 +46,6 @@ namespace Bit.App.Pages
|
|||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
private readonly IOrganizationService _organizationService;
|
|
||||||
private readonly IPolicyService _policyService;
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public GroupingsPageViewModel()
|
public GroupingsPageViewModel()
|
||||||
@@ -62,11 +60,10 @@ namespace Bit.App.Pages
|
|||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
|
||||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
Loading = true;
|
Loading = true;
|
||||||
|
PageTitle = AppResources.MyVault;
|
||||||
GroupedItems = new ObservableRangeCollection<IGroupingsPageListItem>();
|
GroupedItems = new ObservableRangeCollection<IGroupingsPageListItem>();
|
||||||
RefreshCommand = new Command(async () =>
|
RefreshCommand = new Command(async () =>
|
||||||
{
|
{
|
||||||
@@ -90,9 +87,8 @@ namespace Bit.App.Pages
|
|||||||
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; }
|
||||||
public bool ShowNoFolderCipherGroup => NoFolderCiphers != null
|
public bool ShowNoFolderCiphers => (NoFolderCiphers?.Count ?? int.MaxValue) < NoFolderListSize &&
|
||||||
&& NoFolderCiphers.Count < NoFolderListSize
|
(!Collections?.Any() ?? true);
|
||||||
&& (Collections is null || !Collections.Any());
|
|
||||||
public List<CipherView> Ciphers { get; set; }
|
public List<CipherView> Ciphers { get; set; }
|
||||||
public List<CipherView> FavoriteCiphers { get; set; }
|
public List<CipherView> FavoriteCiphers { get; set; }
|
||||||
public List<CipherView> NoFolderCiphers { get; set; }
|
public List<CipherView> NoFolderCiphers { get; set; }
|
||||||
@@ -101,11 +97,6 @@ namespace Bit.App.Pages
|
|||||||
public List<Core.Models.View.CollectionView> Collections { get; set; }
|
public List<Core.Models.View.CollectionView> Collections { get; set; }
|
||||||
public List<TreeNode<Core.Models.View.CollectionView>> NestedCollections { get; set; }
|
public List<TreeNode<Core.Models.View.CollectionView>> NestedCollections { get; set; }
|
||||||
|
|
||||||
protected override ICipherService cipherService => _cipherService;
|
|
||||||
protected override IPolicyService policyService => _policyService;
|
|
||||||
protected override IOrganizationService organizationService => _organizationService;
|
|
||||||
protected override ILogger logger => _logger;
|
|
||||||
|
|
||||||
public bool Refreshing
|
public bool Refreshing
|
||||||
{
|
{
|
||||||
get => _refreshing;
|
get => _refreshing;
|
||||||
@@ -181,12 +172,6 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await InitVaultFilterAsync(MainPage);
|
|
||||||
if (MainPage)
|
|
||||||
{
|
|
||||||
PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault;
|
|
||||||
}
|
|
||||||
|
|
||||||
_doingLoad = true;
|
_doingLoad = true;
|
||||||
LoadedOnce = true;
|
LoadedOnce = true;
|
||||||
ShowNoData = false;
|
ShowNoData = false;
|
||||||
@@ -200,9 +185,9 @@ namespace Bit.App.Pages
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LoadDataAsync();
|
await LoadDataAsync();
|
||||||
if (ShowNoFolderCipherGroup && (NestedFolders?.Any() ?? false))
|
if (ShowNoFolderCiphers && (NestedFolders?.Any() ?? false))
|
||||||
{
|
{
|
||||||
// Remove "No Folder" folder from folders group
|
// Remove "No Folder" from folder listing
|
||||||
NestedFolders = NestedFolders.GetRange(0, NestedFolders.Count - 1);
|
NestedFolders = NestedFolders.GetRange(0, NestedFolders.Count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +262,7 @@ namespace Bit.App.Pages
|
|||||||
groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
|
groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
|
||||||
ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
|
ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
|
||||||
}
|
}
|
||||||
if (ShowNoFolderCipherGroup)
|
if (ShowNoFolderCiphers)
|
||||||
{
|
{
|
||||||
var noFolderCiphersListItems = NoFolderCiphers.Select(
|
var noFolderCiphersListItems = NoFolderCiphers.Select(
|
||||||
c => new GroupingsPageListItem { Cipher = c }).ToList();
|
c => new GroupingsPageListItem { Cipher = c }).ToList();
|
||||||
@@ -309,12 +294,6 @@ namespace Bit.App.Pages
|
|||||||
items.AddRange(itemGroup);
|
items.AddRange(itemGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
|
|
||||||
// because of update to XF v5.0.0.2401
|
|
||||||
GroupedItems.Clear();
|
|
||||||
}
|
|
||||||
GroupedItems.ReplaceRange(items);
|
GroupedItems.ReplaceRange(items);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -337,12 +316,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if (groupedItems.Any())
|
if (groupedItems.Any())
|
||||||
{
|
{
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
|
|
||||||
// because of update to XF v5.0.0.2401
|
|
||||||
GroupedItems.Clear();
|
|
||||||
}
|
|
||||||
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
|
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
|
||||||
GroupedItems.AddRange(items);
|
GroupedItems.AddRange(items);
|
||||||
}
|
}
|
||||||
@@ -369,11 +342,6 @@ namespace Bit.App.Pages
|
|||||||
SyncRefreshing = false;
|
SyncRefreshing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnVaultFilterSelectedAsync()
|
|
||||||
{
|
|
||||||
await LoadAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SelectCipherAsync(CipherView cipher)
|
public async Task SelectCipherAsync(CipherView cipher)
|
||||||
{
|
{
|
||||||
var page = new ViewPage(cipher.Id);
|
var page = new ViewPage(cipher.Id);
|
||||||
@@ -400,26 +368,25 @@ namespace Bit.App.Pages
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var page = new GroupingsPage(false, type, null, null, title, _vaultFilterSelection);
|
var page = new GroupingsPage(false, type, null, null, title);
|
||||||
await Page.Navigation.PushAsync(page);
|
await Page.Navigation.PushAsync(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectFolderAsync(FolderView folder)
|
public async Task SelectFolderAsync(FolderView folder)
|
||||||
{
|
{
|
||||||
var page = new GroupingsPage(false, null, folder.Id ?? "none", null, folder.Name, _vaultFilterSelection);
|
var page = new GroupingsPage(false, null, folder.Id ?? "none", null, folder.Name);
|
||||||
await Page.Navigation.PushAsync(page);
|
await Page.Navigation.PushAsync(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectCollectionAsync(Core.Models.View.CollectionView collection)
|
public async Task SelectCollectionAsync(Core.Models.View.CollectionView collection)
|
||||||
{
|
{
|
||||||
var page = new GroupingsPage(false, null, null, collection.Id, collection.Name, _vaultFilterSelection);
|
var page = new GroupingsPage(false, null, null, collection.Id, collection.Name);
|
||||||
await Page.Navigation.PushAsync(page);
|
await Page.Navigation.PushAsync(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectTrashAsync()
|
public async Task SelectTrashAsync()
|
||||||
{
|
{
|
||||||
var page = new GroupingsPage(false, null, null, null, AppResources.Trash, _vaultFilterSelection, null,
|
var page = new GroupingsPage(false, null, null, null, AppResources.Trash, null, true);
|
||||||
true);
|
|
||||||
await Page.Navigation.PushAsync(page);
|
await Page.Navigation.PushAsync(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +425,7 @@ namespace Bit.App.Pages
|
|||||||
private async Task LoadDataAsync()
|
private async Task LoadDataAsync()
|
||||||
{
|
{
|
||||||
NoDataText = AppResources.NoItems;
|
NoDataText = AppResources.NoItems;
|
||||||
_allCiphers = await GetAllCiphersAsync();
|
_allCiphers = await _cipherService.GetAllDecryptedAsync();
|
||||||
HasCiphers = _allCiphers.Any();
|
HasCiphers = _allCiphers.Any();
|
||||||
FavoriteCiphers?.Clear();
|
FavoriteCiphers?.Clear();
|
||||||
NoFolderCiphers?.Clear();
|
NoFolderCiphers?.Clear();
|
||||||
@@ -472,11 +439,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if (MainPage)
|
if (MainPage)
|
||||||
{
|
{
|
||||||
await FillFoldersAndCollectionsAsync();
|
Folders = await _folderService.GetAllDecryptedAsync();
|
||||||
NestedFolders = await _folderService.GetAllNestedAsync(Folders);
|
NestedFolders = await _folderService.GetAllNestedAsync();
|
||||||
HasFolders = NestedFolders.Any(f => f.Node?.Id != null);
|
HasFolders = NestedFolders.Any(f => f.Node?.Id != null);
|
||||||
NestedCollections = Collections != null ? await _collectionService.GetAllNestedAsync(Collections) : null;
|
Collections = await _collectionService.GetAllDecryptedAsync();
|
||||||
HasCollections = NestedCollections?.Any() ?? false;
|
NestedCollections = await _collectionService.GetAllNestedAsync(Collections);
|
||||||
|
HasCollections = NestedCollections.Any();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -596,34 +564,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task FillFoldersAndCollectionsAsync()
|
|
||||||
{
|
|
||||||
var orgId = GetVaultFilterOrgId();
|
|
||||||
var decFolders = await _folderService.GetAllDecryptedAsync();
|
|
||||||
var decCollections = await _collectionService.GetAllDecryptedAsync();
|
|
||||||
if (IsVaultFilterMyVault)
|
|
||||||
{
|
|
||||||
Folders = BuildFolders(decFolders);
|
|
||||||
Collections = null;
|
|
||||||
}
|
|
||||||
else if (IsVaultFilterOrgVault && !string.IsNullOrWhiteSpace(orgId))
|
|
||||||
{
|
|
||||||
Folders = BuildFolders(decFolders);
|
|
||||||
Collections = decCollections?.Where(c => c.OrganizationId == orgId).ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Folders = decFolders;
|
|
||||||
Collections = decCollections;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<FolderView> BuildFolders(List<FolderView> decFolders)
|
|
||||||
{
|
|
||||||
var folders = decFolders.Where(f => _allCiphers.Any(c => c.FolderId == f.Id)).ToList();
|
|
||||||
return folders.Any() ? folders : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void CipherOptionsAsync(CipherView cipher)
|
private async void CipherOptionsAsync(CipherView cipher)
|
||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
|
|||||||
@@ -39,8 +39,7 @@
|
|||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||||
ItemsSource="{Binding History}"
|
ItemsSource="{Binding History}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
StyleClass="list, list-platform"
|
StyleClass="list, list-platform">
|
||||||
ExtraDataForLogging="Password History Page">
|
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:PasswordHistoryView">
|
<DataTemplate x:DataType="views:PasswordHistoryView">
|
||||||
<Grid
|
<Grid
|
||||||
|
|||||||
@@ -41,17 +41,15 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
var cipher = await _cipherService.GetAsync(CipherId);
|
var cipher = await _cipherService.GetAsync(CipherId);
|
||||||
var decCipher = await cipher.DecryptAsync();
|
var decCipher = await cipher.DecryptAsync();
|
||||||
Device.BeginInvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
History.ResetWithRange(decCipher.PasswordHistory ?? new List<PasswordHistoryView>());
|
History.ResetWithRange(decCipher.PasswordHistory ?? new List<PasswordHistoryView>());
|
||||||
ShowNoData = History.Count == 0;
|
ShowNoData = History.Count == 0;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void CopyAsync(PasswordHistoryView ph)
|
private async void CopyAsync(PasswordHistoryView ph)
|
||||||
{
|
{
|
||||||
await _clipboardService.CopyTextAsync(ph.Password);
|
await _clipboardService.CopyTextAsync(ph.Password);
|
||||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
_platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,6 @@ namespace Bit.App.Pages
|
|||||||
_autofocusCts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
|
_autofocusCts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
|
||||||
|
|
||||||
var autofocusCts = _autofocusCts;
|
var autofocusCts = _autofocusCts;
|
||||||
// this task is needed to be awaited OnDisappearing to avoid some crashes
|
|
||||||
// when changing the value of _zxing.IsScanning
|
|
||||||
_continuousAutofocusTask = Task.Run(async () =>
|
_continuousAutofocusTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -54,7 +52,7 @@ namespace Bit.App.Pages
|
|||||||
while (!autofocusCts.IsCancellationRequested)
|
while (!autofocusCts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromSeconds(2), autofocusCts.Token);
|
await Task.Delay(TimeSpan.FromSeconds(2), autofocusCts.Token);
|
||||||
await Device.InvokeOnMainThreadAsync(() =>
|
Device.BeginInvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
if (!autofocusCts.IsCancellationRequested)
|
if (!autofocusCts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -75,10 +73,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_autofocusCts?.Cancel();
|
_autofocusCts?.Cancel();
|
||||||
|
|
||||||
if (_continuousAutofocusTask != null)
|
|
||||||
{
|
|
||||||
await _continuousAutofocusTask;
|
await _continuousAutofocusTask;
|
||||||
}
|
|
||||||
_zxing.IsScanning = false;
|
_zxing.IsScanning = false;
|
||||||
|
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
|
|||||||
@@ -145,7 +145,6 @@
|
|||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
|
||||||
IsVisible="{Binding Cipher.ViewPassword}" />
|
IsVisible="{Binding Cipher.ViewPassword}" />
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
|||||||
@@ -120,8 +120,7 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon),
|
nameof(ShowPasswordIcon)
|
||||||
nameof(PasswordVisibilityAccessibilityText)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public bool ShowCardNumber
|
public bool ShowCardNumber
|
||||||
@@ -214,7 +213,6 @@ namespace Bit.App.Pages
|
|||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string ShowCardNumberIcon => ShowCardNumber ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowCardNumberIcon => ShowCardNumber ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string ShowCardCodeIcon => ShowCardCode ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowCardCodeIcon => ShowCardCode ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
|
||||||
public string TotpCodeFormatted
|
public string TotpCodeFormatted
|
||||||
{
|
{
|
||||||
get => _totpCodeFormatted;
|
get => _totpCodeFormatted;
|
||||||
@@ -663,7 +661,7 @@ namespace Bit.App.Pages
|
|||||||
await _clipboardService.CopyTextAsync(text);
|
await _clipboardService.CopyTextAsync(text);
|
||||||
if (!string.IsNullOrWhiteSpace(name))
|
if (!string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
_platformUtilsService.ShowToastForCopiedValue(name);
|
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
|
||||||
}
|
}
|
||||||
if (id == "LoginPassword")
|
if (id == "LoginPassword")
|
||||||
{
|
{
|
||||||
@@ -756,12 +754,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (IsBooleanType)
|
if (IsBooleanType)
|
||||||
{
|
{
|
||||||
return _field.Value == "true" ? BitwardenIcons.Square : BitwardenIcons.CheckSquare;
|
return _field.Value == "true" ? "" : "";
|
||||||
}
|
}
|
||||||
else if (IsLinkedType)
|
else if (IsLinkedType)
|
||||||
{
|
{
|
||||||
var i18nKey = _cipher.LinkedFieldI18nKey(Field.LinkedId.GetValueOrDefault());
|
var i18nKey = _cipher.LinkedFieldI18nKey(Field.LinkedId.GetValueOrDefault());
|
||||||
return BitwardenIcons.Link + _i18nService.T(i18nKey);
|
return " " + _i18nService.T(i18nKey);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.Domain;
|
|
||||||
using Bit.Core.Models.View;
|
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
|
||||||
{
|
|
||||||
public abstract class VaultFilterViewModel : BaseViewModel
|
|
||||||
{
|
|
||||||
protected abstract ICipherService cipherService { get; }
|
|
||||||
protected abstract IPolicyService policyService { get; }
|
|
||||||
protected abstract IOrganizationService organizationService { get; }
|
|
||||||
protected abstract ILogger logger { get; }
|
|
||||||
|
|
||||||
protected bool _showVaultFilter;
|
|
||||||
protected bool _personalOwnershipPolicyApplies;
|
|
||||||
protected string _vaultFilterSelection;
|
|
||||||
protected List<Organization> _organizations;
|
|
||||||
|
|
||||||
public VaultFilterViewModel()
|
|
||||||
{
|
|
||||||
VaultFilterCommand = new AsyncCommand(VaultFilterOptionsAsync,
|
|
||||||
onException: ex => logger.Exception(ex),
|
|
||||||
allowsMultipleExecutions: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICommand VaultFilterCommand { get; set; }
|
|
||||||
|
|
||||||
public bool ShowVaultFilter
|
|
||||||
{
|
|
||||||
get => _showVaultFilter;
|
|
||||||
set => SetProperty(ref _showVaultFilter, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string VaultFilterDescription
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_vaultFilterSelection == null || _vaultFilterSelection == AppResources.AllVaults)
|
|
||||||
{
|
|
||||||
return string.Format(AppResources.VaultFilterDescription, AppResources.All);
|
|
||||||
}
|
|
||||||
return string.Format(AppResources.VaultFilterDescription, _vaultFilterSelection);
|
|
||||||
}
|
|
||||||
set => SetProperty(ref _vaultFilterSelection, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetVaultFilterOrgId()
|
|
||||||
{
|
|
||||||
return _organizations?.FirstOrDefault(o => o.Name == _vaultFilterSelection)?.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool IsVaultFilterMyVault => _vaultFilterSelection == AppResources.MyVault;
|
|
||||||
|
|
||||||
protected bool IsVaultFilterOrgVault => _vaultFilterSelection != AppResources.AllVaults &&
|
|
||||||
_vaultFilterSelection != AppResources.MyVault;
|
|
||||||
|
|
||||||
protected async Task InitVaultFilterAsync(bool shouldUpdateShowVaultFilter)
|
|
||||||
{
|
|
||||||
_organizations = await organizationService.GetAllAsync();
|
|
||||||
if (_organizations?.Any() ?? false)
|
|
||||||
{
|
|
||||||
_personalOwnershipPolicyApplies = await policyService.PolicyAppliesToUser(PolicyType.PersonalOwnership);
|
|
||||||
var singleOrgPolicyApplies = await policyService.PolicyAppliesToUser(PolicyType.OnlyOrg);
|
|
||||||
if (_vaultFilterSelection == null || (_personalOwnershipPolicyApplies && singleOrgPolicyApplies))
|
|
||||||
{
|
|
||||||
VaultFilterDescription = AppResources.AllVaults;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shouldUpdateShowVaultFilter)
|
|
||||||
{
|
|
||||||
await Task.Delay(100);
|
|
||||||
ShowVaultFilter = await policyService.ShouldShowVaultFilterAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task<List<CipherView>> GetAllCiphersAsync()
|
|
||||||
{
|
|
||||||
var decCiphers = await cipherService.GetAllDecryptedAsync();
|
|
||||||
if (IsVaultFilterMyVault)
|
|
||||||
{
|
|
||||||
return decCiphers.Where(c => c.OrganizationId == null).ToList();
|
|
||||||
}
|
|
||||||
if (IsVaultFilterOrgVault)
|
|
||||||
{
|
|
||||||
var orgId = GetVaultFilterOrgId();
|
|
||||||
return decCiphers.Where(c => c.OrganizationId == orgId).ToList();
|
|
||||||
}
|
|
||||||
return decCiphers;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task VaultFilterOptionsAsync()
|
|
||||||
{
|
|
||||||
var options = new List<string> { AppResources.AllVaults };
|
|
||||||
if (!_personalOwnershipPolicyApplies)
|
|
||||||
{
|
|
||||||
options.Add(AppResources.MyVault);
|
|
||||||
}
|
|
||||||
if (_organizations.Any())
|
|
||||||
{
|
|
||||||
options.AddRange(_organizations.OrderBy(o => o.Name).Select(o => o.Name));
|
|
||||||
}
|
|
||||||
var selection = await Page.DisplayActionSheet(AppResources.FilterByVault, AppResources.Cancel, null,
|
|
||||||
options.ToArray());
|
|
||||||
if (selection == null || selection == AppResources.Cancel ||
|
|
||||||
(_vaultFilterSelection == null && selection == AppResources.AllVaults) ||
|
|
||||||
(_vaultFilterSelection != null && _vaultFilterSelection == selection))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VaultFilterDescription = selection;
|
|
||||||
await OnVaultFilterSelectedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Task OnVaultFilterSelectedAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9009
src/App/Resources/AppResources.Designer.cs
generated
9009
src/App/Resources/AppResources.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@@ -1861,9 +1861,6 @@
|
|||||||
<value>A friendly name to describe this Send.</value>
|
<value>A friendly name to describe this Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Text" xml:space="preserve">
|
|
||||||
<value>Text</value>
|
|
||||||
</data>
|
|
||||||
<data name="TypeText" xml:space="preserve">
|
<data name="TypeText" xml:space="preserve">
|
||||||
<value>Text</value>
|
<value>Text</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1880,18 +1877,6 @@
|
|||||||
<data name="TypeFileInfo" xml:space="preserve">
|
<data name="TypeFileInfo" xml:space="preserve">
|
||||||
<value>The file you want to send.</value>
|
<value>The file you want to send.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FileTypeIsSelected" xml:space="preserve">
|
|
||||||
<value>File type is selected.</value>
|
|
||||||
</data>
|
|
||||||
<data name="FileTypeIsNotSelected" xml:space="preserve">
|
|
||||||
<value>File type is not selected, tap to select.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TextTypeIsSelected" xml:space="preserve">
|
|
||||||
<value>Text type is selected.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TextTypeIsNotSelected" xml:space="preserve">
|
|
||||||
<value>Text type is not selected, tap to select.</value>
|
|
||||||
</data>
|
|
||||||
<data name="DeletionDate" xml:space="preserve">
|
<data name="DeletionDate" xml:space="preserve">
|
||||||
<value>Deletion Date</value>
|
<value>Deletion Date</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2196,52 +2181,4 @@
|
|||||||
<data name="EnterTheVerificationCodeThatWasSentToYourEmail" xml:space="preserve">
|
<data name="EnterTheVerificationCodeThatWasSentToYourEmail" xml:space="preserve">
|
||||||
<value>Enter the verification code that was sent to your email</value>
|
<value>Enter the verification code that was sent to your email</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SubmitCrashLogs" xml:space="preserve">
|
|
||||||
<value>Submit crash logs</value>
|
|
||||||
</data>
|
|
||||||
<data name="SubmitCrashLogsDescription" xml:space="preserve">
|
|
||||||
<value>Help Bitwarden improve app stability by submitting crash reports.</value>
|
|
||||||
</data>
|
|
||||||
<data name="OptionsExpanded" xml:space="preserve">
|
|
||||||
<value>Options are expanded, tap to collapse.</value>
|
|
||||||
</data>
|
|
||||||
<data name="OptionsCollapsed" xml:space="preserve">
|
|
||||||
<value>Options are collapsed, tap to expand.</value>
|
|
||||||
</data>
|
|
||||||
<data name="UppercaseAtoZ" xml:space="preserve">
|
|
||||||
<value>Uppercase (A to Z)</value>
|
|
||||||
</data>
|
|
||||||
<data name="LowercaseAtoZ" xml:space="preserve">
|
|
||||||
<value>Lowercase (A to Z)</value>
|
|
||||||
</data>
|
|
||||||
<data name="NumbersZeroToNine" xml:space="preserve">
|
|
||||||
<value>Numbers (0 to 9)</value>
|
|
||||||
</data>
|
|
||||||
<data name="SpecialCharacters" xml:space="preserve">
|
|
||||||
<value>Special Characters (!@#$%^&*)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TapToGoBack" xml:space="preserve">
|
|
||||||
<value>Tap to go back</value>
|
|
||||||
</data>
|
|
||||||
<data name="PasswordIsVisibleTapToHide" xml:space="preserve">
|
|
||||||
<value>Password is visible, tap to hide.</value>
|
|
||||||
</data>
|
|
||||||
<data name="PasswordIsNotVisibleTapToShow" xml:space="preserve">
|
|
||||||
<value>Password is not visible, tap to show.</value>
|
|
||||||
</data>
|
|
||||||
<data name="FilterByVault" xml:space="preserve">
|
|
||||||
<value>Filter items by vault</value>
|
|
||||||
</data>
|
|
||||||
<data name="AllVaults" xml:space="preserve">
|
|
||||||
<value>All Vaults</value>
|
|
||||||
</data>
|
|
||||||
<data name="Vaults" xml:space="preserve">
|
|
||||||
<value>Vaults</value>
|
|
||||||
</data>
|
|
||||||
<data name="VaultFilterDescription" xml:space="preserve">
|
|
||||||
<value>Vault: {0}</value>
|
|
||||||
</data>
|
|
||||||
<data name="All" xml:space="preserve">
|
|
||||||
<value>All</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ namespace Bit.App.Services
|
|||||||
private const int DialogPromiseExpiration = 600000; // 10 minutes
|
private const int DialogPromiseExpiration = 600000; // 10 minutes
|
||||||
|
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IClipboardService _clipboardService;
|
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
|
|
||||||
@@ -29,12 +28,10 @@ namespace Bit.App.Services
|
|||||||
|
|
||||||
public MobilePlatformUtilsService(
|
public MobilePlatformUtilsService(
|
||||||
IDeviceActionService deviceActionService,
|
IDeviceActionService deviceActionService,
|
||||||
IClipboardService clipboardService,
|
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IBroadcasterService broadcasterService)
|
IBroadcasterService broadcasterService)
|
||||||
{
|
{
|
||||||
_deviceActionService = deviceActionService;
|
_deviceActionService = deviceActionService;
|
||||||
_clipboardService = clipboardService;
|
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_broadcasterService = broadcasterService;
|
_broadcasterService = broadcasterService;
|
||||||
}
|
}
|
||||||
@@ -132,15 +129,6 @@ namespace Bit.App.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowToastForCopiedValue(string valueNameCopied)
|
|
||||||
{
|
|
||||||
if (!_clipboardService.IsCopyNotificationHandledByPlatform())
|
|
||||||
{
|
|
||||||
ShowToast("info", null,
|
|
||||||
string.Format(AppResources.ValueHasBeenCopied, valueNameCopied));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SupportsFido2()
|
public bool SupportsFido2()
|
||||||
{
|
{
|
||||||
return _deviceActionService.SupportsFido2();
|
return _deviceActionService.SupportsFido2();
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ namespace Bit.App.Services
|
|||||||
Constants.LastBuildKey,
|
Constants.LastBuildKey,
|
||||||
Constants.ClearCiphersCacheKey,
|
Constants.ClearCiphersCacheKey,
|
||||||
Constants.BiometricIntegrityKey,
|
Constants.BiometricIntegrityKey,
|
||||||
Constants.iOSExtensionActiveUserIdKey,
|
|
||||||
Constants.iOSAutoFillClearCiphersCacheKey,
|
Constants.iOSAutoFillClearCiphersCacheKey,
|
||||||
Constants.iOSAutoFillBiometricIntegrityKey,
|
Constants.iOSAutoFillBiometricIntegrityKey,
|
||||||
Constants.iOSExtensionClearCiphersCacheKey,
|
Constants.iOSExtensionClearCiphersCacheKey,
|
||||||
@@ -33,7 +32,7 @@ namespace Bit.App.Services
|
|||||||
Constants.iOSShareExtensionClearCiphersCacheKey,
|
Constants.iOSShareExtensionClearCiphersCacheKey,
|
||||||
Constants.iOSShareExtensionBiometricIntegrityKey,
|
Constants.iOSShareExtensionBiometricIntegrityKey,
|
||||||
Constants.RememberedEmailKey,
|
Constants.RememberedEmailKey,
|
||||||
Constants.RememberedOrgIdentifierKey
|
Constants.RememberedOrgIdentifierKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
public MobileStorageService(
|
public MobileStorageService(
|
||||||
|
|||||||
@@ -268,16 +268,6 @@
|
|||||||
<Setter Property="TextColor"
|
<Setter Property="TextColor"
|
||||||
Value="{DynamicResource ButtonColor}" />
|
Value="{DynamicResource ButtonColor}" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style TargetType="Button"
|
|
||||||
ApplyToDerivedTypes="True"
|
|
||||||
Class="list-row-button-text">
|
|
||||||
<Setter Property="BackgroundColor"
|
|
||||||
Value="Transparent" />
|
|
||||||
<Setter Property="Padding"
|
|
||||||
Value="0" />
|
|
||||||
<Setter Property="TextColor"
|
|
||||||
Value="{DynamicResource ButtonTextColor}" />
|
|
||||||
</Style>
|
|
||||||
<Style TargetType="Button"
|
<Style TargetType="Button"
|
||||||
ApplyToDerivedTypes="True"
|
ApplyToDerivedTypes="True"
|
||||||
Class="segmented-button">
|
Class="segmented-button">
|
||||||
|
|||||||
@@ -1,217 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Bit.App.Models;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.Domain;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Utilities.AccountManagement
|
|
||||||
{
|
|
||||||
public class AccountsManager : IAccountsManager
|
|
||||||
{
|
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
|
||||||
private readonly IStorageService _secureStorageService;
|
|
||||||
private readonly IStateService _stateService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
|
||||||
private readonly IAuthService _authService;
|
|
||||||
|
|
||||||
Func<AppOptions> _getOptionsFunc;
|
|
||||||
private IAccountsManagerHost _accountsManagerHost;
|
|
||||||
|
|
||||||
public AccountsManager(IBroadcasterService broadcasterService,
|
|
||||||
IVaultTimeoutService vaultTimeoutService,
|
|
||||||
IStorageService secureStorageService,
|
|
||||||
IStateService stateService,
|
|
||||||
IPlatformUtilsService platformUtilsService,
|
|
||||||
IAuthService authService)
|
|
||||||
{
|
|
||||||
_broadcasterService = broadcasterService;
|
|
||||||
_vaultTimeoutService = vaultTimeoutService;
|
|
||||||
_secureStorageService = secureStorageService;
|
|
||||||
_stateService = stateService;
|
|
||||||
_platformUtilsService = platformUtilsService;
|
|
||||||
_authService = authService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AppOptions Options => _getOptionsFunc?.Invoke() ?? new AppOptions { IosExtension = true };
|
|
||||||
|
|
||||||
public void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost)
|
|
||||||
{
|
|
||||||
_getOptionsFunc = getOptionsFunc;
|
|
||||||
_accountsManagerHost = accountsManagerHost;
|
|
||||||
|
|
||||||
_broadcasterService.Subscribe(nameof(AccountsManager), OnMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task NavigateOnAccountChangeAsync(bool? isAuthed = null)
|
|
||||||
{
|
|
||||||
// TODO: this could be improved by doing chain of responsability pattern
|
|
||||||
// but for now it may be an overkill, if logic gets more complex consider refactoring it
|
|
||||||
|
|
||||||
var authed = isAuthed ?? await _stateService.IsAuthenticatedAsync();
|
|
||||||
if (authed)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.Login, new LoginNavigationParams(email));
|
|
||||||
}
|
|
||||||
else if (await _vaultTimeoutService.IsLockedAsync() ||
|
|
||||||
await _vaultTimeoutService.ShouldLockAsync())
|
|
||||||
{
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.Lock);
|
|
||||||
}
|
|
||||||
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
|
||||||
{
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.AddEditCipher);
|
|
||||||
}
|
|
||||||
else if (Options.Uri != null)
|
|
||||||
{
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.AutofillCiphers);
|
|
||||||
}
|
|
||||||
else if (Options.CreateSend != null)
|
|
||||||
{
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.SendAddEdit);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.Home);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.Login, new LoginNavigationParams(email));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void OnMessage(Message message)
|
|
||||||
{
|
|
||||||
switch (message.Command)
|
|
||||||
{
|
|
||||||
case AccountsManagerMessageCommands.LOCKED:
|
|
||||||
Locked(message.Data as Tuple<string, bool>);
|
|
||||||
break;
|
|
||||||
case AccountsManagerMessageCommands.LOCK_VAULT:
|
|
||||||
await _vaultTimeoutService.LockAsync(true);
|
|
||||||
break;
|
|
||||||
case AccountsManagerMessageCommands.LOGOUT:
|
|
||||||
LogOut(message.Data as Tuple<string, bool, bool>);
|
|
||||||
break;
|
|
||||||
case AccountsManagerMessageCommands.LOGGED_OUT:
|
|
||||||
// Clean up old migrated key if they ever log out.
|
|
||||||
await _secureStorageService.RemoveAsync("oldKey");
|
|
||||||
break;
|
|
||||||
case AccountsManagerMessageCommands.ADD_ACCOUNT:
|
|
||||||
AddAccount();
|
|
||||||
break;
|
|
||||||
case AccountsManagerMessageCommands.ACCOUNT_ADDED:
|
|
||||||
await _accountsManagerHost.UpdateThemeAsync();
|
|
||||||
break;
|
|
||||||
case AccountsManagerMessageCommands.SWITCHED_ACCOUNT:
|
|
||||||
await SwitchedAccountAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Locked(Tuple<string, bool> extras)
|
|
||||||
{
|
|
||||||
var userId = extras?.Item1;
|
|
||||||
var userInitiated = extras?.Item2 ?? false;
|
|
||||||
Device.BeginInvokeOnMainThread(async () => await LockedAsync(userId, userInitiated));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LockedAsync(string userId, bool userInitiated)
|
|
||||||
{
|
|
||||||
if (!await _stateService.IsActiveAccountAsync(userId))
|
|
||||||
{
|
|
||||||
_platformUtilsService.ShowToast("info", null, AppResources.AccountLockedSuccessfully);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var autoPromptBiometric = !userInitiated;
|
|
||||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
|
||||||
if (vaultTimeout == 0)
|
|
||||||
{
|
|
||||||
autoPromptBiometric = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _accountsManagerHost.SetPreviousPageInfoAsync();
|
|
||||||
|
|
||||||
Device.BeginInvokeOnMainThread(() => _accountsManagerHost.Navigate(NavigationTarget.Lock, new LockNavigationParams(autoPromptBiometric)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddAccount()
|
|
||||||
{
|
|
||||||
Device.BeginInvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
Options.HideAccountSwitcher = false;
|
|
||||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogOut(Tuple<string, bool, bool> extras)
|
|
||||||
{
|
|
||||||
var userId = extras?.Item1;
|
|
||||||
var userInitiated = extras?.Item2 ?? true;
|
|
||||||
var expired = extras?.Item3 ?? false;
|
|
||||||
Device.BeginInvokeOnMainThread(async () => await LogOutAsync(userId, userInitiated, expired));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LogOutAsync(string userId, bool userInitiated, bool expired)
|
|
||||||
{
|
|
||||||
await AppHelpers.LogOutAsync(userId, userInitiated);
|
|
||||||
await NavigateOnAccountChangeAsync();
|
|
||||||
_authService.LogOut(() =>
|
|
||||||
{
|
|
||||||
if (expired)
|
|
||||||
{
|
|
||||||
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SwitchedAccountAsync()
|
|
||||||
{
|
|
||||||
await AppHelpers.OnAccountSwitchAsync();
|
|
||||||
await Device.InvokeOnMainThreadAsync(async () =>
|
|
||||||
{
|
|
||||||
if (await _vaultTimeoutService.ShouldTimeoutAsync())
|
|
||||||
{
|
|
||||||
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await NavigateOnAccountChangeAsync();
|
|
||||||
}
|
|
||||||
await Task.Delay(50);
|
|
||||||
await _accountsManagerHost.UpdateThemeAsync();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using Bit.App.Abstractions;
|
|
||||||
|
|
||||||
namespace Bit.App.Utilities.AccountManagement
|
|
||||||
{
|
|
||||||
public class LockNavigationParams : INavigationParams
|
|
||||||
{
|
|
||||||
public LockNavigationParams(bool autoPromptBiometric = true)
|
|
||||||
{
|
|
||||||
AutoPromptBiometric = autoPromptBiometric;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AutoPromptBiometric { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using Bit.App.Abstractions;
|
|
||||||
|
|
||||||
namespace Bit.App.Utilities.AccountManagement
|
|
||||||
{
|
|
||||||
public class LoginNavigationParams : INavigationParams
|
|
||||||
{
|
|
||||||
public LoginNavigationParams(string email)
|
|
||||||
{
|
|
||||||
Email = email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Email { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -97,14 +97,16 @@ namespace Bit.App.Utilities
|
|||||||
else if (selection == AppResources.CopyUsername)
|
else if (selection == AppResources.CopyUsername)
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Login.Username);
|
await clipboardService.CopyTextAsync(cipher.Login.Username);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Username);
|
platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Username));
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyPassword)
|
else if (selection == AppResources.CopyPassword)
|
||||||
{
|
{
|
||||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Login.Password);
|
await clipboardService.CopyTextAsync(cipher.Login.Password);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id);
|
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,7 +119,8 @@ namespace Bit.App.Utilities
|
|||||||
if (!string.IsNullOrWhiteSpace(totp))
|
if (!string.IsNullOrWhiteSpace(totp))
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(totp);
|
await clipboardService.CopyTextAsync(totp);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp);
|
platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.VerificationCodeTotp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +133,8 @@ namespace Bit.App.Utilities
|
|||||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Card.Number);
|
await clipboardService.CopyTextAsync(cipher.Card.Number);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Number);
|
platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Number));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopySecurityCode)
|
else if (selection == AppResources.CopySecurityCode)
|
||||||
@@ -138,14 +142,16 @@ namespace Bit.App.Utilities
|
|||||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Card.Code);
|
await clipboardService.CopyTextAsync(cipher.Card.Code);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.SecurityCode);
|
platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.SecurityCode));
|
||||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id);
|
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyNotes)
|
else if (selection == AppResources.CopyNotes)
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Notes);
|
await clipboardService.CopyTextAsync(cipher.Notes);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Notes);
|
platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Notes));
|
||||||
}
|
}
|
||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
@@ -256,7 +262,8 @@ namespace Bit.App.Utilities
|
|||||||
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
var clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
var clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
await clipboardService.CopyTextAsync(GetSendUrl(send));
|
await clipboardService.CopyTextAsync(GetSendUrl(send));
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.SendLink);
|
platformUtilsService.ShowToast("info", null,
|
||||||
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.SendLink));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ShareSendUrlAsync(SendView send)
|
public static async Task ShareSendUrlAsync(SendView send)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task PutDeleteCipherAsync(string id);
|
Task PutDeleteCipherAsync(string id);
|
||||||
Task<CipherResponse> PutRestoreCipherAsync(string id);
|
Task<CipherResponse> PutRestoreCipherAsync(string id);
|
||||||
Task RefreshIdentityTokenAsync();
|
Task RefreshIdentityTokenAsync();
|
||||||
Task<SsoPrevalidateResponse> PreValidateSso(string identifier);
|
Task<object> PreValidateSso(string identifier);
|
||||||
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
||||||
TRequest body, bool authed, bool hasResponse, bool logoutOnUnauthorized = true);
|
TRequest body, bool authed, bool hasResponse, bool logoutOnUnauthorized = true);
|
||||||
void SetUrls(EnvironmentUrls urls);
|
void SetUrls(EnvironmentUrls urls);
|
||||||
|
|||||||
@@ -8,17 +8,9 @@ namespace Bit.Core.Abstractions
|
|||||||
/// Copies the <paramref name="text"/> to the Clipboard.
|
/// Copies the <paramref name="text"/> to the Clipboard.
|
||||||
/// If <paramref name="expiresInMs"/> is set > 0 then the Clipboard will be cleared after this time in milliseconds.
|
/// If <paramref name="expiresInMs"/> is set > 0 then the Clipboard will be cleared after this time in milliseconds.
|
||||||
/// if less than 0 then it takes the configuration that the user set in Options.
|
/// if less than 0 then it takes the configuration that the user set in Options.
|
||||||
/// If <paramref name="isSensitive"/> is true the sensitive flag is passed to the clipdata to obfuscate the
|
|
||||||
/// clipboard text in the popup (Android 13+ only)
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">Text to be copied to the Clipboard</param>
|
/// <param name="text">Text to be copied to the Clipboard</param>
|
||||||
/// <param name="expiresInMs">Expiration time in milliseconds of the copied text</param>
|
/// <param name="expiresInMs">Expiration time in milliseconds of the copied text</param>
|
||||||
/// <param name="isSensitive">Flag to mark copied text as sensitive</param>
|
Task CopyTextAsync(string text, int expiresInMs = -1);
|
||||||
Task CopyTextAsync(string text, int expiresInMs = -1, bool isSensitive = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the platform provides its own notification when text is copied to the clipboard
|
|
||||||
/// </summary>
|
|
||||||
bool IsCopyNotificationHandledByPlatform();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<Folder> EncryptAsync(FolderView model, SymmetricCryptoKey key = null);
|
Task<Folder> EncryptAsync(FolderView model, SymmetricCryptoKey key = null);
|
||||||
Task<List<Folder>> GetAllAsync();
|
Task<List<Folder>> GetAllAsync();
|
||||||
Task<List<FolderView>> GetAllDecryptedAsync();
|
Task<List<FolderView>> GetAllDecryptedAsync();
|
||||||
Task<List<TreeNode<FolderView>>> GetAllNestedAsync(List<FolderView> folders = null);
|
Task<List<TreeNode<FolderView>>> GetAllNestedAsync();
|
||||||
Task<Folder> GetAsync(string id);
|
Task<Folder> GetAsync(string id);
|
||||||
Task<TreeNode<FolderView>> GetNestedAsync(string id);
|
Task<TreeNode<FolderView>> GetNestedAsync(string id);
|
||||||
Task ReplaceAsync(Dictionary<string, FolderData> folders);
|
Task ReplaceAsync(Dictionary<string, FolderData> folders);
|
||||||
|
|||||||
@@ -1,29 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Bit.Core.Abstractions
|
namespace Bit.Core.Abstractions
|
||||||
{
|
{
|
||||||
public interface ILogger
|
public interface ILogger
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Place necessary code to initiaze logger
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task InitAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns if the current logger is enable or disable.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<bool> IsEnabled();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the state of the current logger. Setting state enabled to false will block logging.
|
|
||||||
/// </summary>
|
|
||||||
Task SetEnabled(bool value);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs something that is not in itself an exception, e.g. a wrong flow or value that needs to be reported
|
/// Logs something that is not in itself an exception, e.g. a wrong flow or value that needs to be reported
|
||||||
/// and looked into.
|
/// and looked into.
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<(string password, bool valid)> ShowPasswordDialogAndGetItAsync(string title, string body, Func<string, Task<bool>> validator);
|
Task<(string password, bool valid)> ShowPasswordDialogAndGetItAsync(string title, string body, Func<string, Task<bool>> validator);
|
||||||
void ShowToast(string type, string title, string text, Dictionary<string, object> options = null);
|
void ShowToast(string type, string title, string text, Dictionary<string, object> options = null);
|
||||||
void ShowToast(string type, string title, string[] text, Dictionary<string, object> options = null);
|
void ShowToast(string type, string title, string[] text, Dictionary<string, object> options = null);
|
||||||
void ShowToastForCopiedValue(string valueNameCopied);
|
|
||||||
bool SupportsFido2();
|
bool SupportsFido2();
|
||||||
bool SupportsDuo();
|
bool SupportsDuo();
|
||||||
Task<bool> SupportsBiometricAsync();
|
Task<bool> SupportsBiometricAsync();
|
||||||
|
|||||||
@@ -20,6 +20,5 @@ namespace Bit.Core.Abstractions
|
|||||||
string orgId);
|
string orgId);
|
||||||
Task<bool> PolicyAppliesToUser(PolicyType policyType, Func<Policy, bool> policyFilter = null, string userId = null);
|
Task<bool> PolicyAppliesToUser(PolicyType policyType, Func<Policy, bool> policyFilter = null, string userId = null);
|
||||||
int? GetPolicyInt(Policy policy, string key);
|
int? GetPolicyInt(Policy policy, string key);
|
||||||
Task<bool> ShouldShowVaultFilterAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<string> GetActiveUserIdAsync();
|
Task<string> GetActiveUserIdAsync();
|
||||||
Task<bool> IsActiveAccountAsync(string userId = null);
|
Task<bool> IsActiveAccountAsync(string userId = null);
|
||||||
Task SetActiveUserAsync(string userId);
|
Task SetActiveUserAsync(string userId);
|
||||||
Task CheckExtensionActiveUserAndSwitchIfNeededAsync();
|
|
||||||
Task<bool> IsAuthenticatedAsync(string userId = null);
|
Task<bool> IsAuthenticatedAsync(string userId = null);
|
||||||
Task<string> GetUserIdAsync(string email);
|
Task<string> GetUserIdAsync(string email);
|
||||||
Task RefreshAccountViewsAsync(bool allowAddAccountRow);
|
Task RefreshAccountViewsAsync(bool allowAddAccountRow);
|
||||||
@@ -146,6 +145,5 @@ namespace Bit.Core.Abstractions
|
|||||||
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
|
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
|
||||||
Task<string> GetTwoFactorTokenAsync(string email = null);
|
Task<string> GetTwoFactorTokenAsync(string email = null);
|
||||||
Task SetTwoFactorTokenAsync(string value, string email = null);
|
Task SetTwoFactorTokenAsync(string value, string email = null);
|
||||||
Task SaveExtensionActiveUserIdToStorageAsync(string userId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,5 @@
|
|||||||
public const string EyeSlash = "\xe96d";
|
public const string EyeSlash = "\xe96d";
|
||||||
public const string File = "\xe96e";
|
public const string File = "\xe96e";
|
||||||
public const string Paste = "\xe96f";
|
public const string Paste = "\xe96f";
|
||||||
public const string ViewCellMenu = "\xe5d3";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
public static string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState";
|
public static string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState";
|
||||||
public static string iOSShareExtensionClearCiphersCacheKey = "iOSShareExtensionClearCiphersCache";
|
public static string iOSShareExtensionClearCiphersCacheKey = "iOSShareExtensionClearCiphersCache";
|
||||||
public static string iOSShareExtensionBiometricIntegrityKey = "iOSShareExtensionBiometricIntegrityState";
|
public static string iOSShareExtensionBiometricIntegrityKey = "iOSShareExtensionBiometricIntegrityState";
|
||||||
public static string iOSExtensionActiveUserIdKey = "iOSExtensionActiveUserId";
|
|
||||||
public static string EventCollectionKey = "eventCollection";
|
public static string EventCollectionKey = "eventCollection";
|
||||||
public static string RememberedEmailKey = "rememberedEmail";
|
public static string RememberedEmailKey = "rememberedEmail";
|
||||||
public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier";
|
public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier";
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace Bit.Core.Enums
|
|
||||||
{
|
|
||||||
public enum NavigationTarget
|
|
||||||
{
|
|
||||||
HomeLogin,
|
|
||||||
Login,
|
|
||||||
Lock,
|
|
||||||
Home,
|
|
||||||
AddEditCipher,
|
|
||||||
AutofillCiphers,
|
|
||||||
SendAddEdit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
if (defaultOptions)
|
if (defaultOptions)
|
||||||
{
|
{
|
||||||
Length = 14;
|
Length = 14;
|
||||||
AllowAmbiguousChar = true;
|
Ambiguous = false;
|
||||||
Number = true;
|
Number = true;
|
||||||
MinNumber = 1;
|
MinNumber = 1;
|
||||||
Uppercase = true;
|
Uppercase = true;
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int? Length { get; set; }
|
public int? Length { get; set; }
|
||||||
public bool? AllowAmbiguousChar { get; set; }
|
public bool? Ambiguous { get; set; }
|
||||||
public bool? Number { get; set; }
|
public bool? Number { get; set; }
|
||||||
public int? MinNumber { get; set; }
|
public int? MinNumber { get; set; }
|
||||||
public bool? Uppercase { get; set; }
|
public bool? Uppercase { get; set; }
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
public void Merge(PasswordGenerationOptions defaults)
|
public void Merge(PasswordGenerationOptions defaults)
|
||||||
{
|
{
|
||||||
Length = Length ?? defaults.Length;
|
Length = Length ?? defaults.Length;
|
||||||
AllowAmbiguousChar = AllowAmbiguousChar ?? defaults.AllowAmbiguousChar;
|
Ambiguous = Ambiguous ?? defaults.Ambiguous;
|
||||||
Number = Number ?? defaults.Number;
|
Number = Number ?? defaults.Number;
|
||||||
MinNumber = MinNumber ?? defaults.MinNumber;
|
MinNumber = MinNumber ?? defaults.MinNumber;
|
||||||
Uppercase = Uppercase ?? defaults.Uppercase;
|
Uppercase = Uppercase ?? defaults.Uppercase;
|
||||||
|
|||||||
@@ -4,6 +4,5 @@
|
|||||||
{
|
{
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public string MasterPasswordHash { get; set; }
|
public string MasterPasswordHash { get; set; }
|
||||||
public string DeviceIdentifier { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Bit.Core.Models.Response
|
|
||||||
{
|
|
||||||
public class SsoPrevalidateResponse
|
|
||||||
{
|
|
||||||
public string Token { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -547,7 +547,7 @@ namespace Bit.Core.Services
|
|||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SsoPrevalidateResponse> PreValidateSso(string identifier)
|
public async Task<object> PreValidateSso(string identifier)
|
||||||
{
|
{
|
||||||
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
|
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
|
||||||
using (var requestMessage = new HttpRequestMessage())
|
using (var requestMessage = new HttpRequestMessage())
|
||||||
@@ -571,8 +571,7 @@ namespace Bit.Core.Services
|
|||||||
var error = await HandleErrorAsync(response, false, true);
|
var error = await HandleErrorAsync(response, false, true);
|
||||||
throw new ApiException(error);
|
throw new ApiException(error);
|
||||||
}
|
}
|
||||||
var responseJsonString = await response.Content.ReadAsStringAsync();
|
return null;
|
||||||
return JsonConvert.DeserializeObject<SsoPrevalidateResponse>(responseJsonString);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -346,7 +346,6 @@ namespace Bit.Core.Services
|
|||||||
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
||||||
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
||||||
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
||||||
await _tokenService.ClearTwoFactorTokenAsync(email);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,12 +107,9 @@ namespace Bit.Core.Services
|
|||||||
return _decryptedFolderCache;
|
return _decryptedFolderCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<TreeNode<FolderView>>> GetAllNestedAsync(List<FolderView> folders = null)
|
public async Task<List<TreeNode<FolderView>>> GetAllNestedAsync()
|
||||||
{
|
{
|
||||||
if (folders == null)
|
var folders = await GetAllDecryptedAsync();
|
||||||
{
|
|
||||||
folders = await GetAllDecryptedAsync();
|
|
||||||
}
|
|
||||||
var nodes = new List<TreeNode<FolderView>>();
|
var nodes = new List<TreeNode<FolderView>>();
|
||||||
foreach (var f in folders)
|
foreach (var f in folders)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
@@ -46,12 +45,6 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Exception(Exception ex) => Debug.WriteLine(ex);
|
public void Exception(Exception ex) => Debug.WriteLine(ex);
|
||||||
|
|
||||||
public Task InitAsync() => Task.CompletedTask;
|
|
||||||
|
|
||||||
public Task<bool> IsEnabled() => Task.FromResult(true);
|
|
||||||
|
|
||||||
public Task SetEnabled(bool value) => Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,24 +5,13 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Microsoft.AppCenter;
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
using Microsoft.AppCenter.Crashes;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public class Logger : ILogger
|
public class Logger : ILogger
|
||||||
{
|
{
|
||||||
private const string iOSAppSecret = "51f96ae5-68ba-45f6-99a1-8ad9f63046c3";
|
|
||||||
private const string DroidAppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
|
||||||
|
|
||||||
private string _userId;
|
|
||||||
private string _appId;
|
|
||||||
private bool _isInitialised = false;
|
|
||||||
|
|
||||||
static ILogger _instance;
|
static ILogger _instance;
|
||||||
public static ILogger Instance
|
public static ILogger Instance
|
||||||
{
|
{
|
||||||
@@ -40,60 +29,6 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public string Description
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return JsonConvert.SerializeObject(new
|
|
||||||
{
|
|
||||||
AppId = _appId,
|
|
||||||
UserId = _userId
|
|
||||||
}, Formatting.Indented);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitAsync()
|
|
||||||
{
|
|
||||||
if (_isInitialised)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var device = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService").GetDevice();
|
|
||||||
_userId = await ServiceContainer.Resolve<IStateService>("stateService").GetActiveUserIdAsync();
|
|
||||||
_appId = await ServiceContainer.Resolve<IAppIdService>("appIdService").GetAppIdAsync();
|
|
||||||
|
|
||||||
switch (device)
|
|
||||||
{
|
|
||||||
case Enums.DeviceType.Android:
|
|
||||||
AppCenter.Start(DroidAppSecret, typeof(Crashes));
|
|
||||||
break;
|
|
||||||
case Enums.DeviceType.iOS:
|
|
||||||
AppCenter.Start(iOSAppSecret, typeof(Crashes));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AppCenterException("Cannot start AppCenter. Device type is not configured.");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
AppCenter.SetUserId(_userId);
|
|
||||||
|
|
||||||
Crashes.GetErrorAttachments = (ErrorReport report) =>
|
|
||||||
{
|
|
||||||
return new ErrorAttachmentLog[]
|
|
||||||
{
|
|
||||||
ErrorAttachmentLog.AttachmentWithText(Description, "crshdesc.txt"),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
_isInitialised = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsEnabled() => await AppCenter.IsEnabledAsync();
|
|
||||||
|
|
||||||
public async Task SetEnabled(bool value) => await AppCenter.SetEnabledAsync(value);
|
|
||||||
|
|
||||||
public void Error(string message,
|
public void Error(string message,
|
||||||
IDictionary<string, string> extraData = null,
|
IDictionary<string, string> extraData = null,
|
||||||
[CallerMemberName] string memberName = "",
|
[CallerMemberName] string memberName = "",
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
#if !FDROID
|
||||||
|
using Microsoft.AppCenter.Crashes;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@@ -22,9 +25,8 @@ namespace Bit.Core.Services
|
|||||||
#if !FDROID
|
#if !FDROID
|
||||||
// just in case the caller throws the exception in a moment where the logger can't be resolved
|
// just in case the caller throws the exception in a moment where the logger can't be resolved
|
||||||
// we need to track the error as well
|
// we need to track the error as well
|
||||||
Microsoft.AppCenter.Crashes.Crashes.TrackError(ex);
|
Crashes.TrackError(ex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user