mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d224f5e22 | ||
|
|
4831097c0b | ||
|
|
72f2983885 | ||
|
|
ee68dcf3b0 | ||
|
|
daab473cbb | ||
|
|
e6b99270cf | ||
|
|
a8d9aaa7fe | ||
|
|
542ef5f31a | ||
|
|
0b2fc2a647 | ||
|
|
a95fdf67a1 | ||
|
|
73890162bf | ||
|
|
ef14a8f850 | ||
|
|
51a5f58258 | ||
|
|
388ad4e840 | ||
|
|
48a8d9ae35 | ||
|
|
dd6003bd4f | ||
|
|
fba407f3b6 | ||
|
|
88b406544b | ||
|
|
3438ed94ce | ||
|
|
ec71b21264 | ||
|
|
b223f5f16e | ||
|
|
0a64e4c918 | ||
|
|
9b41db962e | ||
|
|
43d3c7b5d7 | ||
|
|
8168089591 | ||
|
|
6b55fc3032 | ||
|
|
87ab42b155 | ||
|
|
98130e89de | ||
|
|
121f0e3628 | ||
|
|
8a3d88b3ce | ||
|
|
b8b41fe847 | ||
|
|
5bbef3ee16 | ||
|
|
9a2b6c8ec9 | ||
|
|
5272c99643 | ||
|
|
43e9515a03 | ||
|
|
7e9b7398c8 | ||
|
|
58d7b001a5 | ||
|
|
a259560d29 | ||
|
|
22c746543a | ||
|
|
bcbc2738ca | ||
|
|
604e3b6892 | ||
|
|
b081a8c634 | ||
|
|
c251b950d1 | ||
|
|
1bbe8d8b98 | ||
|
|
cdc41d3bef | ||
|
|
769851adfe | ||
|
|
c988175e50 | ||
|
|
04539af2a6 | ||
|
|
e0efcfbe45 | ||
|
|
57213607a7 | ||
|
|
2cab62fda5 | ||
|
|
99828c7ead | ||
|
|
ab6dde4a11 | ||
|
|
80bd8ba9d1 | ||
|
|
35853a3815 | ||
|
|
cfbbea59e9 | ||
|
|
14d4b2ee29 | ||
|
|
b6ad3527db |
12
.config/dotnet-tools.json
Normal file
12
.config/dotnet-tools.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-format": {
|
||||
"version": "5.1.250801",
|
||||
"commands": [
|
||||
"dotnet-format"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ root = true
|
||||
# Don't use tabs for indentation.
|
||||
[*]
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
# (Please don't specify an indent_size here; that has too many unintended consequences.)
|
||||
|
||||
# Code files
|
||||
|
||||
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
||||
# .NET format https://github.com/bitwarden/mobile/pull/1738
|
||||
04539af2a66668b6e85476d5cf318c9150ec4357
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,7 +1,7 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
* text=auto eol=lf
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
|
||||
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -21,12 +21,8 @@
|
||||
|
||||
|
||||
|
||||
## 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)
|
||||
|
||||
37
.github/workflows/build.yml
vendored
37
.github/workflows/build.yml
vendored
@@ -60,6 +60,11 @@ 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
|
||||
|
||||
@@ -105,6 +110,14 @@ jobs:
|
||||
- name: Restore packages
|
||||
run: nuget restore
|
||||
|
||||
- name: Restore tools
|
||||
run: dotnet tool restore
|
||||
shell: pwsh
|
||||
|
||||
- name: Verify Format
|
||||
run: dotnet tool run dotnet-format --check
|
||||
shell: pwsh
|
||||
|
||||
- name: Run Core tests
|
||||
run: dotnet test test/Core.Test/Core.Test.csproj
|
||||
|
||||
@@ -201,6 +214,11 @@ 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
|
||||
|
||||
@@ -296,18 +314,6 @@ 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 "########################################"
|
||||
@@ -372,6 +378,11 @@ 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
|
||||
@@ -544,7 +555,7 @@ jobs:
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
env:
|
||||
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
|
||||
run: appcenter crashes upload-symbols -a kspearrin/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
||||
run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
||||
shell: bash
|
||||
|
||||
- name: Deploy to App Store
|
||||
|
||||
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@@ -34,29 +34,13 @@ jobs:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
|
||||
- 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: 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: Get branch name
|
||||
id: branch
|
||||
@@ -83,8 +67,8 @@ jobs:
|
||||
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||
./Bitwarden iOS.zip"
|
||||
commit: ${{ github.sha }}
|
||||
tag: v${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||
name: Version ${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||
tag: v${{ steps.version.outputs.version }}
|
||||
name: Version ${{ steps.version.outputs.version }}
|
||||
body: "<insert release notes here>"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
|
||||
@@ -14,7 +14,7 @@ Here is how you can get involved:
|
||||
|
||||
* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||
|
||||
* **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
* **Help other users:** Go to the [Ask the Bitwarden Community category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
|
||||
* **Translate:** See the localization (i10n) section below
|
||||
|
||||
@@ -35,6 +35,6 @@ We use a translation tool called [Crowdin](https://crowdin.com) to help manage o
|
||||
|
||||
If you are interested in helping translate the Bitwarden mobile app into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-mobile
|
||||
|
||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/kspearrin).
|
||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/dwbit).
|
||||
|
||||
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/
|
||||
|
||||
20
README.md
20
README.md
@@ -33,3 +33,23 @@ Code contributions are welcome! Visual Studio with Xamarin is required to work o
|
||||
Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
|
||||
|
||||
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
|
||||
|
||||
### Dotnet-format
|
||||
|
||||
We recently migrated to using dotnet-format as code formatter. All previous branches will need to updated to avoid large merge conflicts using the following steps:
|
||||
|
||||
1. Check out your local Branch
|
||||
2. Run `git merge e0efcfbe45b2a27c73e9593bfd7a71fad2aa7a35`
|
||||
3. Resolve any merge conflicts, commit.
|
||||
4. Run `dotnet tool run dotnet-format`
|
||||
5. Commit
|
||||
6. Run `git merge -Xours 04539af2a66668b6e85476d5cf318c9150ec4357`
|
||||
7. Push
|
||||
|
||||
#### Git blame
|
||||
|
||||
We also recommend that you configure git to ignore the prettier revision using:
|
||||
|
||||
```bash
|
||||
git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
```
|
||||
|
||||
@@ -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.1</Version>
|
||||
<Version>1.7.3</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" />
|
||||
|
||||
@@ -69,10 +69,7 @@ namespace Bit.Droid
|
||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||
}
|
||||
|
||||
#if !DEBUG && !FDROID
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||
var appCenterTask = appCenterHelper.InitAsync();
|
||||
#endif
|
||||
ServiceContainer.Resolve<ILogger>("logger").InitAsync();
|
||||
|
||||
var toplayout = Window?.DecorView?.RootView;
|
||||
if (toplayout != null)
|
||||
@@ -85,6 +82,7 @@ namespace Bit.Droid
|
||||
_appOptions = GetOptions();
|
||||
LoadApplication(new App.App(_appOptions));
|
||||
|
||||
|
||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||
{
|
||||
if (message.Command == "startEventTimer")
|
||||
|
||||
@@ -12,7 +12,6 @@ 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;
|
||||
@@ -20,6 +19,7 @@ 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,6 +62,15 @@ 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)
|
||||
@@ -121,13 +130,14 @@ namespace Bit.Droid
|
||||
var secureStorageService = new SecureStorageService();
|
||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||
var stateService = new StateService(mobileStorageService, secureStorageService, messagingService);
|
||||
var stateMigrationService =
|
||||
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
||||
var deviceActionService = new DeviceActionService(stateService, messagingService,
|
||||
var clipboardService = new ClipboardService(stateService);
|
||||
var deviceActionService = new DeviceActionService(clipboardService, stateService, messagingService,
|
||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||
broadcasterService);
|
||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
||||
messagingService, broadcasterService);
|
||||
var biometricService = new BiometricService();
|
||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||
@@ -142,7 +152,7 @@ namespace Bit.Droid
|
||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||
ServiceContainer.Register<IStateService>("stateService", stateService);
|
||||
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
|
||||
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
|
||||
ServiceContainer.Register<IClipboardService>("clipboardService", clipboardService);
|
||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.17.1" 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="2022.6.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||
|
||||
|
||||
31
src/Android/Renderers/CustomPageRenderer.cs
Normal file
31
src/Android/Renderers/CustomPageRenderer.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using AndroidX.AppCompat.Widget;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Droid.Renderers;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))]
|
||||
namespace Bit.Droid.Renderers
|
||||
{
|
||||
public class CustomPageRenderer : PageRenderer
|
||||
{
|
||||
public CustomPageRenderer(Context context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
Activity context = (Activity)this.Context;
|
||||
var toolbar = context.FindViewById<Toolbar>(Resource.Id.toolbar);
|
||||
if(toolbar != null)
|
||||
{
|
||||
toolbar.NavigationContentDescription = AppResources.TapToGoBack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -2,4 +2,5 @@
|
||||
<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>
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
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;
|
||||
|
||||
@@ -74,6 +73,7 @@ namespace Bit.Droid.Services
|
||||
catch (InvalidKeyException e)
|
||||
{
|
||||
// Fallback for old bitwarden users without a key
|
||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||
CreateKey();
|
||||
}
|
||||
|
||||
@@ -94,10 +94,11 @@ namespace Bit.Droid.Services
|
||||
keyGen.Init(keyGenSpec);
|
||||
keyGen.GenerateKey();
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
// Catch silently to allow biometrics to function on devices that are in a state where key generation
|
||||
// is not functioning
|
||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Bit.Core;
|
||||
using Android.OS;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Droid.Receivers;
|
||||
using Plugin.CurrentActivity;
|
||||
@@ -26,13 +26,41 @@ namespace Bit.Droid.Services
|
||||
PendingIntentFlags.UpdateCurrent));
|
||||
}
|
||||
|
||||
public async Task CopyTextAsync(string text, int expiresInMs = -1)
|
||||
public async Task CopyTextAsync(string text, int expiresInMs = -1, bool isSensitive = true)
|
||||
{
|
||||
await Clipboard.SetTextAsync(text);
|
||||
// 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 ClearClipboardAlarmAsync(expiresInMs);
|
||||
}
|
||||
|
||||
public bool IsCopyNotificationHandledByPlatform()
|
||||
{
|
||||
// Android 13+ provides built-in notification when text is copied to the clipboard
|
||||
return (int)Build.VERSION.SdkInt >= 33;
|
||||
}
|
||||
|
||||
private void CopyToClipboard(string text, bool isSensitive = true)
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var clipboardManager = activity.GetSystemService(
|
||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||
var clipData = ClipData.NewPlainText("bitwarden", text);
|
||||
if (isSensitive)
|
||||
{
|
||||
clipData.Description.Extras ??= new PersistableBundle();
|
||||
clipData.Description.Extras.PutBoolean("android.content.extra.IS_SENSITIVE", true);
|
||||
}
|
||||
clipboardManager.PrimaryClip = clipData;
|
||||
}
|
||||
|
||||
private async Task ClearClipboardAlarmAsync(int expiresInMs = -1)
|
||||
{
|
||||
var clearMs = expiresInMs;
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class DeviceActionService : IDeviceActionService
|
||||
{
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
@@ -47,11 +48,13 @@ 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;
|
||||
@@ -929,20 +932,12 @@ namespace Bit.Droid.Services
|
||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||
if (totp != null)
|
||||
{
|
||||
CopyToClipboard(totp);
|
||||
await _clipboardService.CopyTextAsync(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;
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
#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
|
||||
12
src/App/Abstractions/IAccountsManager.cs
Normal file
12
src/App/Abstractions/IAccountsManager.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
14
src/App/Abstractions/IAccountsManagerHost.cs
Normal file
14
src/App/Abstractions/IAccountsManagerHost.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Bit.Core.Enums;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.View;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Bit.App.Abstractions
|
||||
string[] ProtectedFields { get; }
|
||||
|
||||
Task<bool> ShowPasswordPromptAsync();
|
||||
|
||||
|
||||
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
||||
|
||||
Task<bool> Enabled();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
|
||||
@@ -13,13 +13,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.5" />
|
||||
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.0" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.2" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.3" />
|
||||
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2478" />
|
||||
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
||||
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
@@ -129,6 +128,7 @@
|
||||
<Folder Include="Resources\" />
|
||||
<Folder Include="Behaviors\" />
|
||||
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
||||
<Folder Include="Utilities\AccountManagement\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -421,5 +421,6 @@
|
||||
<None Remove="Behaviors\" />
|
||||
<None Remove="Xamarin.CommunityToolkit" />
|
||||
<None Remove="Controls\AccountSwitchingOverlay\" />
|
||||
<None Remove="Utilities\AccountManagement\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
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;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
namespace Bit.App
|
||||
{
|
||||
public partial class App : Application
|
||||
public partial class App : Application, IAccountsManagerHost
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
@@ -27,6 +28,7 @@ namespace Bit.App
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAccountsManager _accountsManager;
|
||||
|
||||
private static bool _isResumed;
|
||||
|
||||
@@ -47,6 +49,9 @@ 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) =>
|
||||
@@ -71,30 +76,6 @@ 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)
|
||||
@@ -109,22 +90,10 @@ 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 SetMainPageAsync();
|
||||
await _accountsManager.NavigateOnAccountChangeAsync();
|
||||
}
|
||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||
message.Command == "popAllAndGoToTabMyVault" ||
|
||||
@@ -168,7 +137,6 @@ namespace Bit.App
|
||||
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -208,7 +176,10 @@ namespace Bit.App
|
||||
{
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
SetTabsPageFromAutofill(isLocked);
|
||||
if (!SetTabsPageFromAutofill(isLocked))
|
||||
{
|
||||
ClearAutofillUri();
|
||||
}
|
||||
await SleptAsync();
|
||||
}
|
||||
}
|
||||
@@ -231,6 +202,7 @@ namespace Bit.App
|
||||
|
||||
private async Task ResumedAsync()
|
||||
{
|
||||
await _stateService.CheckExtensionActiveUserAndSwitchIfNeededAsync();
|
||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||
_messagingService.Send("startEventTimer");
|
||||
await UpdateThemeAsync();
|
||||
@@ -260,102 +232,6 @@ 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();
|
||||
@@ -365,7 +241,15 @@ namespace Bit.App
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTabsPageFromAutofill(bool isLocked)
|
||||
private void ClearAutofillUri()
|
||||
{
|
||||
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri))
|
||||
{
|
||||
Options.Uri = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetTabsPageFromAutofill(bool isLocked)
|
||||
{
|
||||
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) &&
|
||||
!Options.FromAutofillFramework)
|
||||
@@ -385,7 +269,9 @@ namespace Bit.App
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void Prime()
|
||||
@@ -407,7 +293,7 @@ namespace Bit.App
|
||||
UpdateThemeAsync();
|
||||
};
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
var mainPageTask = SetMainPageAsync();
|
||||
var mainPageTask = _accountsManager.NavigateOnAccountChangeAsync();
|
||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||
}
|
||||
|
||||
@@ -428,23 +314,8 @@ namespace Bit.App
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LockedAsync(string userId, bool userInitiated)
|
||||
public async Task SetPreviousPageInfoAsync()
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -470,8 +341,44 @@ namespace Bit.App
|
||||
}
|
||||
}
|
||||
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||
}
|
||||
|
||||
public void Navigate(NavigationTarget navTarget, INavigationParams navParams)
|
||||
{
|
||||
switch (navTarget)
|
||||
{
|
||||
case NavigationTarget.HomeLogin:
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
break;
|
||||
case NavigationTarget.Login:
|
||||
if (navParams is LoginNavigationParams loginParams)
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options));
|
||||
}
|
||||
break;
|
||||
case NavigationTarget.Lock:
|
||||
if (navParams is LockNavigationParams lockParams)
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||
}
|
||||
break;
|
||||
case NavigationTarget.Home:
|
||||
Current.MainPage = new TabsPage(Options);
|
||||
break;
|
||||
case NavigationTarget.AddEditCipher:
|
||||
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
|
||||
break;
|
||||
case NavigationTarget.AutofillCiphers:
|
||||
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
||||
break;
|
||||
case NavigationTarget.SendAddEdit:
|
||||
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@ 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)
|
||||
@@ -99,7 +103,7 @@ namespace Bit.App.Controls
|
||||
Opacity = 0;
|
||||
IsVisible = true;
|
||||
this.FadeTo(1, 100);
|
||||
|
||||
|
||||
if (Device.RuntimePlatform == Device.Android && MainFab != null)
|
||||
{
|
||||
// start fab fade-out
|
||||
@@ -135,6 +139,8 @@ namespace Bit.App.Controls
|
||||
|
||||
// remove overlay
|
||||
IsVisible = false;
|
||||
|
||||
AfterHide?.Invoke();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -167,7 +173,7 @@ namespace Bit.App.Controls
|
||||
|
||||
private async Task LongPressAccountAsync(AccountViewCellViewModel item)
|
||||
{
|
||||
if (!item.IsAccount)
|
||||
if (!LongPressAccountEnabled || !item.IsAccount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace Bit.App.Controls
|
||||
{
|
||||
_stateService = stateService;
|
||||
_messagingService = messagingService;
|
||||
|
||||
|
||||
SelectAccountCommand = new AsyncCommand<AccountViewCellViewModel>(SelectAccountAsync,
|
||||
onException: ex => logger.Exception(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
|
||||
LongPressAccountCommand = new AsyncCommand<Tuple<ContentPage, AccountViewCellViewModel>>(LongPressAccountAsync,
|
||||
onException: ex => logger.Exception(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
@@ -45,23 +45,28 @@ 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)
|
||||
{
|
||||
if (!item.AccountView.IsActive)
|
||||
_messagingService.Send(AccountsManagerMessageCommands.ADD_ACCOUNT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.AccountView.IsActive)
|
||||
{
|
||||
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
if (FromIOSExtension)
|
||||
{
|
||||
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||
_messagingService.Send("switchedAccount");
|
||||
}
|
||||
else if (AllowActiveAccountSelection)
|
||||
{
|
||||
_messagingService.Send("switchedAccount");
|
||||
await _stateService.SaveExtensionActiveUserIdToStorageAsync(item.AccountView.UserId);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (AllowActiveAccountSelection)
|
||||
{
|
||||
_messagingService.Send("addAccount");
|
||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Windows.Input;
|
||||
using Bit.Core.Models.View;
|
||||
using System.Windows.Input;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
|
||||
@@ -112,13 +112,13 @@ namespace Bit.App.Controls
|
||||
|
||||
private string GetFirstLetters(string data, int charCount)
|
||||
{
|
||||
var parts = data.Split();
|
||||
var parts = data.Split();
|
||||
if (parts.Length > 1 && charCount <= 2)
|
||||
{
|
||||
var text = "";
|
||||
for (int i = 0; i < charCount; i++)
|
||||
{
|
||||
text += parts[i].Substring(0,1);
|
||||
text += parts[i].Substring(0, 1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
<controls:MiButton
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Text=""
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||
Clicked="MoreButton_Clicked"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
|
||||
@@ -4,5 +4,6 @@ namespace Bit.App.Controls
|
||||
{
|
||||
public class ExtendedCollectionView : CollectionView
|
||||
{
|
||||
public string ExtraDataForLogging { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Bit.App.Controls
|
||||
{
|
||||
public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create(
|
||||
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||
|
||||
|
||||
public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create(
|
||||
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Bit.App.Controls
|
||||
get => (Color)GetValue(StepperBackgroundColorProperty);
|
||||
set => SetValue(StepperBackgroundColorProperty, value);
|
||||
}
|
||||
|
||||
|
||||
public Color StepperForegroundColor
|
||||
{
|
||||
get => (Color)GetValue(StepperForegroundColorProperty);
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<controls:MiButton
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Text=""
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
||||
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||
Clicked="MoreButton_Clicked"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -13,7 +13,7 @@ namespace Bit.App.Controls
|
||||
|
||||
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
||||
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
|
||||
|
||||
|
||||
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
|
||||
nameof(ShowOptions), typeof(bool), typeof(SendViewCell), true, BindingMode.OneWay);
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ namespace Bit.App.Effects
|
||||
{
|
||||
public class FabShadowEffect : RoutingEffect
|
||||
{
|
||||
public FabShadowEffect()
|
||||
: base("Bitwarden.FabShadowEffect")
|
||||
public FabShadowEffect()
|
||||
: base("Bitwarden.FabShadowEffect")
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -46,7 +46,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get => _showPassword;
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
}
|
||||
|
||||
public bool IsPolicyInEffect
|
||||
@@ -66,8 +70,9 @@ namespace Bit.App.Pages
|
||||
get => _policy;
|
||||
set => SetProperty(ref _policy, value);
|
||||
}
|
||||
|
||||
|
||||
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; }
|
||||
@@ -84,7 +89,7 @@ namespace Bit.App.Pages
|
||||
await CheckPasswordPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task CheckPasswordPolicy()
|
||||
{
|
||||
Policy = await _policyService.GetMasterPasswordPolicyOptions();
|
||||
@@ -166,7 +171,7 @@ namespace Bit.App.Pages
|
||||
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Bit.App.Resources;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
||||
}
|
||||
|
||||
|
||||
private void Cancel_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
@@ -117,7 +117,7 @@ namespace Bit.App.Pages
|
||||
_vm.StartRegisterAction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task StartRegisterAsync()
|
||||
{
|
||||
var page = new RegisterPage(this);
|
||||
@@ -145,7 +145,7 @@ namespace Bit.App.Pages
|
||||
_vm.StartEnvironmentAction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task StartEnvironmentAsync()
|
||||
{
|
||||
await _accountListOverlay.HideAsync();
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="_passwordGrid"
|
||||
@@ -119,7 +120,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
</Grid>
|
||||
<StackLayout
|
||||
StyleClass="box-row"
|
||||
|
||||
@@ -72,7 +72,8 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon)
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -128,6 +129,7 @@ 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; }
|
||||
@@ -272,7 +274,7 @@ namespace Bit.App.Pages
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||
var passwordValid = false;
|
||||
|
||||
|
||||
if (storedKeyHash != null)
|
||||
{
|
||||
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
|
||||
|
||||
@@ -101,7 +101,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
<StackLayout Padding="10, 0">
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Bit.App.Pages
|
||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||
}
|
||||
|
||||
|
||||
private async Task UpdateTempPasswordAsync()
|
||||
{
|
||||
var page = new UpdateTempPasswordPage();
|
||||
|
||||
@@ -58,7 +58,8 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon)
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ namespace Bit.App.Pages
|
||||
get => _showCancelButton;
|
||||
set => SetProperty(ref _showCancelButton, value);
|
||||
}
|
||||
|
||||
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
@@ -85,6 +86,7 @@ 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; }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Bit.App.Models;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -99,7 +99,7 @@ namespace Bit.App.Pages
|
||||
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
|
||||
private async Task UpdateTempPasswordAsync()
|
||||
{
|
||||
var page = new UpdateTempPasswordPage();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -81,10 +81,12 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||
string ssoToken;
|
||||
|
||||
try
|
||||
{
|
||||
await _apiService.PreValidateSso(OrgIdentifier);
|
||||
var response = await _apiService.PreValidateSso(OrgIdentifier);
|
||||
ssoToken = response.Token;
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
@@ -112,7 +114,8 @@ 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);
|
||||
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier) + "&" +
|
||||
"ssoToken=" + Uri.EscapeDataString(ssoToken);
|
||||
|
||||
WebAuthenticatorResult authResult = null;
|
||||
try
|
||||
@@ -170,7 +173,7 @@ namespace Bit.App.Pages
|
||||
else if (response.ResetMasterPassword)
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
}
|
||||
}
|
||||
else if (response.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
|
||||
@@ -68,7 +68,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
</Grid>
|
||||
<Label
|
||||
Text="{u:I18n MasterPasswordDescription}"
|
||||
@@ -106,7 +107,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
</Grid>
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Bit.App.Pages
|
||||
await _vm.SubmitAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task RegistrationSuccessAsync(HomePage homePage)
|
||||
{
|
||||
if (homePage != null)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
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.Request;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.Core;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -39,7 +39,7 @@ namespace Bit.App.Pages
|
||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||
ShowTerms = !_platformUtilsService.IsSelfHost();
|
||||
}
|
||||
|
||||
|
||||
public ICommand PoliciesClickCommand => new Command<string>((url) =>
|
||||
{
|
||||
_platformUtilsService.LaunchUri(url);
|
||||
@@ -51,28 +51,30 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon)
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public bool AcceptPolicies
|
||||
{
|
||||
get => _acceptPolicies;
|
||||
set => SetProperty(ref _acceptPolicies, value);
|
||||
}
|
||||
|
||||
|
||||
public Thickness SwitchMargin
|
||||
{
|
||||
get => Device.RuntimePlatform == Device.Android
|
||||
? new Thickness(0, 0, 0, 0)
|
||||
get => Device.RuntimePlatform == Device.Android
|
||||
? new Thickness(0, 0, 0, 0)
|
||||
: new Thickness(0, 0, 10, 0);
|
||||
}
|
||||
|
||||
|
||||
public bool ShowTerms { get; set; }
|
||||
public Command SubmitCommand { get; }
|
||||
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; }
|
||||
@@ -136,7 +138,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
// TODO: Password strength check?
|
||||
|
||||
|
||||
if (showLoading)
|
||||
{
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||
|
||||
@@ -107,7 +107,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
</Grid>
|
||||
<Label
|
||||
Text="{u:I18n MasterPasswordDescription}"
|
||||
@@ -145,7 +146,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
</Grid>
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
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.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -55,7 +55,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get => _showPassword;
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
}
|
||||
|
||||
public bool IsPolicyInEffect
|
||||
@@ -63,7 +67,7 @@ namespace Bit.App.Pages
|
||||
get => _isPolicyInEffect;
|
||||
set => SetProperty(ref _isPolicyInEffect, value);
|
||||
}
|
||||
|
||||
|
||||
public bool ResetPasswordAutoEnroll
|
||||
{
|
||||
get => _resetPasswordAutoEnroll;
|
||||
@@ -86,6 +90,7 @@ 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; }
|
||||
@@ -214,13 +219,14 @@ namespace Bit.App.Pages
|
||||
// Request
|
||||
var resetRequest = new OrganizationUserResetPasswordEnrollmentRequest
|
||||
{
|
||||
ResetPasswordKey = encryptedKey.EncryptedString
|
||||
ResetPasswordKey = encryptedKey.EncryptedString,
|
||||
MasterPasswordHash = masterPasswordHash,
|
||||
};
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
// Enroll user
|
||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||
}
|
||||
|
||||
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
SetPasswordSuccessAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Bit.App.Controls;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -47,7 +47,8 @@ namespace Bit.App.Pages
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
ToolbarItems.Add(_moreItem);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolbarItems.Add(_useAnotherTwoStepMethod);
|
||||
}
|
||||
@@ -92,7 +93,8 @@ namespace Bit.App.Pages
|
||||
if (_vm.TotpMethod)
|
||||
{
|
||||
RequestFocus(_totpEntry);
|
||||
} else if (_vm.YubikeyMethod)
|
||||
}
|
||||
else if (_vm.YubikeyMethod)
|
||||
{
|
||||
RequestFocus(_yubikeyTokenEntry);
|
||||
}
|
||||
@@ -187,7 +189,7 @@ namespace Bit.App.Pages
|
||||
var page = new SetPasswordPage(_appOptions, _orgIdentifier);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
|
||||
private async Task UpdateTempPasswordAsync()
|
||||
{
|
||||
var page = new UpdateTempPasswordPage();
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
@@ -29,6 +29,7 @@ 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;
|
||||
@@ -49,6 +50,7 @@ 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());
|
||||
@@ -113,7 +115,7 @@ namespace Bit.App.Pages
|
||||
public Action StartSetPasswordAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
|
||||
|
||||
protected override II18nService i18nService => _i18nService;
|
||||
protected override IEnvironmentService environmentService => _environmentService;
|
||||
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||
@@ -293,7 +295,7 @@ namespace Bit.App.Pages
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
||||
}
|
||||
var result = await _authService.LogInTwoFactorAsync(SelectedProviderType.Value, Token, _captchaToken, Remember);
|
||||
|
||||
|
||||
if (result.CaptchaNeeded)
|
||||
{
|
||||
if (await HandleCaptchaAsync(result.CaptchaSiteKey))
|
||||
@@ -304,12 +306,12 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
_captchaToken = null;
|
||||
|
||||
|
||||
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
_messagingService.Send("listenYubiKeyOTP", false);
|
||||
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
||||
|
||||
|
||||
if (_authingWithSso && result.ResetMasterPassword)
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
@@ -317,7 +319,7 @@ namespace Bit.App.Pages
|
||||
else if (result.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TwoFactorAuthSuccessAction?.Invoke();
|
||||
@@ -380,7 +382,8 @@ namespace Bit.App.Pages
|
||||
var request = new TwoFactorEmailRequest
|
||||
{
|
||||
Email = _authService.Email,
|
||||
MasterPasswordHash = _authService.MasterPasswordHash
|
||||
MasterPasswordHash = _authService.MasterPasswordHash,
|
||||
DeviceIdentifier = await _appIdService.GetAppIdAsync()
|
||||
};
|
||||
await _apiService.PostTwoFactorEmailAsync(request);
|
||||
if (showLoading)
|
||||
|
||||
@@ -105,7 +105,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box">
|
||||
@@ -140,7 +141,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
</Grid>
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using Bit.App.Resources;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -14,29 +14,29 @@ namespace Bit.App.Pages
|
||||
private readonly string _pageName;
|
||||
|
||||
public UpdateTempPasswordPage()
|
||||
{
|
||||
{
|
||||
// Service Init
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
|
||||
|
||||
// Binding
|
||||
InitializeComponent();
|
||||
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
||||
_vm = BindingContext as UpdateTempPasswordPageViewModel;
|
||||
_vm.Page = this;
|
||||
SetActivityIndicator();
|
||||
|
||||
|
||||
// Actions Declaration
|
||||
_vm.LogOutAction = () =>
|
||||
{
|
||||
_messagingService.Send("logout");
|
||||
};
|
||||
_vm.UpdateTempPasswordSuccessAction = () => Device.BeginInvokeOnMainThread(UpdateTempPasswordSuccess);
|
||||
|
||||
|
||||
// Link fields that will be referenced in codebehind
|
||||
MasterPasswordEntry = _masterPassword;
|
||||
ConfirmMasterPasswordEntry = _confirmMasterPassword;
|
||||
|
||||
|
||||
// Return Types and Commands
|
||||
_masterPassword.ReturnType = ReturnType.Next;
|
||||
_masterPassword.ReturnCommand = new Command(() => _confirmMasterPassword.Focus());
|
||||
@@ -77,7 +77,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void UpdateTempPasswordSuccess()
|
||||
{
|
||||
_messagingService.Send("logout");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Bit.App.Resources;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Request;
|
||||
using Xamarin.Forms;
|
||||
@@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
||||
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||
}
|
||||
|
||||
|
||||
public Command SubmitCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public Command ToggleConfirmPasswordCommand { get; }
|
||||
@@ -37,23 +37,23 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task SubmitAsync()
|
||||
{
|
||||
if (!await ValidateMasterPasswordAsync())
|
||||
if (!await ValidateMasterPasswordAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Retrieve details for key generation
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
|
||||
|
||||
// Create new key and hash new password
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||
|
||||
|
||||
// Create new encKey for the User
|
||||
var newEncKey = await _cryptoService.RemakeEncKeyAsync(key);
|
||||
|
||||
|
||||
// Create request
|
||||
var request = new UpdateTempPasswordRequest
|
||||
{
|
||||
@@ -61,7 +61,7 @@ namespace Bit.App.Pages
|
||||
NewMasterPasswordHash = masterPasswordHash,
|
||||
MasterPasswordHint = Hint
|
||||
};
|
||||
|
||||
|
||||
// Initiate API action
|
||||
try
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Bit.App.Pages
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
PageTitle = AppResources.VerificationCode;
|
||||
|
||||
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
MainActionCommand = new AsyncCommand(MainActionAsync, allowsMultipleExecutions: false);
|
||||
RequestOTPCommand = new AsyncCommand(RequestOTPAsync, allowsMultipleExecutions: false);
|
||||
@@ -60,7 +60,7 @@ namespace Bit.App.Pages
|
||||
get => _mainActionText;
|
||||
set => SetProperty(ref _mainActionText, value);
|
||||
}
|
||||
|
||||
|
||||
public string SendCodeStatus
|
||||
{
|
||||
get => _sendCodeStatus;
|
||||
|
||||
@@ -35,12 +35,12 @@ namespace Bit.App.Pages
|
||||
protected async override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
|
||||
|
||||
if (IsThemeDirty)
|
||||
{
|
||||
UpdateOnThemeChanged();
|
||||
}
|
||||
|
||||
|
||||
await SaveActivityAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
@@ -37,11 +37,14 @@ 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 = true,
|
||||
PrefersEphemeralWebBrowserSession = false,
|
||||
};
|
||||
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@
|
||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||
ItemsSource="{Binding History}"
|
||||
VerticalOptions="FillAndExpand"
|
||||
StyleClass="list, list-platform">
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Generator History Page">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
||||
<Grid
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
base.OnAppearing();
|
||||
|
||||
await LoadOnAppearedAsync(_mainLayout, true, async () => {
|
||||
await LoadOnAppearedAsync(_mainLayout, true, async () =>
|
||||
{
|
||||
await _vm.InitAsync();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -41,8 +41,11 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
var history = await _passwordGenerationService.GetHistoryAsync();
|
||||
History.ResetWithRange(history ?? new List<GeneratedPasswordHistory>());
|
||||
ShowNoData = History.Count == 0;
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
History.ResetWithRange(history ?? new List<GeneratedPasswordHistory>());
|
||||
ShowNoData = History.Count == 0;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ClearAsync()
|
||||
@@ -55,8 +58,7 @@ namespace Bit.App.Pages
|
||||
private async void CopyAsync(GeneratedPasswordHistory ph)
|
||||
{
|
||||
await _clipboardService.CopyTextAsync(ph.Password);
|
||||
_platformUtilsService.ShowToast("info", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||
}
|
||||
|
||||
public async Task UpdateOnThemeChanged()
|
||||
|
||||
@@ -183,52 +183,68 @@
|
||||
<Label
|
||||
Text="A-Z"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
||||
<Switch
|
||||
IsToggled="{Binding Uppercase}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End" />
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
<Label
|
||||
Text="a-z"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}"/>
|
||||
<Switch
|
||||
IsToggled="{Binding Lowercase}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End" />
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n LowercaseAtoZ}" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
<Label
|
||||
Text="0-9"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
||||
<Switch
|
||||
IsToggled="{Binding Number}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End" />
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-switch">
|
||||
<Label
|
||||
Text="!@#$%^&*"
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
HorizontalOptions="StartAndExpand"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
||||
<Switch
|
||||
IsToggled="{Binding Special}"
|
||||
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
||||
Converter={StaticResource inverseBool}}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End" />
|
||||
HorizontalOptions="End"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
<StackLayout StyleClass="box-row, box-row-stepper">
|
||||
@@ -277,7 +293,7 @@
|
||||
StyleClass="box-label-regular"
|
||||
HorizontalOptions="StartAndExpand" />
|
||||
<Switch
|
||||
IsToggled="{Binding AvoidAmbiguous}"
|
||||
IsToggled="{Binding AvoidAmbiguousChars}"
|
||||
StyleClass="box-value"
|
||||
HorizontalOptions="End" />
|
||||
</StackLayout>
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Bit.App.Pages
|
||||
public partial class GeneratorPage : BaseContentPage, IThemeDirtablePage
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
|
||||
|
||||
private GeneratorPageViewModel _vm;
|
||||
private readonly bool _fromTabPage;
|
||||
private readonly Action<string> _selectAction;
|
||||
@@ -77,7 +77,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using Bit.App.Resources;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
||||
private bool _lowercase;
|
||||
private bool _number;
|
||||
private bool _special;
|
||||
private bool _avoidAmbiguous;
|
||||
private bool _allowAmbiguousChars;
|
||||
private int _minNumber;
|
||||
private int _minSpecial;
|
||||
private int _length = 5;
|
||||
@@ -130,19 +129,29 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public bool AvoidAmbiguous
|
||||
public bool AllowAmbiguousChars
|
||||
{
|
||||
get => _avoidAmbiguous;
|
||||
get => _allowAmbiguousChars;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _avoidAmbiguous, value))
|
||||
if (SetProperty(ref _allowAmbiguousChars, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(AvoidAmbiguousChars)
|
||||
}))
|
||||
{
|
||||
_options.Ambiguous = !value;
|
||||
_options.AllowAmbiguousChar = value;
|
||||
var task = SaveOptionsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AvoidAmbiguousChars
|
||||
{
|
||||
get => !AllowAmbiguousChars;
|
||||
set => AllowAmbiguousChars = !value;
|
||||
}
|
||||
|
||||
public int MinNumber
|
||||
{
|
||||
get => _minNumber;
|
||||
@@ -225,7 +234,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public PasswordGeneratorPolicyOptions EnforcedPolicyOptions
|
||||
{
|
||||
get => _enforcedPolicyOptions;
|
||||
@@ -309,13 +318,12 @@ namespace Bit.App.Pages
|
||||
public async Task CopyAsync()
|
||||
{
|
||||
await _clipboardService.CopyTextAsync(Password);
|
||||
_platformUtilsService.ShowToast("success", null,
|
||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||
}
|
||||
|
||||
private void LoadFromOptions()
|
||||
{
|
||||
AvoidAmbiguous = !_options.Ambiguous.GetValueOrDefault();
|
||||
AllowAmbiguousChars = _options.AllowAmbiguousChar.GetValueOrDefault();
|
||||
TypeSelectedIndex = _options.Type == "passphrase" ? 1 : 0;
|
||||
IsPassword = TypeSelectedIndex == 0;
|
||||
MinNumber = _options.MinNumber.GetValueOrDefault();
|
||||
@@ -333,7 +341,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private void SetOptions()
|
||||
{
|
||||
_options.Ambiguous = !AvoidAmbiguous;
|
||||
_options.AllowAmbiguousChar = AllowAmbiguousChars;
|
||||
_options.Type = TypeSelectedIndex == 1 ? "passphrase" : "password";
|
||||
_options.MinNumber = MinNumber;
|
||||
_options.MinSpecial = MinSpecial;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<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"
|
||||
@@ -121,6 +122,7 @@
|
||||
Clicked="FileType_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n File}"
|
||||
AutomationProperties.HelpText="{Binding FileTypeAccessibilityLabel}"
|
||||
Grid.Column="0">
|
||||
</Button>
|
||||
<Button
|
||||
@@ -132,6 +134,7 @@
|
||||
Clicked="TextType_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Text}"
|
||||
AutomationProperties.HelpText="{Binding TextTypeAccessibilityLabel}"
|
||||
Grid.Column="1">
|
||||
</Button>
|
||||
</Grid>
|
||||
@@ -250,29 +253,33 @@
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
Orientation="Horizontal"
|
||||
Spacing="0">
|
||||
Spacing="0"
|
||||
xct:TouchEffect.Command="{Binding ToggleOptionsCommand}"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{Binding OptionsAccessilibityText}">
|
||||
<Button
|
||||
Text="{u:I18n Options}"
|
||||
x:Name="_btnOptions"
|
||||
StyleClass="box-row-button"
|
||||
TextColor="{DynamicResource PrimaryColor}"
|
||||
Margin="0" />
|
||||
Margin="0"
|
||||
AutomationProperties.IsInAccessibleTree="False"/>
|
||||
<controls:IconButton
|
||||
x:Name="_btnOptionsUp"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
|
||||
StyleClass="box-row-button"
|
||||
TextColor="{DynamicResource PrimaryColor}"
|
||||
Clicked="ToggleOptions_Clicked"
|
||||
IsVisible="False" />
|
||||
IsVisible="{Binding ShowOptions}"
|
||||
AutomationProperties.IsInAccessibleTree="False"/>
|
||||
<controls:IconButton
|
||||
x:Name="_btnOptionsDown"
|
||||
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
|
||||
StyleClass="box-row-button"
|
||||
TextColor="{DynamicResource PrimaryColor}"
|
||||
Clicked="ToggleOptions_Clicked"
|
||||
IsVisible="False" />
|
||||
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}"
|
||||
AutomationProperties.IsInAccessibleTree="False"/>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="True">
|
||||
<StackLayout IsVisible="{Binding ShowOptions}">
|
||||
<StackLayout
|
||||
StyleClass="box-row"
|
||||
Margin="0,10,0,0">
|
||||
@@ -437,7 +444,8 @@
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Margin="10,0,0,0"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||
</StackLayout>
|
||||
<Label
|
||||
Text="{u:I18n PasswordInfo}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
@@ -53,10 +53,9 @@ namespace Bit.App.Pages
|
||||
_vm.SegmentedButtonFontSize = 13;
|
||||
_vm.SegmentedButtonMargins = new Thickness(0, 10, 0, 0);
|
||||
_vm.EditorMargins = new Thickness(0, 5, 0, 0);
|
||||
// Review this when https://github.com/bitwarden/mobile/pull/1454 workaround can be reverted
|
||||
//_btnOptions.WidthRequest = 70;
|
||||
//_btnOptionsDown.WidthRequest = 30;
|
||||
//_btnOptionsUp.WidthRequest = 30;
|
||||
_btnOptions.WidthRequest = 70;
|
||||
_btnOptionsDown.WidthRequest = 30;
|
||||
_btnOptionsUp.WidthRequest = 30;
|
||||
}
|
||||
else if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
@@ -210,11 +209,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleOptions_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
_vm.ToggleOptions();
|
||||
}
|
||||
|
||||
private void ClearExpirationDate_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
@@ -338,10 +332,10 @@ namespace Bit.App.Pages
|
||||
|
||||
_vm.IsAddFromShare = true;
|
||||
_vm.CopyInsteadOfShareAfterSaving = _appOptions.CopyInsteadOfShareAfterSaving;
|
||||
|
||||
|
||||
var name = _appOptions.CreateSend.Item2;
|
||||
_vm.Send.Name = name;
|
||||
|
||||
|
||||
var type = _appOptions.CreateSend.Item1;
|
||||
if (type == SendType.File)
|
||||
{
|
||||
|
||||
@@ -40,10 +40,12 @@ namespace Bit.App.Pages
|
||||
private TimeSpan? _expirationTime;
|
||||
private bool _isOverridingPickers;
|
||||
private int? _maxAccessCount;
|
||||
private string[] _additionalSendProperties = new []
|
||||
private string[] _additionalSendProperties = new[]
|
||||
{
|
||||
nameof(IsText),
|
||||
nameof(IsFile),
|
||||
nameof(FileTypeAccessibilityLabel),
|
||||
nameof(TextTypeAccessibilityLabel)
|
||||
};
|
||||
private bool _disableHideEmail;
|
||||
private bool _sendOptionsPolicyInEffect;
|
||||
@@ -59,6 +61,7 @@ namespace Bit.App.Pages
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
ToggleOptionsCommand = new Command(ToggleOptions);
|
||||
|
||||
TypeOptions = new List<KeyValuePair<string, SendType>>
|
||||
{
|
||||
@@ -89,6 +92,7 @@ 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; }
|
||||
@@ -102,6 +106,7 @@ 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; }
|
||||
@@ -134,7 +139,11 @@ namespace Bit.App.Pages
|
||||
public bool ShowOptions
|
||||
{
|
||||
get => _showOptions;
|
||||
set => SetProperty(ref _showOptions, value);
|
||||
set => SetProperty(ref _showOptions, value,
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(OptionsAccessilibityText)
|
||||
});
|
||||
}
|
||||
public int ExpirationDateTypeSelectedIndex
|
||||
{
|
||||
@@ -209,9 +218,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get => _showPassword;
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new []
|
||||
additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowPasswordIcon)
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
}
|
||||
public bool DisableHideEmail
|
||||
@@ -231,13 +241,16 @@ 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()
|
||||
{
|
||||
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
||||
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_emailVerified = await _stateService.GetEmailVerifiedAsync();
|
||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
||||
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
||||
}
|
||||
@@ -381,7 +394,7 @@ namespace Bit.App.Pages
|
||||
|
||||
UpdateSendData();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(NewPassword))
|
||||
if (string.IsNullOrWhiteSpace(NewPassword))
|
||||
{
|
||||
NewPassword = null;
|
||||
}
|
||||
@@ -521,7 +534,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
|
||||
}
|
||||
|
||||
|
||||
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
_deviceActionService.CloseMainApp();
|
||||
|
||||
@@ -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,7 +138,8 @@
|
||||
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform" />
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Send Groupings Page" />
|
||||
</RefreshView>
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Controls;
|
||||
@@ -133,7 +133,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||
@@ -160,7 +160,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
var page = new SendsPage(_vm.Filter, _vm.Type != null);
|
||||
var page = new SendsPage(_vm.Filter, _vm.Type);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await _vaultTimeoutService.LockAsync(true, true);
|
||||
}
|
||||
|
||||
|
||||
private void About_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
_vm.ShowAbout();
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
}
|
||||
|
||||
public string Title { get; }
|
||||
public string ItemCount { get; }
|
||||
public string ItemCount { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace Bit.App.Pages
|
||||
ShowNoData = false;
|
||||
Loading = true;
|
||||
ShowList = false;
|
||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
var groupedSends = new List<SendGroupingsPageListGroup>();
|
||||
var page = Page as SendGroupingsPage;
|
||||
|
||||
|
||||
@@ -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,7 +66,8 @@
|
||||
VerticalOptions="FillAndExpand"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform">
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Sends Page">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:SendView">
|
||||
<controls:SendViewCell
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -12,15 +13,22 @@ namespace Bit.App.Pages
|
||||
private SendsPageViewModel _vm;
|
||||
private bool _hasFocused;
|
||||
|
||||
public SendsPage(Func<SendView, bool> filter, bool type = false)
|
||||
public SendsPage(Func<SendView, bool> filter, SendType? type = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as SendsPageViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.Filter = filter;
|
||||
if (type)
|
||||
if (type != null)
|
||||
{
|
||||
_vm.PageTitle = AppResources.SearchType;
|
||||
if (type == SendType.File)
|
||||
{
|
||||
_vm.PageTitle = AppResources.SearchFileSends;
|
||||
}
|
||||
else if (type == SendType.Text)
|
||||
{
|
||||
_vm.PageTitle = AppResources.SearchTextSends;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -33,6 +41,7 @@ namespace Bit.App.Pages
|
||||
_searchBar.Placeholder = AppResources.Search;
|
||||
_mainLayout.Children.Insert(0, _searchBar);
|
||||
_mainLayout.Children.Insert(1, _separator);
|
||||
ShowModalAnimationDelay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -35,11 +35,11 @@ namespace Bit.App.Pages
|
||||
get => _sendEnabled;
|
||||
set => SetProperty(ref _sendEnabled, value);
|
||||
}
|
||||
|
||||
|
||||
public bool ShowNoData
|
||||
{
|
||||
get => _showNoData;
|
||||
set => SetProperty(ref _showNoData, value, additionalPropertyNames: new []
|
||||
set => SetProperty(ref _showNoData, value, additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowSearchDirection)
|
||||
});
|
||||
@@ -48,7 +48,7 @@ namespace Bit.App.Pages
|
||||
public bool ShowList
|
||||
{
|
||||
get => _showList;
|
||||
set => SetProperty(ref _showList, value, additionalPropertyNames: new []
|
||||
set => SetProperty(ref _showList, value, additionalPropertyNames: new[]
|
||||
{
|
||||
nameof(ShowSearchDirection)
|
||||
});
|
||||
@@ -58,7 +58,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
if (!string.IsNullOrWhiteSpace((Page as SendsPage).SearchBar.Text))
|
||||
{
|
||||
Search((Page as SendsPage).SearchBar.Text, 200);
|
||||
|
||||
@@ -49,12 +49,12 @@ namespace Bit.App.Pages
|
||||
_vm.ToggleAutofillService();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ToggleInlineAutofill(object sender, EventArgs e)
|
||||
{
|
||||
_vm.ToggleInlineAutofill();
|
||||
}
|
||||
|
||||
|
||||
private void ToggleAccessibility(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
@@ -62,7 +62,7 @@ namespace Bit.App.Pages
|
||||
_vm.ToggleAccessibility();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ToggleDrawOver(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly MobileI18nService _i18nService;
|
||||
|
||||
|
||||
private bool _autofillServiceToggled;
|
||||
private bool _inlineAutofillToggled;
|
||||
private bool _accessibilityToggled;
|
||||
@@ -26,9 +26,9 @@ namespace Bit.App.Pages
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
||||
PageTitle = AppResources.AutofillServices;
|
||||
}
|
||||
|
||||
|
||||
#region Autofill Service
|
||||
|
||||
|
||||
public bool AutofillServiceVisible
|
||||
{
|
||||
get => _deviceActionService.SystemMajorVersion() >= 26;
|
||||
@@ -43,16 +43,16 @@ namespace Bit.App.Pages
|
||||
nameof(InlineAutofillEnabled)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Inline Autofill
|
||||
|
||||
public bool InlineAutofillVisible
|
||||
{
|
||||
get => _deviceActionService.SystemMajorVersion() >= 30;
|
||||
}
|
||||
|
||||
|
||||
public bool InlineAutofillEnabled
|
||||
{
|
||||
get => AutofillServiceToggled;
|
||||
@@ -69,9 +69,9 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Accessibility
|
||||
|
||||
public string AccessibilityDescriptionLabel
|
||||
@@ -97,7 +97,7 @@ namespace Bit.App.Pages
|
||||
return _i18nService.T("AccessibilityDescription4");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool AccessibilityToggled
|
||||
{
|
||||
get => _accessibilityToggled;
|
||||
@@ -109,9 +109,9 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Draw-Over
|
||||
|
||||
|
||||
public bool DrawOverVisible
|
||||
{
|
||||
get => _deviceActionService.SystemMajorVersion() >= 23;
|
||||
@@ -135,12 +135,12 @@ namespace Bit.App.Pages
|
||||
return _i18nService.T("DrawOverDescription3");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool DrawOverEnabled
|
||||
{
|
||||
get => AccessibilityToggled;
|
||||
}
|
||||
|
||||
|
||||
public bool DrawOverToggled
|
||||
{
|
||||
get => _drawOverToggled;
|
||||
@@ -148,7 +148,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||
@@ -189,7 +189,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
_deviceActionService.OpenAccessibilityOverlayPermissionSettings();
|
||||
}
|
||||
|
||||
|
||||
public void UpdateEnabled()
|
||||
{
|
||||
AutofillServiceToggled =
|
||||
@@ -197,7 +197,7 @@ namespace Bit.App.Pages
|
||||
AccessibilityToggled = _deviceActionService.AutofillAccessibilityServiceRunning();
|
||||
DrawOverToggled = _deviceActionService.AutofillAccessibilityOverlayPermitted();
|
||||
}
|
||||
|
||||
|
||||
private async Task UpdateInlineAutofillToggledAsync()
|
||||
{
|
||||
if (_inited)
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
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}"
|
||||
|
||||
@@ -4,11 +4,11 @@ using System.Text;
|
||||
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.Utilities;
|
||||
using Bit.Core;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -77,7 +77,7 @@ namespace Bit.App.Pages
|
||||
InstructionText = _i18nService.T("ExportVaultMasterPasswordDescription");
|
||||
SecretName = _i18nService.T("MasterPassword");
|
||||
}
|
||||
|
||||
|
||||
UpdateWarning();
|
||||
}
|
||||
|
||||
@@ -109,7 +109,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
get => _showPassword;
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[] {nameof(ShowPasswordIcon)});
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText),
|
||||
});
|
||||
}
|
||||
|
||||
public bool UseOTPVerification
|
||||
@@ -139,6 +143,7 @@ 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()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Bit.App.Resources;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Bit.App.Resources;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Bit.App.Resources;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
VerticalOptions="FillAndExpand"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform">
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Folders Page">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="views:FolderView">
|
||||
<controls:ExtendedStackLayout
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Bit.Core.Models.View;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Models.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Bit.App.Resources;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Bit.App.Resources;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform" />
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Settings Page" />
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Bit.App.Pages
|
||||
|
||||
async void OnTimePickerPropertyChanged(object sender, PropertyChangedEventArgs args)
|
||||
{
|
||||
var s = (TimePicker) sender;
|
||||
var s = (TimePicker)sender;
|
||||
var time = s.Time.TotalMinutes;
|
||||
if (s.IsFocused && args.PropertyName == "Time")
|
||||
{
|
||||
@@ -167,6 +167,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await _vm.UpdatePinAsync();
|
||||
}
|
||||
else if (item.Name == AppResources.SubmitCrashLogs)
|
||||
{
|
||||
await _vm.LoggerReportingAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
var biometricName = AppResources.Biometrics;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Xamarin.Forms;
|
||||
using ZXing.Client.Result;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
|
||||
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,6 +39,7 @@ 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?>>
|
||||
@@ -79,6 +80,7 @@ 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;
|
||||
@@ -123,7 +125,7 @@ namespace Bit.App.Pages
|
||||
|
||||
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
||||
!await _keyConnectorService.GetUsesKeyConnector();
|
||||
|
||||
_reportLoggingEnabled = await _loggerService.IsEnabled();
|
||||
BuildList();
|
||||
}
|
||||
|
||||
@@ -286,6 +288,26 @@ 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 =>
|
||||
@@ -428,7 +450,7 @@ namespace Bit.App.Pages
|
||||
var securityItems = new List<SettingsPageListItem>
|
||||
{
|
||||
new SettingsPageListItem { Name = AppResources.VaultTimeout, SubLabel = _vaultTimeoutDisplayValue },
|
||||
new SettingsPageListItem
|
||||
new SettingsPageListItem
|
||||
{
|
||||
Name = AppResources.VaultTimeoutAction,
|
||||
SubLabel = _vaultTimeoutActionDisplayValue
|
||||
@@ -494,11 +516,19 @@ 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 }
|
||||
};
|
||||
@@ -576,5 +606,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return _vaultTimeouts.FirstOrDefault(o => o.Key == key).Value;
|
||||
}
|
||||
|
||||
private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option;
|
||||
|
||||
private bool CompareSelection(string selection, string compareTo) => selection == compareTo || selection == $"✓ {compareTo}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Bit.App.Effects;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Effects;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
@@ -10,15 +12,18 @@ 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;
|
||||
private NavigationPage _generatorPage;
|
||||
|
||||
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
||||
{
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
|
||||
@@ -78,12 +83,26 @@ 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;
|
||||
@@ -93,7 +112,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
CurrentPage = _generatorPage;
|
||||
}
|
||||
|
||||
|
||||
public void ResetToSendPage()
|
||||
{
|
||||
CurrentPage = _sendGroupingsPage;
|
||||
@@ -131,5 +150,19 @@ namespace Bit.App.Pages
|
||||
groupingsPage.HideAccountSwitchingOverlayAsync().FireAndForget();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateVaultButtonTitleAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
var isShowingVaultFilter = await policyService.ShouldShowVaultFilterAsync();
|
||||
_groupingsPage.Title = isShowingVaultFilter ? AppResources.Vaults : AppResources.MyVault;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Value.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
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"
|
||||
@@ -732,7 +733,6 @@
|
||||
<BoxView StyleClass="box-row-separator" />
|
||||
</StackLayout>
|
||||
<controls:RepeaterView
|
||||
x:Name="_collectionsRepeaterView"
|
||||
ItemsSource="{Binding Collections}"
|
||||
IsVisible="{Binding HasCollections}">
|
||||
<controls:RepeaterView.ItemTemplate>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.PlatformConfiguration;
|
||||
@@ -30,6 +30,7 @@ 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,
|
||||
@@ -51,7 +52,7 @@ 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.CollectionsRepeaterView = _collectionsRepeaterView;
|
||||
_vm.OrganizationId = organizationId;
|
||||
_vm.Type = type;
|
||||
_vm.DefaultName = name ?? appOptions?.SaveName;
|
||||
_vm.DefaultUri = uri ?? appOptions?.Uri;
|
||||
@@ -171,7 +172,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
RequestFocus(_nameEntry);
|
||||
}
|
||||
_scrollView.Scrolled += (sender, args) => _vm.HandleScroll();
|
||||
});
|
||||
// Hide password reprompt option if using key connector
|
||||
_passwordPrompt.IsVisible = !await _keyConnectorService.GetUsesKeyConnector();
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
@@ -44,7 +43,6 @@ namespace Bit.App.Pages
|
||||
private int _ownershipSelectedIndex;
|
||||
private bool _hasCollections;
|
||||
private string _previousCipherId;
|
||||
private DateTime _lastHandledScrollTime;
|
||||
private List<Core.Models.View.CollectionView> _writeableCollections;
|
||||
private string[] _additionalCipherProperties = new string[]
|
||||
{
|
||||
@@ -67,7 +65,7 @@ namespace Bit.App.Pages
|
||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Exact, AppResources.Exact),
|
||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never)
|
||||
};
|
||||
|
||||
|
||||
public AddEditPageViewModel()
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
@@ -168,7 +166,7 @@ namespace Bit.App.Pages
|
||||
public ExtendedObservableCollection<LoginUriView> Uris { get; set; }
|
||||
public ExtendedObservableCollection<AddEditPageFieldViewModel> Fields { get; set; }
|
||||
public ExtendedObservableCollection<CollectionViewModel> Collections { get; set; }
|
||||
public RepeaterView CollectionsRepeaterView { get; set; }
|
||||
|
||||
public int TypeSelectedIndex
|
||||
{
|
||||
get => _typeSelectedIndex;
|
||||
@@ -251,7 +249,8 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showPassword, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(ShowPasswordIcon)
|
||||
nameof(ShowPasswordIcon),
|
||||
nameof(PasswordVisibilityAccessibilityText)
|
||||
});
|
||||
}
|
||||
public bool ShowCardNumber
|
||||
@@ -300,6 +299,7 @@ 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()
|
||||
{
|
||||
@@ -352,7 +352,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
Cipher.Name += " - " + AppResources.Clone;
|
||||
// If not allowing personal ownership, update cipher's org Id to prompt downstream changes
|
||||
if (Cipher.OrganizationId == null && !AllowPersonal)
|
||||
if (Cipher.OrganizationId == null && !AllowPersonal)
|
||||
{
|
||||
Cipher.OrganizationId = OrganizationId;
|
||||
}
|
||||
@@ -401,7 +401,7 @@ namespace Bit.App.Pages
|
||||
IdentityTitleOptions.FindIndex(k => k.Value == Cipher.Identity.Title);
|
||||
OwnershipSelectedIndex = string.IsNullOrWhiteSpace(Cipher.OrganizationId) ? 0 :
|
||||
OwnershipOptions.FindIndex(k => k.Value == Cipher.OrganizationId);
|
||||
|
||||
|
||||
// If the selected organization is on Index 0 and we've removed the personal option, force refresh
|
||||
if (!AllowPersonal && OwnershipSelectedIndex == 0)
|
||||
{
|
||||
@@ -453,11 +453,11 @@ namespace Bit.App.Pages
|
||||
AppResources.Ok);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ((!EditMode || CloneMode) && !AllowPersonal && string.IsNullOrWhiteSpace(Cipher.OrganizationId))
|
||||
{
|
||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||
AppResources.PersonalOwnershipSubmitError,AppResources.Ok);
|
||||
AppResources.PersonalOwnershipSubmitError, AppResources.Ok);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -537,7 +537,7 @@ namespace Bit.App.Pages
|
||||
AppResources.AnErrorHasOccurred);
|
||||
}
|
||||
}
|
||||
catch(Exception genex)
|
||||
catch (Exception genex)
|
||||
{
|
||||
_logger.Exception(genex);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
@@ -821,30 +821,13 @@ namespace Bit.App.Pages
|
||||
{
|
||||
var cols = _writeableCollections.Where(c => c.OrganizationId == Cipher.OrganizationId)
|
||||
.Select(c => new CollectionViewModel { Collection = c }).ToList();
|
||||
HasCollections = cols.Any();
|
||||
Collections.ResetWithRange(cols);
|
||||
Collections = new ExtendedObservableCollection<CollectionViewModel>(cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
HasCollections = false;
|
||||
Collections.ResetWithRange(new List<CollectionViewModel>());
|
||||
Collections = new ExtendedObservableCollection<CollectionViewModel>(new List<CollectionViewModel>());
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleScroll()
|
||||
{
|
||||
// workaround for https://github.com/xamarin/Xamarin.Forms/issues/13607
|
||||
// required for org ownership/collections to render properly in XF4.5+
|
||||
if (!HasCollections ||
|
||||
EditMode ||
|
||||
(DateTime.Now - _lastHandledScrollTime < TimeSpan.FromMilliseconds(200)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
CollectionsRepeaterView.ItemsSource = Collections;
|
||||
_lastHandledScrollTime = DateTime.Now;
|
||||
HasCollections = Collections.Any();
|
||||
}
|
||||
|
||||
private void TriggerCipherChanged()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using System;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using Bit.App.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
||||
@@ -15,7 +15,18 @@
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Icon="search.png" Clicked="Search_Clicked" />
|
||||
<controls:ExtendedToolbarItem
|
||||
x:Name="_accountAvatar"
|
||||
IconImageSource="{Binding AvatarImageSource}"
|
||||
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
|
||||
Order="Primary"
|
||||
Priority="-1"
|
||||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Account}" />
|
||||
<ToolbarItem Icon="search.png" Clicked="Search_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Search}" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ContentPage.Resources>
|
||||
@@ -58,7 +69,7 @@
|
||||
VerticalOptions="CenterAndExpand"
|
||||
Padding="20, 0"
|
||||
Spacing="20"
|
||||
IsVisible="{Binding ShowList, Converter={StaticResource inverseBool}}">
|
||||
IsVisible="{Binding ShowNoData}">
|
||||
<Label
|
||||
Text="{Binding NoDataText}"
|
||||
HorizontalTextAlignment="Center"></Label>
|
||||
@@ -74,7 +85,8 @@
|
||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="RowSelected"
|
||||
StyleClass="list, list-platform" />
|
||||
StyleClass="list, list-platform"
|
||||
ExtraDataForLogging="Autofill Ciphers Page" />
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
@@ -88,6 +100,8 @@
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<!-- Android FAB -->
|
||||
<Button
|
||||
x:Name="_fab"
|
||||
Image="plus.png"
|
||||
@@ -99,6 +113,14 @@
|
||||
<effects:FabShadowEffect />
|
||||
</Button.Effects>
|
||||
</Button>
|
||||
|
||||
<controls:AccountSwitchingOverlayView
|
||||
x:Name="_accountListOverlay"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
MainPage="{Binding Source={x:Reference _page}}"
|
||||
MainFab="{Binding Source={x:Reference _fab}, Path=.}"
|
||||
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
|
||||
</AbsoluteLayout>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user