1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-16 00:03:22 +00:00

Compare commits

..

6 Commits

Author SHA1 Message Date
Alison Fernandes
9cc1a501a5 Merge branch 'master' into uitests 2022-04-27 23:28:47 +01:00
Alison Fernandes
b0a8694801 Implemented AccountSwitching UI tests 2022-04-27 23:27:53 +01:00
Alison Fernandes
b9c1ab7c1d Added Id to the activity indicator 2022-04-10 00:03:40 +01:00
Alison Fernandes
4d96b091f7 Enabling screenshots temporarily. 2022-04-09 00:32:24 +01:00
Alison Fernandes
88fee155db Added UI test project 2022-04-05 00:35:48 +01:00
Alison Fernandes
f930028920 Added id's to views in preparation for UI tests 2022-04-05 00:29:35 +01:00
156 changed files with 5281 additions and 8201 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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" />

View File

@@ -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")

View File

@@ -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);

View File

@@ -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"/>

View File

@@ -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;
}
}
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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
}
}
}

View File

@@ -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;

View File

@@ -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;

View 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

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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));
}
}
}

View File

@@ -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;
}

View File

@@ -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");
}
}

View File

@@ -108,7 +108,7 @@
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
Text="&#xe5d3;"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"

View File

@@ -4,6 +4,5 @@ namespace Bit.App.Controls
{
public class ExtendedCollectionView : CollectionView
{
public string ExtraDataForLogging { get; set; }
}
}

View File

@@ -122,7 +122,7 @@
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
Text="&#xe5d3;"
IsVisible="{Binding ShowOptions, Mode=OneWay}"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"

View File

@@ -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; }

View File

@@ -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}"

View File

@@ -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}"

View File

@@ -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"

View File

@@ -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; }

View File

@@ -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>

View File

@@ -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; }

View File

@@ -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

View File

@@ -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

View File

@@ -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; }

View File

@@ -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

View File

@@ -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; }

View File

@@ -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)

View File

@@ -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

View File

@@ -70,6 +70,7 @@ namespace Bit.App.Pages
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.Center,
Color = ThemeManager.GetResourceColor("PrimaryColor"),
AutomationId = "activity_indicator"
};
if (targetView != null)
{

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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="!@#$%^&amp;*"
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>

View File

@@ -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;

View File

@@ -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}"

View File

@@ -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())

View File

@@ -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()
{

View File

@@ -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>

View File

@@ -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

View File

@@ -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}"

View File

@@ -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()
{

View File

@@ -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

View File

@@ -113,7 +113,6 @@
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform"
ExtraDataForLogging="Settings Page" />
StyleClass="list, list-platform" />
</pages:BaseContentPage>

View File

@@ -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;

View File

@@ -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}";
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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"

View File

@@ -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;

View File

@@ -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()
{

View File

@@ -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>

View File

@@ -33,9 +33,7 @@
Text="&#xe5c4;"
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

View File

@@ -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)
{

View File

@@ -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())

View File

@@ -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>

View File

@@ -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));
}
}

View File

@@ -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())

View File

@@ -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

View File

@@ -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));
}
}
}

View File

@@ -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();

View File

@@ -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"

View File

@@ -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
{

View File

@@ -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();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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 (!@#$%^&amp;*)</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>

View File

@@ -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();

View File

@@ -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(

View File

@@ -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">

View File

@@ -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();
});
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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.

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -111,6 +111,5 @@
public const string EyeSlash = "\xe96d";
public const string File = "\xe96e";
public const string Paste = "\xe96f";
public const string ViewCellMenu = "\xe5d3";
}
}

View File

@@ -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";

View File

@@ -1,13 +0,0 @@
namespace Bit.Core.Enums
{
public enum NavigationTarget
{
HomeLogin,
Login,
Lock,
Home,
AddEditCipher,
AutofillCiphers,
SendAddEdit
}
}

View File

@@ -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;

View File

@@ -4,6 +4,5 @@
{
public string Email { get; set; }
public string MasterPasswordHash { get; set; }
public string DeviceIdentifier { get; set; }
}
}

View File

@@ -1,7 +0,0 @@
namespace Bit.Core.Models.Response
{
public class SsoPrevalidateResponse
{
public string Token { get; set; }
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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 = "",

View File

@@ -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