mirror of
https://github.com/bitwarden/mobile
synced 2025-12-16 00:03:22 +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
|
||||
- [ ] 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)
|
||||
- [ ] This change requires a **documentation update** (notify the documentation 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
|
||||
needs: setup
|
||||
steps:
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
with:
|
||||
nuget-version: 5.9.0
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||
|
||||
@@ -214,11 +209,6 @@ jobs:
|
||||
name: F-Droid Build
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
with:
|
||||
nuget-version: 5.9.0
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||
|
||||
@@ -314,6 +304,18 @@ jobs:
|
||||
|
||||
$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 "##### Uninstall from Core.csproj"
|
||||
Write-Output "########################################"
|
||||
@@ -378,11 +380,6 @@ jobs:
|
||||
runs-on: macos-11
|
||||
needs: setup
|
||||
steps:
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
with:
|
||||
nuget-version: 5.9.0
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
nuget help | grep Version
|
||||
|
||||
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@@ -34,13 +34,29 @@ jobs:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
|
||||
- name: Check Release Version
|
||||
id: version
|
||||
uses: bitwarden/gh-actions/release-version-check@8f055ef543c7433c967a1b9b04a0f230923233bb
|
||||
with:
|
||||
release-type: ${{ github.event.inputs.release_type }}
|
||||
project-type: xamarin
|
||||
file: src/Android/Properties/AndroidManifest.xml
|
||||
- name: Retrieve Mobile release version
|
||||
id: retrieve-mobile-version
|
||||
run: |
|
||||
ver=$(sed -E -n '/^<manifest/s/^.*[ ]android:versionName="([^"]+)".*$/\1/p' \
|
||||
./src/Android/Properties/AndroidManifest.xml | tr -d '"')
|
||||
echo "::set-output name=mobile_version::${ver}"
|
||||
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
|
||||
id: branch
|
||||
@@ -67,8 +83,8 @@ jobs:
|
||||
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||
./Bitwarden iOS.zip"
|
||||
commit: ${{ github.sha }}
|
||||
tag: v${{ steps.version.outputs.version }}
|
||||
name: Version ${{ steps.version.outputs.version }}
|
||||
tag: v${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||
name: Version ${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||
body: "<insert release notes here>"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
|
||||
@@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UiTests", "src\UiTests\UiTests.csproj", "{23FB637B-1705-485F-9464-078FCAF361A8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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|iPhoneSimulator.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -464,6 +496,7 @@ Global
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {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
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
||||
<PackageReference Include="Xamarin.Essentials">
|
||||
<Version>1.7.3</Version>
|
||||
<Version>1.7.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||
<Version>122.0.0</Version>
|
||||
@@ -145,12 +145,12 @@
|
||||
<Compile Include="Tiles\GeneratorTileService.cs" />
|
||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
||||
<Compile Include="Utilities\AppCenterHelper.cs" />
|
||||
<Compile Include="Utilities\ThemeHelpers.cs" />
|
||||
<Compile Include="WebAuthCallbackActivity.cs" />
|
||||
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||
<Compile Include="Services\ClipboardService.cs" />
|
||||
<Compile Include="Utilities\IntentExtensions.cs" />
|
||||
<Compile Include="Renderers\CustomPageRenderer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
||||
|
||||
@@ -64,12 +64,15 @@ namespace Bit.Droid
|
||||
Intent?.Validate();
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
if (!CoreHelpers.InDebugMode())
|
||||
{
|
||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||
}
|
||||
//if (!CoreHelpers.InDebugMode())
|
||||
//{
|
||||
// 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;
|
||||
if (toplayout != null)
|
||||
@@ -82,7 +85,6 @@ namespace Bit.Droid
|
||||
_appOptions = GetOptions();
|
||||
LoadApplication(new App.App(_appOptions));
|
||||
|
||||
|
||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||
{
|
||||
if (message.Command == "startEventTimer")
|
||||
|
||||
@@ -12,6 +12,7 @@ using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Services;
|
||||
using Bit.Droid.Utilities;
|
||||
using Plugin.CurrentActivity;
|
||||
using Plugin.Fingerprint;
|
||||
using Xamarin.Android.Net;
|
||||
@@ -19,7 +20,6 @@ using System.Net.Http;
|
||||
using System.Net;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.App.Pages;
|
||||
using Bit.App.Utilities.AccountManagement;
|
||||
#if !FDROID
|
||||
using Android.Gms.Security;
|
||||
#endif
|
||||
@@ -62,15 +62,6 @@ namespace Bit.Droid
|
||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"));
|
||||
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 (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||
@@ -130,14 +121,13 @@ namespace Bit.Droid
|
||||
var secureStorageService = new SecureStorageService();
|
||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||
var stateService = new StateService(mobileStorageService, secureStorageService, messagingService);
|
||||
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||
var stateMigrationService =
|
||||
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
||||
var clipboardService = new ClipboardService(stateService);
|
||||
var deviceActionService = new DeviceActionService(clipboardService, stateService, messagingService,
|
||||
var deviceActionService = new DeviceActionService(stateService, messagingService,
|
||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
||||
messagingService, broadcasterService);
|
||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||
broadcasterService);
|
||||
var biometricService = new BiometricService();
|
||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||
@@ -152,7 +142,7 @@ namespace Bit.Droid
|
||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||
ServiceContainer.Register<IStateService>("stateService", stateService);
|
||||
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
|
||||
ServiceContainer.Register<IClipboardService>("clipboardService", clipboardService);
|
||||
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
|
||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2022.6.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"/>
|
||||
|
||||
|
||||
@@ -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">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -2,5 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -3,10 +3,11 @@ using System.Threading.Tasks;
|
||||
using Android.OS;
|
||||
using Android.Security.Keystore;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Java.Security;
|
||||
using Javax.Crypto;
|
||||
#if !FDROID
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
#endif
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
{
|
||||
@@ -73,7 +74,9 @@ namespace Bit.Droid.Services
|
||||
catch (InvalidKeyException e)
|
||||
{
|
||||
// Fallback for old bitwarden users without a key
|
||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||
#if !FDROID
|
||||
Crashes.TrackError(e);
|
||||
#endif
|
||||
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
|
||||
// is not functioning
|
||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||
#if !FDROID
|
||||
Crashes.TrackError(e);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Droid.Receivers;
|
||||
using Plugin.CurrentActivity;
|
||||
@@ -26,41 +26,13 @@ namespace Bit.Droid.Services
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
CopyToClipboard(text, isSensitive);
|
||||
}
|
||||
await Clipboard.SetTextAsync(text);
|
||||
|
||||
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)
|
||||
{
|
||||
var clearMs = expiresInMs;
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class DeviceActionService : IDeviceActionService
|
||||
{
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
@@ -48,13 +47,11 @@ namespace Bit.Droid.Services
|
||||
private string _userAgent;
|
||||
|
||||
public DeviceActionService(
|
||||
IClipboardService clipboardService,
|
||||
IStateService stateService,
|
||||
IMessagingService messagingService,
|
||||
IBroadcasterService broadcasterService,
|
||||
Func<IEventService> eventServiceFunc)
|
||||
{
|
||||
_clipboardService = clipboardService;
|
||||
_stateService = stateService;
|
||||
_messagingService = messagingService;
|
||||
_broadcasterService = broadcasterService;
|
||||
@@ -932,12 +929,20 @@ namespace Bit.Droid.Services
|
||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||
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()
|
||||
{
|
||||
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>
|
||||
|
||||
<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="Xamarin.CommunityToolkit" Version="2.0.2" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.3" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.1" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.2" />
|
||||
<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.Forms" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
@@ -128,7 +129,6 @@
|
||||
<Folder Include="Resources\" />
|
||||
<Folder Include="Behaviors\" />
|
||||
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
||||
<Folder Include="Utilities\AccountManagement\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -421,6 +421,5 @@
|
||||
<None Remove="Behaviors\" />
|
||||
<None Remove="Xamarin.CommunityToolkit" />
|
||||
<None Remove="Controls\AccountSwitchingOverlay\" />
|
||||
<None Remove="Utilities\AccountManagement\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -6,7 +6,6 @@ using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.App.Utilities.AccountManagement;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
@@ -17,7 +16,7 @@ using Xamarin.Forms.Xaml;
|
||||
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
namespace Bit.App
|
||||
{
|
||||
public partial class App : Application, IAccountsManagerHost
|
||||
public partial class App : Application
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
@@ -28,7 +27,6 @@ namespace Bit.App
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAccountsManager _accountsManager;
|
||||
|
||||
private static bool _isResumed;
|
||||
|
||||
@@ -49,9 +47,6 @@ namespace Bit.App
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
|
||||
|
||||
_accountsManager.Init(() => Options, this);
|
||||
|
||||
Bootstrap();
|
||||
_broadcasterService.Subscribe(nameof(App), async (message) =>
|
||||
@@ -76,6 +71,30 @@ namespace Bit.App
|
||||
_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")
|
||||
{
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
@@ -90,10 +109,22 @@ namespace Bit.App
|
||||
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")
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
await _accountsManager.NavigateOnAccountChangeAsync();
|
||||
await SetMainPageAsync();
|
||||
}
|
||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||
message.Command == "popAllAndGoToTabMyVault" ||
|
||||
@@ -137,6 +168,7 @@ namespace Bit.App
|
||||
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,7 +234,6 @@ namespace Bit.App
|
||||
|
||||
private async Task ResumedAsync()
|
||||
{
|
||||
await _stateService.CheckExtensionActiveUserAndSwitchIfNeededAsync();
|
||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
_messagingService.Send("startEventTimer");
|
||||
await UpdateThemeAsync();
|
||||
@@ -232,6 +263,102 @@ namespace Bit.App
|
||||
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()
|
||||
{
|
||||
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
||||
@@ -293,7 +420,7 @@ namespace Bit.App
|
||||
UpdateThemeAsync();
|
||||
};
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
var mainPageTask = _accountsManager.NavigateOnAccountChangeAsync();
|
||||
var mainPageTask = SetMainPageAsync();
|
||||
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;
|
||||
if (Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
|
||||
{
|
||||
@@ -341,44 +483,8 @@ namespace Bit.App
|
||||
}
|
||||
}
|
||||
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +63,6 @@ namespace Bit.App.Controls
|
||||
|
||||
public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70;
|
||||
|
||||
public bool LongPressAccountEnabled { get; set; } = true;
|
||||
|
||||
public Action AfterHide { get; set; }
|
||||
|
||||
public async Task ToggleVisibilityAsync()
|
||||
{
|
||||
if (IsVisible)
|
||||
@@ -139,8 +135,6 @@ namespace Bit.App.Controls
|
||||
|
||||
// remove overlay
|
||||
IsVisible = false;
|
||||
|
||||
AfterHide?.Invoke();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,7 +167,7 @@ namespace Bit.App.Controls
|
||||
|
||||
private async Task LongPressAccountAsync(AccountViewCellViewModel item)
|
||||
{
|
||||
if (!LongPressAccountEnabled || !item.IsAccount)
|
||||
if (!item.IsAccount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -45,28 +45,23 @@ namespace Bit.App.Controls
|
||||
|
||||
public ICommand LongPressAccountCommand { get; }
|
||||
|
||||
public bool FromIOSExtension { get; set; }
|
||||
|
||||
private async Task SelectAccountAsync(AccountViewCellViewModel item)
|
||||
{
|
||||
if (!item.AccountView.IsAccount)
|
||||
if (item.AccountView.IsAccount)
|
||||
{
|
||||
_messagingService.Send(AccountsManagerMessageCommands.ADD_ACCOUNT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.AccountView.IsActive)
|
||||
{
|
||||
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
if (FromIOSExtension)
|
||||
if (!item.AccountView.IsActive)
|
||||
{
|
||||
await _stateService.SaveExtensionActiveUserIdToStorageAsync(item.AccountView.UserId);
|
||||
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||
_messagingService.Send("switchedAccount");
|
||||
}
|
||||
else if (AllowActiveAccountSelection)
|
||||
{
|
||||
_messagingService.Send("switchedAccount");
|
||||
}
|
||||
}
|
||||
else if (AllowActiveAccountSelection)
|
||||
else
|
||||
{
|
||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
_messagingService.Send("addAccount");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
<controls:MiButton
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
||||
Text=""
|
||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||
Clicked="MoreButton_Clicked"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
|
||||
@@ -4,6 +4,5 @@ namespace Bit.App.Controls
|
||||
{
|
||||
public class ExtendedCollectionView : CollectionView
|
||||
{
|
||||
public string ExtraDataForLogging { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<controls:MiButton
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
||||
Text=""
|
||||
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||
Clicked="MoreButton_Clicked"
|
||||
|
||||
@@ -46,11 +46,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get => _showPassword;
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
||||
}
|
||||
|
||||
public bool IsPolicyInEffect
|
||||
@@ -72,7 +68,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string MasterPassword { get; set; }
|
||||
public string ConfirmMasterPassword { get; set; }
|
||||
public string Hint { get; set; }
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<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>
|
||||
|
||||
<ScrollView>
|
||||
@@ -34,7 +34,8 @@
|
||||
Placeholder="ex. https://bitwarden.company.com"
|
||||
StyleClass="box-value"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding SubmitCommand}" />
|
||||
ReturnCommand="{Binding SubmitCommand}"
|
||||
AutomationId="server_input"/>
|
||||
</StackLayout>
|
||||
<Label
|
||||
Text="{u:I18n SelfHostedEnvironmentFooter}"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
Priority="-1"
|
||||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Account}" />
|
||||
AutomationProperties.Name="{u:I18n Account}"/>
|
||||
<ToolbarItem
|
||||
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
@@ -37,15 +37,18 @@
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
Source="logo.png"
|
||||
VerticalOptions="Center" />
|
||||
VerticalOptions="Center"
|
||||
AutomationId="logo_image"
|
||||
/>
|
||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||
StyleClass="text-lg"
|
||||
HorizontalTextAlignment="Center">
|
||||
</Label>
|
||||
HorizontalTextAlignment="Center"/>
|
||||
|
||||
<StackLayout Spacing="5">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
Clicked="LogIn_Clicked"
|
||||
AutomationId="homepage_login_button"/>
|
||||
<Button Text="{u:I18n CreateAccount}"
|
||||
Clicked="Register_Clicked" />
|
||||
<Button Text="{u:I18n LogInSso}"
|
||||
|
||||
@@ -80,8 +80,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="_passwordGrid"
|
||||
@@ -120,8 +119,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<StackLayout
|
||||
StyleClass="box-row"
|
||||
|
||||
@@ -72,8 +72,7 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText),
|
||||
nameof(ShowPasswordIcon)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -129,7 +128,6 @@ namespace Bit.App.Pages
|
||||
public Command SubmitCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string MasterPassword { get; set; }
|
||||
public string Pin { get; set; }
|
||||
public Action UnlockedAction { get; set; }
|
||||
|
||||
@@ -56,7 +56,8 @@
|
||||
x:Name="_email"
|
||||
Text="{Binding Email}"
|
||||
Keyboard="Email"
|
||||
StyleClass="box-value">
|
||||
StyleClass="box-value"
|
||||
AutomationId="email_input">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Disabled">
|
||||
@@ -92,7 +93,8 @@
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
ReturnType="Go"
|
||||
ReturnCommand="{Binding LogInCommand}" />
|
||||
ReturnCommand="{Binding LogInCommand}"
|
||||
AutomationId="password_input"/>
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
@@ -101,17 +103,18 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
<StackLayout Padding="10, 0">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
Clicked="LogIn_Clicked"
|
||||
AutomationId="loginpage_login_button"/>
|
||||
<Button Text="{u:I18n Cancel}"
|
||||
IsVisible="{Binding ShowCancelButton}"
|
||||
Clicked="Cancel_Clicked" />
|
||||
Clicked="Cancel_Clicked"
|
||||
AutomationId="cancel_button"/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
||||
@@ -58,8 +58,7 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
nameof(ShowPasswordIcon)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -86,7 +85,6 @@ namespace Bit.App.Pages
|
||||
public Command LogInCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public Action StartTwoFactorAction { get; set; }
|
||||
public Action LogInSuccessAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
|
||||
@@ -81,12 +81,10 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||
string ssoToken;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _apiService.PreValidateSso(OrgIdentifier);
|
||||
ssoToken = response.Token;
|
||||
await _apiService.PreValidateSso(OrgIdentifier);
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
@@ -114,8 +112,7 @@ namespace Bit.App.Pages
|
||||
"response_type=code&scope=api%20offline_access&" +
|
||||
"state=" + state + "&code_challenge=" + codeChallenge + "&" +
|
||||
"code_challenge_method=S256&response_mode=query&" +
|
||||
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier) + "&" +
|
||||
"ssoToken=" + Uri.EscapeDataString(ssoToken);
|
||||
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier);
|
||||
|
||||
WebAuthenticatorResult authResult = null;
|
||||
try
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<Label
|
||||
Text="{u:I18n MasterPasswordDescription}"
|
||||
@@ -107,8 +106,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
|
||||
@@ -51,8 +51,7 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
nameof(ShowPasswordIcon)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,7 +73,6 @@ namespace Bit.App.Pages
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public Command ToggleConfirmPasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string MasterPassword { get; set; }
|
||||
|
||||
@@ -107,8 +107,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<Label
|
||||
Text="{u:I18n MasterPasswordDescription}"
|
||||
@@ -146,8 +145,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
|
||||
@@ -55,11 +55,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get => _showPassword;
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
||||
}
|
||||
|
||||
public bool IsPolicyInEffect
|
||||
@@ -90,7 +86,6 @@ namespace Bit.App.Pages
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public Command ToggleConfirmPasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string MasterPassword { get; set; }
|
||||
public string ConfirmMasterPassword { get; set; }
|
||||
public string Hint { get; set; }
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace Bit.App.Pages
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly IAppIdService _appIdService;
|
||||
|
||||
private TwoFactorProviderType? _selectedProviderType;
|
||||
private string _totpInstruction;
|
||||
@@ -50,7 +49,6 @@ namespace Bit.App.Pages
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
|
||||
PageTitle = AppResources.TwoStepLogin;
|
||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||
@@ -382,8 +380,7 @@ namespace Bit.App.Pages
|
||||
var request = new TwoFactorEmailRequest
|
||||
{
|
||||
Email = _authService.Email,
|
||||
MasterPasswordHash = _authService.MasterPasswordHash,
|
||||
DeviceIdentifier = await _appIdService.GetAppIdAsync()
|
||||
MasterPasswordHash = _authService.MasterPasswordHash
|
||||
};
|
||||
await _apiService.PostTwoFactorEmailAsync(request);
|
||||
if (showLoading)
|
||||
|
||||
@@ -105,8 +105,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box">
|
||||
@@ -141,8 +140,7 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</Grid>
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace Bit.App.Pages
|
||||
VerticalOptions = LayoutOptions.CenterAndExpand,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Color = ThemeManager.GetResourceColor("PrimaryColor"),
|
||||
AutomationId = "activity_indicator"
|
||||
};
|
||||
if (targetView != null)
|
||||
{
|
||||
|
||||
@@ -37,14 +37,11 @@ namespace Bit.App.Pages
|
||||
bool cancelled = false;
|
||||
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
|
||||
{
|
||||
Url = new Uri(url),
|
||||
CallbackUrl = new Uri(callbackUri),
|
||||
PrefersEphemeralWebBrowserSession = false,
|
||||
PrefersEphemeralWebBrowserSession = true,
|
||||
};
|
||||
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@
|
||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||
ItemsSource="{Binding History}"
|
||||
VerticalOptions="FillAndExpand"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Generator History Page">
|
||||
StyleClass="list, list-platform">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
||||
<Grid
|
||||
|
||||
@@ -58,7 +58,8 @@ namespace Bit.App.Pages
|
||||
private async void CopyAsync(GeneratedPasswordHistory ph)
|
||||
{
|
||||
await _clipboardService.CopyTextAsync(ph.Password);
|
||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||
_platformUtilsService.ShowToast("info", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||
}
|
||||
|
||||
public async Task UpdateOnThemeChanged()
|
||||
|
||||
@@ -183,68 +183,52 @@
|
||||
<Label
|
||||
Text="A-Z"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding Uppercase}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
||||
HorizontalOptions="End" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
<Label
|
||||
Text="a-z"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}"/>
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding Lowercase}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}" />
|
||||
HorizontalOptions="End" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
<Label
|
||||
Text="0-9"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding Number}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
||||
HorizontalOptions="End" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
<Label
|
||||
Text="!@#$%^&*"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding Special}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
||||
HorizontalOptions="End" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-stepper">
|
||||
@@ -293,7 +277,7 @@
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding AvoidAmbiguousChars}"
|
||||
IsToggled="{Binding AvoidAmbiguous}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End" />
|
||||
</StackLayout>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -22,7 +23,7 @@ namespace Bit.App.Pages
|
||||
private bool _lowercase;
|
||||
private bool _number;
|
||||
private bool _special;
|
||||
private bool _allowAmbiguousChars;
|
||||
private bool _avoidAmbiguous;
|
||||
private int _minNumber;
|
||||
private int _minSpecial;
|
||||
private int _length = 5;
|
||||
@@ -129,29 +130,19 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllowAmbiguousChars
|
||||
public bool AvoidAmbiguous
|
||||
{
|
||||
get => _allowAmbiguousChars;
|
||||
get => _avoidAmbiguous;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _allowAmbiguousChars, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(AvoidAmbiguousChars)
|
||||
}))
|
||||
if (SetProperty(ref _avoidAmbiguous, value))
|
||||
{
|
||||
_options.AllowAmbiguousChar = value;
|
||||
_options.Ambiguous = !value;
|
||||
var task = SaveOptionsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AvoidAmbiguousChars
|
||||
{
|
||||
get => !AllowAmbiguousChars;
|
||||
set => AllowAmbiguousChars = !value;
|
||||
}
|
||||
|
||||
public int MinNumber
|
||||
{
|
||||
get => _minNumber;
|
||||
@@ -318,12 +309,13 @@ namespace Bit.App.Pages
|
||||
public async Task CopyAsync()
|
||||
{
|
||||
await _clipboardService.CopyTextAsync(Password);
|
||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||
_platformUtilsService.ShowToast("success", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||
}
|
||||
|
||||
private void LoadFromOptions()
|
||||
{
|
||||
AllowAmbiguousChars = _options.AllowAmbiguousChar.GetValueOrDefault();
|
||||
AvoidAmbiguous = !_options.Ambiguous.GetValueOrDefault();
|
||||
TypeSelectedIndex = _options.Type == "passphrase" ? 1 : 0;
|
||||
IsPassword = TypeSelectedIndex == 0;
|
||||
MinNumber = _options.MinNumber.GetValueOrDefault();
|
||||
@@ -341,7 +333,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private void SetOptions()
|
||||
{
|
||||
_options.AllowAmbiguousChar = AllowAmbiguousChars;
|
||||
_options.Ambiguous = !AvoidAmbiguous;
|
||||
_options.Type = TypeSelectedIndex == 1 ? "passphrase" : "password";
|
||||
_options.MinNumber = MinNumber;
|
||||
_options.MinSpecial = MinSpecial;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
x:Class="Bit.App.Pages.SendAddEditPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
@@ -122,7 +121,6 @@
|
||||
Clicked="FileType_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n File}"
|
||||
AutomationProperties.HelpText="{Binding FileTypeAccessibilityLabel}"
|
||||
Grid.Column="0">
|
||||
</Button>
|
||||
<Button
|
||||
@@ -134,7 +132,6 @@
|
||||
Clicked="TextType_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Text}"
|
||||
AutomationProperties.HelpText="{Binding TextTypeAccessibilityLabel}"
|
||||
Grid.Column="1">
|
||||
</Button>
|
||||
</Grid>
|
||||
@@ -253,31 +250,28 @@
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
Orientation="Horizontal"
|
||||
Spacing="0"
|
||||
xct:TouchEffect.Command="{Binding ToggleOptionsCommand}"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{Binding OptionsAccessilibityText}">
|
||||
Spacing="0">
|
||||
<Button
|
||||
Text="{u:I18n Options}"
|
||||
x:Name="_btnOptions"
|
||||
StyleClass="box-row-button"
|
||||
TextColor="{DynamicResource PrimaryColor}"
|
||||
Margin="0"
|
||||
AutomationProperties.IsInAccessibleTree="False"/>
|
||||
Clicked="ToggleOptions_Clicked"/>
|
||||
<controls:IconButton
|
||||
x:Name="_btnOptionsUp"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
|
||||
StyleClass="box-row-button"
|
||||
TextColor="{DynamicResource PrimaryColor}"
|
||||
IsVisible="{Binding ShowOptions}"
|
||||
AutomationProperties.IsInAccessibleTree="False"/>
|
||||
Clicked="ToggleOptions_Clicked"
|
||||
IsVisible="{Binding ShowOptions}" />
|
||||
<controls:IconButton
|
||||
x:Name="_btnOptionsDown"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
|
||||
StyleClass="box-row-button"
|
||||
TextColor="{DynamicResource PrimaryColor}"
|
||||
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}"
|
||||
AutomationProperties.IsInAccessibleTree="False"/>
|
||||
Clicked="ToggleOptions_Clicked"
|
||||
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}" />
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding ShowOptions}">
|
||||
<StackLayout
|
||||
@@ -444,8 +438,7 @@
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Margin="10,0,0,0"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
</StackLayout>
|
||||
<Label
|
||||
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)
|
||||
{
|
||||
if (DoOnce())
|
||||
|
||||
@@ -44,8 +44,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
nameof(IsText),
|
||||
nameof(IsFile),
|
||||
nameof(FileTypeAccessibilityLabel),
|
||||
nameof(TextTypeAccessibilityLabel)
|
||||
};
|
||||
private bool _disableHideEmail;
|
||||
private bool _sendOptionsPolicyInEffect;
|
||||
@@ -61,7 +59,6 @@ namespace Bit.App.Pages
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
ToggleOptionsCommand = new Command(ToggleOptions);
|
||||
|
||||
TypeOptions = new List<KeyValuePair<string, SendType>>
|
||||
{
|
||||
@@ -92,7 +89,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
public Command TogglePasswordCommand { get; set; }
|
||||
public Command ToggleOptionsCommand { get; set; }
|
||||
public string SendId { get; set; }
|
||||
public int SegmentedButtonHeight { get; set; }
|
||||
public int SegmentedButtonFontSize { get; set; }
|
||||
@@ -106,7 +102,6 @@ namespace Bit.App.Pages
|
||||
public bool DisableHideEmailControl { get; set; }
|
||||
public bool IsAddFromShare { get; set; }
|
||||
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, string>> DeletionTypeOptions { get; }
|
||||
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
||||
@@ -139,11 +134,7 @@ namespace Bit.App.Pages
|
||||
public bool ShowOptions
|
||||
{
|
||||
get => _showOptions;
|
||||
set => SetProperty(ref _showOptions, value,
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(OptionsAccessilibityText)
|
||||
});
|
||||
set => SetProperty(ref _showOptions, value);
|
||||
}
|
||||
public int ExpirationDateTypeSelectedIndex
|
||||
{
|
||||
@@ -220,8 +211,7 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
nameof(ShowPasswordIcon)
|
||||
});
|
||||
}
|
||||
public bool DisableHideEmail
|
||||
@@ -241,9 +231,6 @@ namespace Bit.App.Pages
|
||||
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
|
||||
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.SendGroupingsPage"
|
||||
@@ -138,8 +138,7 @@
|
||||
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Send Groupings Page" />
|
||||
StyleClass="list, list-platform" />
|
||||
</RefreshView>
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -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"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
@@ -66,8 +66,7 @@
|
||||
VerticalOptions="FillAndExpand"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Sends Page">
|
||||
StyleClass="list, list-platform">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:SendView">
|
||||
<controls:SendViewCell
|
||||
|
||||
@@ -105,7 +105,6 @@
|
||||
Grid.Column="1"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
||||
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
||||
<Label
|
||||
Text="{u:I18n ConfirmYourIdentity}"
|
||||
|
||||
@@ -109,11 +109,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get => _showPassword;
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText),
|
||||
});
|
||||
additionalPropertyNames: new string[] { nameof(ShowPasswordIcon) });
|
||||
}
|
||||
|
||||
public bool UseOTPVerification
|
||||
@@ -143,7 +139,6 @@ namespace Bit.App.Pages
|
||||
public Command TogglePasswordCommand { get; }
|
||||
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
|
||||
public void TogglePassword()
|
||||
{
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
VerticalOptions="FillAndExpand"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Folders Page">
|
||||
StyleClass="list, list-platform">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:FolderView">
|
||||
<controls:ExtendedStackLayout
|
||||
|
||||
@@ -113,7 +113,6 @@
|
||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Settings Page" />
|
||||
StyleClass="list, list-platform" />
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -167,10 +167,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await _vm.UpdatePinAsync();
|
||||
}
|
||||
else if (item.Name == AppResources.SubmitCrashLogs)
|
||||
{
|
||||
await _vm.LoggerReportingAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
var biometricName = AppResources.Biometrics;
|
||||
|
||||
@@ -7,10 +7,10 @@ using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
using ZXing.Client.Result;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -29,7 +29,7 @@ namespace Bit.App.Pages
|
||||
private readonly ILocalizeService _localizeService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly ILogger _loggerService;
|
||||
|
||||
private const int CustomVaultTimeoutValue = -100;
|
||||
|
||||
private bool _supportsBiometric;
|
||||
@@ -39,7 +39,6 @@ namespace Bit.App.Pages
|
||||
private string _vaultTimeoutDisplayValue;
|
||||
private string _vaultTimeoutActionDisplayValue;
|
||||
private bool _showChangeMasterPassword;
|
||||
private bool _reportLoggingEnabled;
|
||||
|
||||
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
||||
new List<KeyValuePair<string, int?>>
|
||||
@@ -80,7 +79,6 @@ namespace Bit.App.Pages
|
||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
_loggerService = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
GroupedItems = new ObservableRangeCollection<ISettingsPageListItem>();
|
||||
PageTitle = AppResources.Settings;
|
||||
@@ -125,7 +123,7 @@ namespace Bit.App.Pages
|
||||
|
||||
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
||||
!await _keyConnectorService.GetUsesKeyConnector();
|
||||
_reportLoggingEnabled = await _loggerService.IsEnabled();
|
||||
|
||||
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()
|
||||
{
|
||||
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.WebVault });
|
||||
}
|
||||
|
||||
var otherItems = new List<SettingsPageListItem>
|
||||
{
|
||||
new SettingsPageListItem { Name = AppResources.Options },
|
||||
new SettingsPageListItem { Name = AppResources.About },
|
||||
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.DeleteAccount }
|
||||
};
|
||||
@@ -606,9 +576,5 @@ namespace Bit.App.Pages
|
||||
{
|
||||
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 System.Threading.Tasks;
|
||||
using Bit.App.Effects;
|
||||
using Bit.App.Effects;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
@@ -12,10 +10,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class TabsPage : TabbedPage
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||
|
||||
private NavigationPage _groupingsPage;
|
||||
private NavigationPage _sendGroupingsPage;
|
||||
@@ -23,7 +19,6 @@ namespace Bit.App.Pages
|
||||
|
||||
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
||||
{
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
|
||||
@@ -83,26 +78,12 @@ namespace Bit.App.Pages
|
||||
protected override async void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
_broadcasterService.Subscribe(nameof(TabsPage), async (message) =>
|
||||
{
|
||||
if (message.Command == "syncCompleted")
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync());
|
||||
}
|
||||
});
|
||||
await UpdateVaultButtonTitleAsync();
|
||||
if (await _keyConnectorService.UserNeedsMigration())
|
||||
{
|
||||
_messagingService.Send("convertAccountToKeyConnector");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
_broadcasterService.Unsubscribe(nameof(TabsPage));
|
||||
}
|
||||
|
||||
public void ResetToVaultPage()
|
||||
{
|
||||
CurrentPage = _groupingsPage;
|
||||
@@ -150,19 +131,5 @@ namespace Bit.App.Pages
|
||||
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"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
||||
IsVisible="{Binding Cipher.ViewPassword}" />
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace Bit.App.Pages
|
||||
CipherType? type = null,
|
||||
string folderId = null,
|
||||
string collectionId = null,
|
||||
string organizationId = null,
|
||||
string name = null,
|
||||
string uri = null,
|
||||
bool fromAutofill = false,
|
||||
@@ -52,7 +51,6 @@ namespace Bit.App.Pages
|
||||
_vm.CipherId = cipherId;
|
||||
_vm.FolderId = folderId == "none" ? null : folderId;
|
||||
_vm.CollectionIds = collectionId != null ? new HashSet<string>(new List<string> { collectionId }) : null;
|
||||
_vm.OrganizationId = organizationId;
|
||||
_vm.Type = type;
|
||||
_vm.DefaultName = name ?? appOptions?.SaveName;
|
||||
_vm.DefaultUri = uri ?? appOptions?.Uri;
|
||||
|
||||
@@ -249,8 +249,7 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
nameof(ShowPasswordIcon)
|
||||
});
|
||||
}
|
||||
public bool ShowCardNumber
|
||||
@@ -299,7 +298,6 @@ namespace Bit.App.Pages
|
||||
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
|
||||
public bool AllowPersonal { get; set; }
|
||||
public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
|
||||
@@ -85,8 +85,7 @@
|
||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Autofill Ciphers Page" />
|
||||
StyleClass="list, list-platform" />
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
@@ -33,9 +33,7 @@
|
||||
Text=""
|
||||
VerticalOptions="CenterAndExpand"
|
||||
Clicked="BackButton_Clicked"
|
||||
x:Name="_backButton"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n TapToGoBack}"/>
|
||||
x:Name="_backButton" />
|
||||
<controls:ExtendedSearchBar
|
||||
x:Name="_searchBar"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
@@ -49,31 +47,6 @@
|
||||
</ContentPage.Resources>
|
||||
|
||||
<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}"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.Search}}"
|
||||
StyleClass="text-muted"
|
||||
@@ -93,8 +66,7 @@
|
||||
VerticalOptions="FillAndExpand"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Ciphers Page">
|
||||
StyleClass="list, list-platform">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:CipherView">
|
||||
<controls:CipherViewCell
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace Bit.App.Pages
|
||||
private CiphersPageViewModel _vm;
|
||||
private bool _hasFocused;
|
||||
|
||||
public CiphersPage(Func<CipherView, bool> filter, string pageTitle = null, string vaultFilterSelection = null,
|
||||
string autofillUrl = null, bool deleted = false)
|
||||
public CiphersPage(Func<CipherView, bool> filter, string pageTitle = null, string autofillUrl = null, bool deleted = false)
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as CiphersPageViewModel;
|
||||
@@ -34,7 +33,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_vm.PageTitle = AppResources.SearchVault;
|
||||
}
|
||||
_vm.VaultFilterDescription = vaultFilterSelection;
|
||||
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
|
||||
@@ -5,17 +5,17 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class CiphersPageViewModel : VaultFilterViewModel
|
||||
public class CiphersPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly ICipherService _cipherService;
|
||||
@@ -23,10 +23,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private CancellationTokenSource _searchCancellationTokenSource;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private bool _showNoData;
|
||||
private bool _showList;
|
||||
@@ -40,9 +37,6 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
Ciphers = new ExtendedObservableCollection<CipherView>();
|
||||
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
||||
@@ -54,11 +48,6 @@ namespace Bit.App.Pages
|
||||
public string AutofillUrl { 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
|
||||
{
|
||||
get => _showNoData;
|
||||
@@ -87,9 +76,11 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await InitVaultFilterAsync(true);
|
||||
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)
|
||||
@@ -116,9 +107,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
try
|
||||
{
|
||||
var vaultFilteredCiphers = await GetAllCiphersAsync();
|
||||
ciphers = await _searchService.SearchCiphersAsync(searchText,
|
||||
Filter ?? (c => c.IsDeleted == Deleted), vaultFilteredCiphers, cts.Token);
|
||||
Filter ?? (c => c.IsDeleted == Deleted), null, cts.Token);
|
||||
cts.Token.ThrowIfCancellationRequested();
|
||||
}
|
||||
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)
|
||||
{
|
||||
if ((Page as BaseContentPage).DoOnce())
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
xmlns:effects="clr-namespace:Bit.App.Effects"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
||||
x:DataType="pages:GroupingsPageViewModel"
|
||||
Title="{Binding PageTitle}"
|
||||
x:Name="_page">
|
||||
@@ -107,30 +106,6 @@
|
||||
GroupTemplate="{StaticResource groupTemplate}" />
|
||||
|
||||
<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
|
||||
VerticalOptions="CenterAndExpand"
|
||||
Padding="20, 0"
|
||||
@@ -155,8 +130,7 @@
|
||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Groupings Page" />
|
||||
StyleClass="list, list-platform" />
|
||||
</RefreshView>
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace Bit.App.Pages
|
||||
private PreviousPageInfo _previousPage;
|
||||
|
||||
public GroupingsPage(bool mainPage, CipherType? type = null, string folderId = null,
|
||||
string collectionId = null, string pageTitle = null, string vaultFilterSelection = null,
|
||||
PreviousPageInfo previousPage = null, bool deleted = false)
|
||||
string collectionId = null, string pageTitle = null, PreviousPageInfo previousPage = null,
|
||||
bool deleted = false)
|
||||
{
|
||||
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
||||
InitializeComponent();
|
||||
@@ -52,10 +52,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_vm.PageTitle = pageTitle;
|
||||
}
|
||||
if (vaultFilterSelection != null)
|
||||
{
|
||||
_vm.VaultFilterDescription = vaultFilterSelection;
|
||||
}
|
||||
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
@@ -263,7 +259,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class GroupingsPageViewModel : VaultFilterViewModel
|
||||
public class GroupingsPageViewModel : BaseViewModel
|
||||
{
|
||||
private const int NoFolderListSize = 100;
|
||||
|
||||
@@ -46,8 +46,6 @@ namespace Bit.App.Pages
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public GroupingsPageViewModel()
|
||||
@@ -62,11 +60,10 @@ namespace Bit.App.Pages
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
Loading = true;
|
||||
PageTitle = AppResources.MyVault;
|
||||
GroupedItems = new ObservableRangeCollection<IGroupingsPageListItem>();
|
||||
RefreshCommand = new Command(async () =>
|
||||
{
|
||||
@@ -90,9 +87,8 @@ namespace Bit.App.Pages
|
||||
public bool HasCiphers { get; set; }
|
||||
public bool HasFolders { get; set; }
|
||||
public bool HasCollections { get; set; }
|
||||
public bool ShowNoFolderCipherGroup => NoFolderCiphers != null
|
||||
&& NoFolderCiphers.Count < NoFolderListSize
|
||||
&& (Collections is null || !Collections.Any());
|
||||
public bool ShowNoFolderCiphers => (NoFolderCiphers?.Count ?? int.MaxValue) < NoFolderListSize &&
|
||||
(!Collections?.Any() ?? true);
|
||||
public List<CipherView> Ciphers { get; set; }
|
||||
public List<CipherView> FavoriteCiphers { 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<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
|
||||
{
|
||||
get => _refreshing;
|
||||
@@ -181,12 +172,6 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
await InitVaultFilterAsync(MainPage);
|
||||
if (MainPage)
|
||||
{
|
||||
PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault;
|
||||
}
|
||||
|
||||
_doingLoad = true;
|
||||
LoadedOnce = true;
|
||||
ShowNoData = false;
|
||||
@@ -200,9 +185,9 @@ namespace Bit.App.Pages
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -277,7 +262,7 @@ namespace Bit.App.Pages
|
||||
groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
|
||||
ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
|
||||
}
|
||||
if (ShowNoFolderCipherGroup)
|
||||
if (ShowNoFolderCiphers)
|
||||
{
|
||||
var noFolderCiphersListItems = NoFolderCiphers.Select(
|
||||
c => new GroupingsPageListItem { Cipher = c }).ToList();
|
||||
@@ -309,12 +294,6 @@ namespace Bit.App.Pages
|
||||
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);
|
||||
}
|
||||
else
|
||||
@@ -337,12 +316,6 @@ namespace Bit.App.Pages
|
||||
|
||||
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.AddRange(items);
|
||||
}
|
||||
@@ -369,11 +342,6 @@ namespace Bit.App.Pages
|
||||
SyncRefreshing = false;
|
||||
}
|
||||
|
||||
protected override async Task OnVaultFilterSelectedAsync()
|
||||
{
|
||||
await LoadAsync();
|
||||
}
|
||||
|
||||
public async Task SelectCipherAsync(CipherView cipher)
|
||||
{
|
||||
var page = new ViewPage(cipher.Id);
|
||||
@@ -400,26 +368,25 @@ namespace Bit.App.Pages
|
||||
default:
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task SelectTrashAsync()
|
||||
{
|
||||
var page = new GroupingsPage(false, null, null, null, AppResources.Trash, _vaultFilterSelection, null,
|
||||
true);
|
||||
var page = new GroupingsPage(false, null, null, null, AppResources.Trash, null, true);
|
||||
await Page.Navigation.PushAsync(page);
|
||||
}
|
||||
|
||||
@@ -458,7 +425,7 @@ namespace Bit.App.Pages
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
NoDataText = AppResources.NoItems;
|
||||
_allCiphers = await GetAllCiphersAsync();
|
||||
_allCiphers = await _cipherService.GetAllDecryptedAsync();
|
||||
HasCiphers = _allCiphers.Any();
|
||||
FavoriteCiphers?.Clear();
|
||||
NoFolderCiphers?.Clear();
|
||||
@@ -472,11 +439,12 @@ namespace Bit.App.Pages
|
||||
|
||||
if (MainPage)
|
||||
{
|
||||
await FillFoldersAndCollectionsAsync();
|
||||
NestedFolders = await _folderService.GetAllNestedAsync(Folders);
|
||||
Folders = await _folderService.GetAllDecryptedAsync();
|
||||
NestedFolders = await _folderService.GetAllNestedAsync();
|
||||
HasFolders = NestedFolders.Any(f => f.Node?.Id != null);
|
||||
NestedCollections = Collections != null ? await _collectionService.GetAllNestedAsync(Collections) : null;
|
||||
HasCollections = NestedCollections?.Any() ?? false;
|
||||
Collections = await _collectionService.GetAllDecryptedAsync();
|
||||
NestedCollections = await _collectionService.GetAllNestedAsync(Collections);
|
||||
HasCollections = NestedCollections.Any();
|
||||
}
|
||||
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)
|
||||
{
|
||||
if ((Page as BaseContentPage).DoOnce())
|
||||
|
||||
@@ -39,8 +39,7 @@
|
||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||
ItemsSource="{Binding History}"
|
||||
VerticalOptions="FillAndExpand"
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Password History Page">
|
||||
StyleClass="list, list-platform">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:PasswordHistoryView">
|
||||
<Grid
|
||||
|
||||
@@ -41,17 +41,15 @@ namespace Bit.App.Pages
|
||||
{
|
||||
var cipher = await _cipherService.GetAsync(CipherId);
|
||||
var decCipher = await cipher.DecryptAsync();
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
History.ResetWithRange(decCipher.PasswordHistory ?? new List<PasswordHistoryView>());
|
||||
ShowNoData = History.Count == 0;
|
||||
});
|
||||
History.ResetWithRange(decCipher.PasswordHistory ?? new List<PasswordHistoryView>());
|
||||
ShowNoData = History.Count == 0;
|
||||
}
|
||||
|
||||
private async void CopyAsync(PasswordHistoryView ph)
|
||||
{
|
||||
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));
|
||||
|
||||
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 () =>
|
||||
{
|
||||
try
|
||||
@@ -54,7 +52,7 @@ namespace Bit.App.Pages
|
||||
while (!autofocusCts.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(2), autofocusCts.Token);
|
||||
await Device.InvokeOnMainThreadAsync(() =>
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
if (!autofocusCts.IsCancellationRequested)
|
||||
{
|
||||
@@ -75,10 +73,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_autofocusCts?.Cancel();
|
||||
|
||||
if (_continuousAutofocusTask != null)
|
||||
{
|
||||
await _continuousAutofocusTask;
|
||||
}
|
||||
await _continuousAutofocusTask;
|
||||
_zxing.IsScanning = false;
|
||||
|
||||
base.OnDisappearing();
|
||||
|
||||
@@ -145,7 +145,6 @@
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
|
||||
IsVisible="{Binding Cipher.ViewPassword}" />
|
||||
<controls:IconButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
|
||||
@@ -120,8 +120,7 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
nameof(ShowPasswordIcon)
|
||||
});
|
||||
}
|
||||
public bool ShowCardNumber
|
||||
@@ -214,7 +213,6 @@ namespace Bit.App.Pages
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string ShowCardNumberIcon => ShowCardNumber ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string ShowCardCodeIcon => ShowCardCode ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string TotpCodeFormatted
|
||||
{
|
||||
get => _totpCodeFormatted;
|
||||
@@ -663,7 +661,7 @@ namespace Bit.App.Pages
|
||||
await _clipboardService.CopyTextAsync(text);
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
_platformUtilsService.ShowToastForCopiedValue(name);
|
||||
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
|
||||
}
|
||||
if (id == "LoginPassword")
|
||||
{
|
||||
@@ -756,12 +754,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (IsBooleanType)
|
||||
{
|
||||
return _field.Value == "true" ? BitwardenIcons.Square : BitwardenIcons.CheckSquare;
|
||||
return _field.Value == "true" ? "" : "";
|
||||
}
|
||||
else if (IsLinkedType)
|
||||
{
|
||||
var i18nKey = _cipher.LinkedFieldI18nKey(Field.LinkedId.GetValueOrDefault());
|
||||
return BitwardenIcons.Link + _i18nService.T(i18nKey);
|
||||
return " " + _i18nService.T(i18nKey);
|
||||
}
|
||||
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>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="Text" xml:space="preserve">
|
||||
<value>Text</value>
|
||||
</data>
|
||||
<data name="TypeText" xml:space="preserve">
|
||||
<value>Text</value>
|
||||
</data>
|
||||
@@ -1880,18 +1877,6 @@
|
||||
<data name="TypeFileInfo" xml:space="preserve">
|
||||
<value>The file you want to send.</value>
|
||||
</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">
|
||||
<value>Deletion Date</value>
|
||||
</data>
|
||||
@@ -2196,52 +2181,4 @@
|
||||
<data name="EnterTheVerificationCodeThatWasSentToYourEmail" xml:space="preserve">
|
||||
<value>Enter the verification code that was sent to your email</value>
|
||||
</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>
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace Bit.App.Services
|
||||
private const int DialogPromiseExpiration = 600000; // 10 minutes
|
||||
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
|
||||
@@ -29,12 +28,10 @@ namespace Bit.App.Services
|
||||
|
||||
public MobilePlatformUtilsService(
|
||||
IDeviceActionService deviceActionService,
|
||||
IClipboardService clipboardService,
|
||||
IMessagingService messagingService,
|
||||
IBroadcasterService broadcasterService)
|
||||
{
|
||||
_deviceActionService = deviceActionService;
|
||||
_clipboardService = clipboardService;
|
||||
_messagingService = messagingService;
|
||||
_broadcasterService = broadcasterService;
|
||||
}
|
||||
@@ -132,15 +129,6 @@ namespace Bit.App.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ShowToastForCopiedValue(string valueNameCopied)
|
||||
{
|
||||
if (!_clipboardService.IsCopyNotificationHandledByPlatform())
|
||||
{
|
||||
ShowToast("info", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, valueNameCopied));
|
||||
}
|
||||
}
|
||||
|
||||
public bool SupportsFido2()
|
||||
{
|
||||
return _deviceActionService.SupportsFido2();
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace Bit.App.Services
|
||||
Constants.LastBuildKey,
|
||||
Constants.ClearCiphersCacheKey,
|
||||
Constants.BiometricIntegrityKey,
|
||||
Constants.iOSExtensionActiveUserIdKey,
|
||||
Constants.iOSAutoFillClearCiphersCacheKey,
|
||||
Constants.iOSAutoFillBiometricIntegrityKey,
|
||||
Constants.iOSExtensionClearCiphersCacheKey,
|
||||
@@ -33,7 +32,7 @@ namespace Bit.App.Services
|
||||
Constants.iOSShareExtensionClearCiphersCacheKey,
|
||||
Constants.iOSShareExtensionBiometricIntegrityKey,
|
||||
Constants.RememberedEmailKey,
|
||||
Constants.RememberedOrgIdentifierKey
|
||||
Constants.RememberedOrgIdentifierKey,
|
||||
};
|
||||
|
||||
public MobileStorageService(
|
||||
|
||||
@@ -268,16 +268,6 @@
|
||||
<Setter Property="TextColor"
|
||||
Value="{DynamicResource ButtonColor}" />
|
||||
</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"
|
||||
ApplyToDerivedTypes="True"
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -117,7 +119,8 @@ namespace Bit.App.Utilities
|
||||
if (!string.IsNullOrWhiteSpace(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())
|
||||
{
|
||||
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)
|
||||
@@ -138,14 +142,16 @@ namespace Bit.App.Utilities
|
||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else if (selection == AppResources.CopyNotes)
|
||||
{
|
||||
await clipboardService.CopyTextAsync(cipher.Notes);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Notes);
|
||||
platformUtilsService.ShowToast("info", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Notes));
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
@@ -256,7 +262,8 @@ namespace Bit.App.Utilities
|
||||
var platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
var clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||
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)
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Bit.Core.Abstractions
|
||||
Task PutDeleteCipherAsync(string id);
|
||||
Task<CipherResponse> PutRestoreCipherAsync(string id);
|
||||
Task RefreshIdentityTokenAsync();
|
||||
Task<SsoPrevalidateResponse> PreValidateSso(string identifier);
|
||||
Task<object> PreValidateSso(string identifier);
|
||||
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
||||
TRequest body, bool authed, bool hasResponse, bool logoutOnUnauthorized = true);
|
||||
void SetUrls(EnvironmentUrls urls);
|
||||
|
||||
@@ -8,17 +8,9 @@ namespace Bit.Core.Abstractions
|
||||
/// 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 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>
|
||||
/// <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="isSensitive">Flag to mark copied text as sensitive</param>
|
||||
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();
|
||||
Task CopyTextAsync(string text, int expiresInMs = -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<Folder> EncryptAsync(FolderView model, SymmetricCryptoKey key = null);
|
||||
Task<List<Folder>> GetAllAsync();
|
||||
Task<List<FolderView>> GetAllDecryptedAsync();
|
||||
Task<List<TreeNode<FolderView>>> GetAllNestedAsync(List<FolderView> folders = null);
|
||||
Task<List<TreeNode<FolderView>>> GetAllNestedAsync();
|
||||
Task<Folder> GetAsync(string id);
|
||||
Task<TreeNode<FolderView>> GetNestedAsync(string id);
|
||||
Task ReplaceAsync(Dictionary<string, FolderData> folders);
|
||||
|
||||
@@ -1,29 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
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>
|
||||
/// Logs something that is not in itself an exception, e.g. a wrong flow or value that needs to be reported
|
||||
/// 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);
|
||||
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 SupportsDuo();
|
||||
Task<bool> SupportsBiometricAsync();
|
||||
|
||||
@@ -20,6 +20,5 @@ namespace Bit.Core.Abstractions
|
||||
string orgId);
|
||||
Task<bool> PolicyAppliesToUser(PolicyType policyType, Func<Policy, bool> policyFilter = null, string userId = null);
|
||||
int? GetPolicyInt(Policy policy, string key);
|
||||
Task<bool> ShouldShowVaultFilterAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GetActiveUserIdAsync();
|
||||
Task<bool> IsActiveAccountAsync(string userId = null);
|
||||
Task SetActiveUserAsync(string userId);
|
||||
Task CheckExtensionActiveUserAndSwitchIfNeededAsync();
|
||||
Task<bool> IsAuthenticatedAsync(string userId = null);
|
||||
Task<string> GetUserIdAsync(string email);
|
||||
Task RefreshAccountViewsAsync(bool allowAddAccountRow);
|
||||
@@ -146,6 +145,5 @@ namespace Bit.Core.Abstractions
|
||||
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
|
||||
Task<string> GetTwoFactorTokenAsync(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 File = "\xe96e";
|
||||
public const string Paste = "\xe96f";
|
||||
public const string ViewCellMenu = "\xe5d3";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
public static string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState";
|
||||
public static string iOSShareExtensionClearCiphersCacheKey = "iOSShareExtensionClearCiphersCache";
|
||||
public static string iOSShareExtensionBiometricIntegrityKey = "iOSShareExtensionBiometricIntegrityState";
|
||||
public static string iOSExtensionActiveUserIdKey = "iOSExtensionActiveUserId";
|
||||
public static string EventCollectionKey = "eventCollection";
|
||||
public static string RememberedEmailKey = "rememberedEmail";
|
||||
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)
|
||||
{
|
||||
Length = 14;
|
||||
AllowAmbiguousChar = true;
|
||||
Ambiguous = false;
|
||||
Number = true;
|
||||
MinNumber = 1;
|
||||
Uppercase = true;
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
public int? Length { get; set; }
|
||||
public bool? AllowAmbiguousChar { get; set; }
|
||||
public bool? Ambiguous { get; set; }
|
||||
public bool? Number { get; set; }
|
||||
public int? MinNumber { get; set; }
|
||||
public bool? Uppercase { get; set; }
|
||||
@@ -45,7 +45,7 @@
|
||||
public void Merge(PasswordGenerationOptions defaults)
|
||||
{
|
||||
Length = Length ?? defaults.Length;
|
||||
AllowAmbiguousChar = AllowAmbiguousChar ?? defaults.AllowAmbiguousChar;
|
||||
Ambiguous = Ambiguous ?? defaults.Ambiguous;
|
||||
Number = Number ?? defaults.Number;
|
||||
MinNumber = MinNumber ?? defaults.MinNumber;
|
||||
Uppercase = Uppercase ?? defaults.Uppercase;
|
||||
|
||||
@@ -4,6 +4,5 @@
|
||||
{
|
||||
public string Email { 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;
|
||||
}
|
||||
|
||||
public async Task<SsoPrevalidateResponse> PreValidateSso(string identifier)
|
||||
public async Task<object> PreValidateSso(string identifier)
|
||||
{
|
||||
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
|
||||
using (var requestMessage = new HttpRequestMessage())
|
||||
@@ -571,8 +571,7 @@ namespace Bit.Core.Services
|
||||
var error = await HandleErrorAsync(response, false, true);
|
||||
throw new ApiException(error);
|
||||
}
|
||||
var responseJsonString = await response.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<SsoPrevalidateResponse>(responseJsonString);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -346,7 +346,6 @@ namespace Bit.Core.Services
|
||||
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
||||
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
||||
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
||||
await _tokenService.ClearTwoFactorTokenAsync(email);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,12 +107,9 @@ namespace Bit.Core.Services
|
||||
return _decryptedFolderCache;
|
||||
}
|
||||
|
||||
public async Task<List<TreeNode<FolderView>>> GetAllNestedAsync(List<FolderView> folders = null)
|
||||
public async Task<List<TreeNode<FolderView>>> GetAllNestedAsync()
|
||||
{
|
||||
if (folders == null)
|
||||
{
|
||||
folders = await GetAllDecryptedAsync();
|
||||
}
|
||||
var folders = await GetAllDecryptedAsync();
|
||||
var nodes = new List<TreeNode<FolderView>>();
|
||||
foreach (var f in folders)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
@@ -46,12 +45,6 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -5,24 +5,13 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AppCenter;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
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;
|
||||
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,
|
||||
IDictionary<string, string> extraData = null,
|
||||
[CallerMemberName] string memberName = "",
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
#if !FDROID
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
#endif
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -22,9 +25,8 @@ namespace Bit.Core.Services
|
||||
#if !FDROID
|
||||
// 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
|
||||
Microsoft.AppCenter.Crashes.Crashes.TrackError(ex);
|
||||
Crashes.TrackError(ex);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user