1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-21 20:03:16 +00:00

Compare commits

..

44 Commits

Author SHA1 Message Date
Jacob Fink
a484ca633c Merge branch 'beeep-totp' of https://github.com/bitwarden/mobile into beeep-totp 2022-06-06 08:58:52 -04:00
André Bispo
655b51b6a5 Merge branch 'master' into beeep-totp
# Conflicts:
#	src/App/App.csproj
2022-06-04 18:05:14 +01:00
CarleyDiaz-Bitwarden-zz
8168089591 Updating icons for Linked and Boolean fields (#1935)
Update to icons used for Boolean and Linked to use real icons

Co-authored-by: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com>
2022-06-02 16:43:14 -04:00
github-actions[bot]
6b55fc3032 Bumped version to 2022.05.1 (#1933)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-06-02 09:30:39 -04:00
github-actions[bot]
87ab42b155 Bumped version to 2022.05.0 (#1931)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-06-01 16:18:06 -04:00
André Filipe da Silva Bispo
98130e89de PS-689 Android: Accessibility - back buttons in search and vault > bin lack appropriate accessible name (#1929)
* PS-689 Added back buttons accessibility text

* PS-689 Changed resource key from "GoBack" to "TapToGoBack"

* PS-689: class rename
2022-06-01 20:50:19 +01:00
André Filipe da Silva Bispo
121f0e3628 PS-675 Added accessibility text to password show/hide toggles (#1926)
* PS-675 Added accessibility text to password show hide toggles

* PS-675 refactor string resource key name
2022-06-01 16:02:28 +01:00
mp-bw
8a3d88b3ce [SG-79] Mobile Vault Filter (#1928)
* [SG-79] Vault Filter

* Update vault button text after sync

* formatting

* cleanup

* cleanup
2022-05-31 13:34:54 -04:00
Federico Maccaroni
b8b41fe847 [PS-536] Fix vault blank after unlocking and back navigation (#1930)
* PS-536 Fix by hack vault blank after unlocking and back navigate when previous page has value on iOS

* PS-536 Added platform check to the hack so it doesn't affect Android performance given that's an issue particular for iOS
2022-05-27 17:17:08 -04:00
André Filipe da Silva Bispo
5bbef3ee16 [PS-676] Accessibility - "Options" expand/collapse control does not announce state (#1925)
* PS-676: Accessibility - "Options" expand/collapse control does not announce state
- Moved to click event to the stacklayout
- Added accessibility text to stacklayout
- Removed accessibility on views inside stacklayout

* PS-676 Changed event to command

Co-authored-by: André Bispo <abispo@bitwarden.com>
2022-05-27 14:52:19 +01:00
Carlos Gonçalves
9a2b6c8ec9 PS-593 - View model properties are now updated on main thread (#1927) 2022-05-27 14:16:45 +01:00
André Filipe da Silva Bispo
5272c99643 PS-674: Accessibility password generator toggles (#1924)
- Additional information for VoiceOver and TalkBack
- Added new labels

Co-authored-by: André Bispo <abispo@bitwarden.com>
2022-05-25 17:25:21 +01:00
André Filipe da Silva Bispo
43e9515a03 [PS-672] Accessibility - File/Text controls unintuitive, "Text" accessible name incorrect (#1923)
* PS-672: Accessibility - File/Text controls unintuitive, "Text" accessible name incorrect
- Added new text to help identify File / Text segmented button
- Added missing text for Text button accessibility

* PS-672: refactor code with pr suggestions

* PS-672: removed unnecessary code

* PS-672: change text from "click" to "tap"

* PS-672: removed comma

Co-authored-by: André Bispo <abispo@bitwarden.com>
2022-05-25 17:20:51 +01:00
André Filipe da Silva Bispo
7e9b7398c8 PS-90: App keeps asking for 2FA even though I've checked the "remember me" checkbox (#1921)
- Logout was removing the 2FA token, portion of code deleted.
- Clear saved token when 2FA error is returned by the server.

Co-authored-by: André Bispo <abispo@bitwarden.com>
2022-05-24 15:09:24 +01:00
Jake Fink
58d7b001a5 add master password reprompt to share sheet extension (#1922) 2022-05-23 16:01:04 -04:00
André Filipe da Silva Bispo
a259560d29 PS-592 Mobile - Name field is not prioritised in search results (#1907)
- Search method now gives priority to matches in the Name property

Co-authored-by: André Bispo <abispo@bitwarden.com>
2022-05-23 10:20:48 +01:00
André Filipe da Silva Bispo
22c746543a PS-518 - Add setting to block AppCenter / Analytics - Mobile (#1905)
* PS-518 - Add setting to block AppCenter / Analytics - Mobile
- Added another entry into Settings page under the Others section
- Added prompt to ask user to enable / disable Crash Reports
- Added compilation tags to remove if the build is FDroid 

* PS-518 Add setting to block AppCenter / Analytics - Mobile
- Reduced FDroid compilation tags throughout the code
- Added Init, Enable and State methods to Logger
- Simplified SettingsPageViewModel Enable/Disable code

* PS-518 Add setting to block AppCenter / Analytics - Mobile
- Appcenter references were removed from App project, 
- Removed FDroid build.yml code that was deleting Appcenter packages from App.csproj

Co-authored-by: André Bispo <abispo@bitwarden.com>
2022-05-18 17:59:19 +01:00
André Filipe da Silva Bispo
bcbc2738ca PS-591 Fix avoid ambiguous characters #1664 (#1906)
* PS-591 - iOS - Avoid ambiguous characters is activated inside the main client, but is deactivated when creating a vault item from the autofill prompt. #1664
- Refactor the name of the property Ambiguous to AvoidAmbiguous, this naming was misleading.
- Fixed bug where the boolean value for the AvoidAmbiguous property was being stored inverted.

* PS-591 - iOS - Avoid ambiguous characters is activated inside the main client, but is deactivated when creating a vault item from the autofill prompt. #1664
- Changed AvoidAmbiguous to AllowAmbiguous
- Added wrapper Prop to bind UI Toggle to VM

Co-authored-by: André Bispo <abispo@bitwarden.com>
2022-05-18 14:58:49 +01:00
Thomas Rittson
604e3b6892 Remove testing requirements from pr template (#1901) 2022-05-10 17:02:40 -04:00
mp-bw
b081a8c634 fix a11y issue with hCaptcha on iOS (#1896) 2022-04-28 15:30:33 -04:00
Federico Maccaroni
c251b950d1 PS-77 Updated two-factor email request to include the device identifier to check whether to send the 2fa email because of a new device (#1895) 2022-04-28 10:51:13 -03:00
github-actions[bot]
1bbe8d8b98 Bumped version to 2.18.1 (#1894)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-27 09:04:29 -07:00
github-actions[bot]
cdc41d3bef Bumped version to 2.18.0 (#1893)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-27 08:24:29 -07:00
Oscar Hinton
769851adfe Update .gitattributes to fix build issue (#1892) 2022-04-26 16:51:58 -04:00
Oscar Hinton
c988175e50 [TI-8] Add .git-blame-ignore-revs (#1891) 2022-04-26 11:27:13 -04:00
Oscar Hinton
04539af2a6 Run dotnet format (#1738) 2022-04-26 17:21:17 +02:00
Oscar Hinton
e0efcfbe45 Add dotnet-format tool (#1737) 2022-04-26 17:21:07 +02:00
Federico Maccaroni
57213607a7 PS-291 Fix password history to update the collection on the main thread to load correctly (#1890) 2022-04-25 17:43:55 -04:00
Federico Maccaroni
2cab62fda5 TDL-13 Removed workaround of null reference on LabelRenderer given that Xamarin forms has been updated with the fix (#1889) 2022-04-25 16:09:47 -03:00
sneakernuts
99828c7ead Switched org (#1887) 2022-04-22 13:35:19 +00:00
mp-bw
ab6dde4a11 update XF and revert old workarounds (#1885) 2022-04-21 21:29:47 -04:00
dwbit
80bd8ba9d1 Change source string 'copy notes' to 'copy note' (#1881)
Change the value for 'copy notes' to 'copy note' since it is applying a single action on 1 item. This source string is already 'copy note' in the browser extension.
2022-04-21 08:03:21 -04:00
dwbit
35853a3815 Contribution Documentation edits (#1880)
* Update crowdin manager and forum category

Updating Crowdin contact from Kyle to dwbit. Update 'User-to-User Support' forum category to 'Ask the Bitwarden Community'

* Text corrections

Correcting title of forum category

* Add 'category' to text change

Update the 'Ask the Bitwarden Community' text change.
2022-04-20 23:25:37 +01:00
mp-bw
cfbbea59e9 account switching in autofill UI (Android) (#1882) 2022-04-19 20:38:17 -04:00
Jake Fink
14d4b2ee29 [BEEEP] add context to search titles (#1878)
* add more descriptive titles to search pages

* add App Resources
2022-04-10 13:00:52 -04:00
github-actions[bot]
b6ad3527db Autosync the updated translations (#1877)
Co-authored-by: github-actions <>
2022-04-08 11:52:21 +02:00
Jacob Fink
0b626cedc7 got the countdown working 2022-03-04 16:44:22 -05:00
Jacob Fink
3ac2580742 add toolbar icons 2022-02-17 11:26:14 -05:00
Jacob Fink
008ed8eb56 add authentication view cell 2022-02-16 15:55:02 -05:00
Jacob Fink
26e0e43bb4 Merge branch 'master' into beeep-totp 2022-02-16 09:25:29 -05:00
Jacob Fink
0ad992faec add tab page 2022-02-16 09:25:19 -05:00
Jacob Fink
98dd8298ea clear extra code and fix build 2022-02-15 14:15:42 -05:00
Jacob Fink
bb37bac620 Revert config files from previous commit
This reverts commit b02c58e362.
2022-02-15 13:36:32 -05:00
Carlos J. Muentes
b02c58e362 Initial commit of new TOTP page 2022-02-11 15:22:51 -05:00
291 changed files with 2675 additions and 1107 deletions

12
.config/dotnet-tools.json Normal file
View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-format": {
"version": "5.1.250801",
"commands": [
"dotnet-format"
]
}
}
}

View File

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

@@ -0,0 +1,2 @@
# .NET format https://github.com/bitwarden/mobile/pull/1738
04539af2a66668b6e85476d5cf318c9150ec4357

2
.gitattributes vendored
View File

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

View File

@@ -21,11 +21,6 @@
## Testing requirements
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
## Before you submit
- [ ] 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)

View File

@@ -105,6 +105,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
@@ -296,18 +304,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 "########################################"

View File

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

View File

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

View File

@@ -145,12 +145,12 @@
<Compile Include="Tiles\GeneratorTileService.cs" />
<Compile Include="Tiles\MyVaultTileService.cs" />
<Compile Include="Utilities\AndroidHelpers.cs" />
<Compile Include="Utilities\AppCenterHelper.cs" />
<Compile Include="Utilities\ThemeHelpers.cs" />
<Compile Include="WebAuthCallbackActivity.cs" />
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
<Compile Include="Services\ClipboardService.cs" />
<Compile Include="Utilities\IntentExtensions.cs" />
<Compile Include="Renderers\CustomPageRenderer.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\bwi-font.ttf" />

View File

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

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.18.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2022.05.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>

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

View File

@@ -3,11 +3,10 @@ using System.Threading.Tasks;
using Android.OS;
using Android.Security.Keystore;
using Bit.Core.Abstractions;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Java.Security;
using Javax.Crypto;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
namespace Bit.Droid.Services
{
@@ -74,9 +73,7 @@ namespace Bit.Droid.Services
catch (InvalidKeyException e)
{
// Fallback for old bitwarden users without a key
#if !FDROID
Crashes.TrackError(e);
#endif
LoggerHelper.LogEvenIfCantBeResolved(e);
CreateKey();
}
@@ -101,9 +98,7 @@ namespace Bit.Droid.Services
{
// Catch silently to allow biometrics to function on devices that are in a state where key generation
// is not functioning
#if !FDROID
Crashes.TrackError(e);
#endif
LoggerHelper.LogEvenIfCantBeResolved(e);
}
}
}

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ namespace Bit.App.Abstractions
string[] ProtectedFields { get; }
Task<bool> ShowPasswordPromptAsync();
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
Task<bool> Enabled();

View File

@@ -1,5 +1,5 @@
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace Bit.App.Abstractions
{

View File

@@ -13,7 +13,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.1" />
@@ -128,6 +127,7 @@
<ItemGroup>
<Folder Include="Resources\" />
<Folder Include="Behaviors\" />
<Folder Include="Pages\Authenticator\" />
<Folder Include="Controls\AccountSwitchingOverlay\" />
</ItemGroup>

View File

@@ -1,15 +1,15 @@
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.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;
@@ -208,7 +208,10 @@ namespace Bit.App
{
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
SetTabsPageFromAutofill(isLocked);
if (!SetTabsPageFromAutofill(isLocked))
{
ClearAutofillUri();
}
await SleptAsync();
}
}
@@ -315,7 +318,7 @@ namespace Bit.App
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
}
else if (await _vaultTimeoutService.IsLockedAsync() ||
else if (await _vaultTimeoutService.IsLockedAsync() ||
await _vaultTimeoutService.ShouldLockAsync())
{
Current.MainPage = new NavigationPage(new LockPage(Options));
@@ -365,7 +368,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 +396,9 @@ namespace Bit.App
}
});
});
return true;
}
return false;
}
private void Prime()

View File

@@ -99,7 +99,7 @@ namespace Bit.App.Controls
Opacity = 0;
IsVisible = true;
this.FadeTo(1, 100);
if (Device.RuntimePlatform == Device.Android && MainFab != null)
{
// start fab fade-out

View File

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

View File

@@ -1,5 +1,5 @@
using System.Windows.Input;
using Bit.Core.Models.View;
using System.Windows.Input;
using Xamarin.Forms;
namespace Bit.App.Controls

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.AuthenticatorViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
x:DataType="pages:AuthenticatorPageListItem">
<Grid.Resources>
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
<u:IconImageConverter x:Key="iconImageConverter"/>
<u:InverseBoolConverter x:Key="inverseBool" />
<u:StringHasValueConverter x:Key="stringHasValueConverter" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:IconLabel
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
AutomationProperties.IsInAccessibleTree="False" />
<ff:CachedImage
Grid.Column="0"
BitmapOptimizations="True"
ErrorPlaceholder="login.png"
LoadingPlaceholder="login.png"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
IsVisible="{Binding ShowIconImage}"
Source="{Binding IconImageSource, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:MonoLabel
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="3"
StyleClass="list-title, list-title-platform"
Text="{Binding TotpCodeFormatted, Mode=OneWay}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Cipher.Name}" />
<controls:IconLabel
Grid.Column="1"
Grid.Row="1"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.Collection}}"
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Shared}" />
</Grid>
<Label
Text="{Binding TotpSec, Mode=OneWay}"
Style="{DynamicResource textTotp}"
Margin="0, 0, 10, 0"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
HorizontalOptions="End"
HorizontalTextAlignment="End"
VerticalOptions="CenterAndExpand" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
Command="{Binding CopyCommand}"
CommandParameter="LoginTotp"
Grid.Row="0"
Grid.Column="2"
Grid.RowSpan="2"
Padding="0,0,1,0"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyTotp}" />
</controls:ExtendedGrid>

View File

@@ -0,0 +1,121 @@
using System;
using Bit.App.Utilities;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class AuthenticatorViewCell : ExtendedGrid
{
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
nameof(Cipher), typeof(CipherView), typeof(AuthenticatorViewCell), default(CipherView), BindingMode.TwoWay);
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(AuthenticatorViewCell));
public static readonly BindableProperty TotpSecProperty = BindableProperty.Create(
nameof(TotpSec), typeof(long), typeof(AuthenticatorViewCell));
//public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
// nameof(ButtonCommand), typeof(Command<CipherView>), typeof(AuthenticatorViewCell));
public AuthenticatorViewCell()
{
InitializeComponent();
}
public Command CopyCommand { get; set; }
public CipherView Cipher
{
get => GetValue(CipherProperty) as CipherView;
set => SetValue(CipherProperty, value);
}
public bool? WebsiteIconsEnabled
{
get => (bool)GetValue(WebsiteIconsEnabledProperty);
set => SetValue(WebsiteIconsEnabledProperty, value);
}
public long TotpSec
{
get => (long)GetValue(TotpSecProperty);
set => SetValue(TotpSecProperty, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled ?? false
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
&& IconImageSource != null;
}
private string _iconImageSource = string.Empty;
public string IconImageSource
{
get
{
if (_iconImageSource == string.Empty) // default value since icon source can return null
{
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
}
return _iconImageSource;
}
}
private string _totpCodeFormatted = "938 928";
public string TotpCodeFormatted
{
get => _totpCodeFormatted;
set => _totpCodeFormatted = value;
}
//public Command<CipherView> ButtonCommand
//{
// get => GetValue(ButtonCommandProperty) as Command<CipherView>;
// set => SetValue(ButtonCommandProperty, value);
//}
//protected override void OnPropertyChanged(string propertyName = null)
//{
// base.OnPropertyChanged(propertyName);
// if (propertyName == CipherProperty.PropertyName)
// {
// if (Cipher == null)
// {
// return;
// }
// _cipherLabel.Text = Cipher.Name;
// }
// else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
// {
// if (Cipher == null)
// {
// return;
// }
// ((AuthenticatorViewCellViewModel)BindingContext).WebsiteIconsEnabled = WebsiteIconsEnabled ?? false;
// }
// else if (propertyName == TotpSecProperty.PropertyName)
// {
// if (Cipher == null)
// {
// return;
// }
// ((AuthenticatorViewCellViewModel)BindingContext).UpdateTotpSec(TotpSec);
// }
//}
private void MoreButton_Clicked(object sender, EventArgs e)
{
var cipher = ((sender as MiButton)?.BindingContext as AuthenticatorViewCellViewModel)?.Cipher;
if (cipher != null)
{
//ButtonCommand?.Execute(cipher);
}
}
}
}

View File

@@ -0,0 +1,103 @@
using System.Threading.Tasks;
using Bit.App.Utilities;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class AuthenticatorViewCellViewModel : ExtendedViewModel
{
private CipherView _cipher;
private string _totpCodeFormatted = "938 928";
private string _totpSec;
private bool _websiteIconsEnabled;
private string _iconImageSource = string.Empty;
public AuthenticatorViewCellViewModel(CipherView cipherView, bool websiteIconsEnabled)
{
Cipher = cipherView;
WebsiteIconsEnabled = websiteIconsEnabled;
}
public Command CopyCommand { get; set; }
public CipherView Cipher
{
get => _cipher;
set => SetProperty(ref _cipher, value);
}
public string TotpCodeFormatted
{
get => _totpCodeFormatted;
set => SetProperty(ref _totpCodeFormatted, value);
}
public string TotpSec
{
get => _totpSec;
set => SetProperty(ref _totpSec, value);
}
public bool WebsiteIconsEnabled
{
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
&& IconImageSource != null;
}
public string IconImageSource
{
get
{
if (_iconImageSource == string.Empty) // default value since icon source can return null
{
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
}
return _iconImageSource;
}
}
public void UpdateTotpSec(long totpSec)
{
_totpSec = totpSec.ToString();
}
//private async Task TotpUpdateCodeAsync()
//{
// if (Cipher == null || Cipher.Type != Core.Enums.CipherType.Login || Cipher.Login.Totp == null)
// {
// _totpInterval = null;
// return;
// }
// _totpCode = await _totpService.GetCodeAsync(Cipher.Login.Totp);
// if (_totpCode != null)
// {
// if (_totpCode.Length > 4)
// {
// var half = (int)Math.Floor(_totpCode.Length / 2M);
// TotpCodeFormatted = string.Format("{0} {1}", _totpCode.Substring(0, half),
// _totpCode.Substring(half));
// }
// else
// {
// TotpCodeFormatted = _totpCode;
// }
// }
// else
// {
// TotpCodeFormatted = null;
// _totpInterval = null;
// }
//}
}
}

View File

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

View File

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

View File

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

View File

@@ -122,7 +122,7 @@
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
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"

View File

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

View File

@@ -4,8 +4,8 @@ namespace Bit.App.Effects
{
public class FabShadowEffect : RoutingEffect
{
public FabShadowEffect()
: base("Bitwarden.FabShadowEffect")
public FabShadowEffect()
: base("Bitwarden.FabShadowEffect")
{ }
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
@@ -170,7 +170,7 @@ namespace Bit.App.Pages
else if (response.ResetMasterPassword)
{
StartSetPasswordAction?.Invoke();
}
}
else if (response.ForcePasswordReset)
{
UpdateTempPasswordAction?.Invoke();

View File

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

View File

@@ -55,7 +55,7 @@ namespace Bit.App.Pages
await _vm.SubmitAsync();
}
}
private async Task RegistrationSuccessAsync(HomePage homePage)
{
if (homePage != null)

View File

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

View File

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

View File

@@ -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; }
@@ -220,7 +225,7 @@ namespace Bit.App.Pages
// Enroll user
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
}
await _deviceActionService.HideLoadingAsync();
SetPasswordSuccessAction?.Invoke();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,97 @@
<?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.AuthenticatorPage"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls"
x:DataType="pages:AuthenticatorPageViewModel"
Title="{Binding PageTitle}"
x:Name="_page">
<ContentPage.BindingContext>
<pages:AuthenticatorPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="search.png" Clicked="Search_Clicked"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Search}" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<ToolbarItem x:Name="_aboutIconItem" x:Key="aboutIconItem" Icon="info.png"
Clicked="About_Clicked" Order="Primary" Priority="-1"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AboutSend}" />
<ToolbarItem x:Name="_syncItem" x:Key="syncItem" Text="{u:I18n Sync}"
Clicked="Sync_Clicked" Order="Secondary" />
<ToolbarItem x:Name="_lockItem" x:Key="lockItem" Text="{u:I18n Lock}"
Clicked="Lock_Clicked" Order="Secondary" />
<ToolbarItem x:Name="_aboutTextItem" x:Key="aboutTextItem" Text="{u:I18n AboutSend}"
Clicked="About_Clicked" Order="Secondary" />
<ToolbarItem x:Name="_addItem" x:Key="addItem" Icon="plus.png"
Clicked="AddButton_Clicked" Order="Primary"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AddItem}" />
<DataTemplate x:Key="authenticatorTemplate"
x:DataType="pages:AuthenticatorPageListItem">
<controls:AuthenticatorViewCell
Cipher="{Binding Cipher}"
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
TotpSec="{Binding TotpSec}"/>
</DataTemplate>
<StackLayout x:Key="mainLayout" x:Name="_mainLayout">
<RefreshView>
<controls:ExtendedCollectionView
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform">
<controls:ExtendedCollectionView.ItemTemplate>
<DataTemplate x:DataType="pages:AuthenticatorPageListItem">
<controls:AuthenticatorViewCell />
</DataTemplate>
</controls:ExtendedCollectionView.ItemTemplate>
</controls:ExtendedCollectionView>
</RefreshView>
</StackLayout>
</ResourceDictionary>
</ContentPage.Resources>
<AbsoluteLayout
x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1" />
<Button
x:Name="_fab"
Image="plus.png"
Clicked="AddButton_Clicked"
Style="{StaticResource btn-fab}"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AddItem}">
<Button.Effects>
<effects:FabShadowEffect />
</Button.Effects>
</Button>
</AbsoluteLayout>
</pages:BaseContentPage>

View File

@@ -0,0 +1,176 @@
using Bit.App.Resources;
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
public partial class AuthenticatorPage : BaseContentPage
{
#region Members
private readonly IBroadcasterService _broadcasterService;
private readonly ISyncService _syncService;
private readonly ICipherService _cipherService;
private AuthenticatorPageViewModel _vm;
private readonly bool _fromTabPage;
private readonly Action<string> _selectAction;
private readonly TabsPage _tabsPage;
#endregion
public AuthenticatorPage(bool fromTabPage, Action<string> selectAction = null, TabsPage tabsPage = null)
{
//_tabsPage = tabsPage;
InitializeComponent();
//_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_vm = BindingContext as AuthenticatorPageViewModel;
//_vm.Page = this;
//_fromTabPage = fromTabPage;
//_selectAction = selectAction;
if (Device.RuntimePlatform == Device.iOS)
{
_absLayout.Children.Remove(_fab);
ToolbarItems.Add(_aboutIconItem);
ToolbarItems.Add(_addItem);
}
else
{
ToolbarItems.Add(_syncItem);
ToolbarItems.Add(_lockItem);
ToolbarItems.Add(_aboutTextItem);
}
}
public async Task InitAsync()
{
await _vm.LoadAsync();
}
protected async override void OnAppearing()
{
base.OnAppearing();
//if (!_fromTabPage)
//{
// await InitAsync();
//}
//_broadcasterService.Subscribe(nameof(GeneratorPage), async (message) =>
//{
// if (message.Command == "updatedTheme")
// {
// Device.BeginInvokeOnMainThread(() =>
// {
// //_vm.RedrawPassword();
// });
// }
//});
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
{
try
{
await _vm.LoadAsync();
}
catch (Exception e) when (e.Message.Contains("No key."))
{
await Task.Delay(1000);
await _vm.LoadAsync();
}
}
else
{
await Task.Delay(5000);
if (!_vm.Loaded)
{
await _vm.LoadAsync();
}
}
AdjustToolbar();
//await CheckAddRequest();
}, _mainContent);
}
private async void Search_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
// var page = new SendsPage(_vm.Filter, _vm.Type != null);
// await Navigation.PushModalAsync(new NavigationPage(page));
}
}
private async void Sync_Clicked(object sender, EventArgs e)
{
// await _vm.SyncAsync();
}
private async void Lock_Clicked(object sender, EventArgs e)
{
// await _vaultTimeoutService.LockAsync(true, true);
}
private void About_Clicked(object sender, EventArgs e)
{
// _vm.ShowAbout();
}
private async void AddButton_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
// var page = new SendAddEditPage(null, null, _vm.Type);
// await Navigation.PushModalAsync(new NavigationPage(page));
}
}
private async void RowSelected(object sender, SelectionChangedEventArgs e)
{
}
private async void Copy_Clicked(object sender, EventArgs e)
{
//await _vm.CopyAsync();
}
private async void More_Clicked(object sender, EventArgs e)
{
//if (!DoOnce())
//{
// return;
//}
//var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
// null, AppResources.PasswordHistory);
//if (selection == AppResources.PasswordHistory)
//{
// var page = new GeneratorHistoryPage();
// await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
//}
}
protected override void OnDisappearing()
{
base.OnDisappearing();
//_broadcasterService.Unsubscribe(nameof(GeneratorPage));
}
private void AdjustToolbar()
{
//_addItem.IsEnabled = !_vm.Deleted;
//_addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
}
}
}

View File

@@ -0,0 +1,129 @@
using System;
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.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class AuthenticatorPageListItem : ExtendedViewModel
{
//private string _totpCode;
private readonly ITotpService _totpService;
//public CipherView Cipher { get; set; }
//public CipherType? Type { get; set; }
//public int interval { get; set; }
//public long TotpSec { get; set; }
//private DateTime? _totpInterval = null;
private CipherView _cipher;
private bool _websiteIconsEnabled;
private string _iconImageSource = string.Empty;
public int interval { get; set; }
private string _totpSec;
private string _totpCode;
private string _totpCodeFormatted = "938 928";
public AuthenticatorPageListItem(CipherView cipherView, bool websiteIconsEnabled)
{
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
Cipher = cipherView;
WebsiteIconsEnabled = websiteIconsEnabled;
interval = _totpService.GetTimeInterval(Cipher.Login.Totp);
}
public Command CopyCommand { get; set; }
public CipherView Cipher
{
get => _cipher;
set => SetProperty(ref _cipher, value);
}
public string TotpCodeFormatted
{
get => _totpCodeFormatted;
set => SetProperty(ref _totpCodeFormatted, value);
}
public string TotpSec
{
get => _totpSec;
set => SetProperty(ref _totpSec, value);
}
public bool WebsiteIconsEnabled
{
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
&& IconImageSource != null;
}
public string IconImageSource
{
get
{
if (_iconImageSource == string.Empty) // default value since icon source can return null
{
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
}
return _iconImageSource;
}
}
public async Task TotpTickAsync()
{
var epoc = CoreHelpers.EpocUtcNow() / 1000;
var mod = epoc % interval;
var totpSec = interval - mod;
TotpSec = totpSec.ToString();
//TotpLow = totpSec < 7;
if (mod == 0)
{
await TotpUpdateCodeAsync();
}
}
public async Task TotpUpdateCodeAsync()
{
_totpCode = await _totpService.GetCodeAsync(Cipher.Login.Totp);
if (_totpCode != null)
{
if (_totpCode.Length > 4)
{
var half = (int)Math.Floor(_totpCode.Length / 2M);
TotpCodeFormatted = string.Format("{0} {1}", _totpCode.Substring(0, half),
_totpCode.Substring(half));
}
else
{
TotpCodeFormatted = _totpCode;
}
}
else
{
TotpCodeFormatted = null;
}
}
}
}

View File

@@ -0,0 +1,153 @@
using System;
using System.Threading.Tasks;
using System.Linq;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class AuthenticatorPageViewModel : BaseViewModel
{
#region Members
private readonly IClipboardService _clipboardService;
private readonly ITotpService _totpService;
private readonly IUserService _userService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ICipherService _cipherService;
private bool _showList = true;
private bool _refreshing;
private bool _loaded;
private bool _websiteIconsEnabled = true;
//private long _totpSec;
#endregion
#region Ctor
public AuthenticatorPageViewModel()
{
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
PageTitle = AppResources.Authenticator;
Items = new ExtendedObservableCollection<AuthenticatorPageListItem>();
}
#endregion
#region Methods
public async Task CopyAsync()
{
//await _clipboardService.CopyTextAsync(Password);
//_platformUtilsService.ShowToast("success", null,
// string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
}
public async Task LoadAsync()
{
var authed = await _userService.IsAuthenticatedAsync();
if (!authed)
{
return;
}
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}
try
{
await LoadDataAsync();
}
finally
{
ShowList = true;
Refreshing = false;
}
}
private async Task LoadDataAsync()
{
var _allCiphers = await _cipherService.GetAllDecryptedAsync();
_allCiphers = _allCiphers.Where(c => c.Type == Core.Enums.CipherType.Login && c.Login.Totp != null).ToList();
var filteredCiphers = _allCiphers.Select(c => new AuthenticatorPageListItem(c, WebsiteIconsEnabled)).ToList();
Items.ResetWithRange(filteredCiphers);
foreach (AuthenticatorPageListItem item in Items)
{
item.TotpUpdateCodeAsync();
}
//await TotpUpdateCodeAsync();
// var interval = _totpService.GetTimeInterval(Cipher.Login.Totp);
// await TotpTickAsync(interval);
// _totpInterval = DateTime.UtcNow;
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
foreach(AuthenticatorPageListItem item in Items)
{
item.TotpTickAsync();
}
return true;
});
//}
//private async Task TotpTickAsync(int intervalSeconds)
//{
// var epoc = CoreHelpers.EpocUtcNow() / 1000;
// var mod = epoc % intervalSeconds;
// var totpSec = intervalSeconds - mod;
// TotpSec = totpSec.ToString();
// TotpLow = totpSec < 7;
// if (mod == 0)
// {
// await TotpUpdateCodeAsync();
// }
}
#endregion
#region Properties
public ExtendedObservableCollection<AuthenticatorPageListItem> Items { get; set; }
public Command RefreshCommand { get; set; }
public bool ShowList
{
get => _showList;
set => SetProperty(ref _showList, value);
}
public bool Refreshing
{
get => _refreshing;
set => SetProperty(ref _refreshing, value);
}
public bool WebsiteIconsEnabled
{
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public bool Loaded
{
get => _loaded;
set => SetProperty(ref _loaded, value);
}
//public long TotpSec
//{
// get => _totpSec;
// set => SetProperty(ref _totpSec, value);
//}
#endregion
}
}

View File

@@ -35,12 +35,12 @@ namespace Bit.App.Pages
protected async override void OnAppearing()
{
base.OnAppearing();
if (IsThemeDirty)
{
UpdateOnThemeChanged();
}
await SaveActivityAsync();
}

View File

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

View File

@@ -31,7 +31,8 @@ namespace Bit.App.Pages
{
base.OnAppearing();
await LoadOnAppearedAsync(_mainLayout, true, async () => {
await LoadOnAppearedAsync(_mainLayout, true, async () =>
{
await _vm.InitAsync();
});
}

View File

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

View File

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

View File

@@ -1,10 +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 +23,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 +130,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 +235,7 @@ namespace Bit.App.Pages
}
}
}
public PasswordGeneratorPolicyOptions EnforcedPolicyOptions
{
get => _enforcedPolicyOptions;
@@ -315,7 +325,7 @@ namespace Bit.App.Pages
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 +343,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;

View File

@@ -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,28 +253,31 @@
</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"
Clicked="ToggleOptions_Clicked"/>
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="{Binding ShowOptions}" />
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="{Binding ShowOptions, Converter={StaticResource inverseBool}}" />
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}"
AutomationProperties.IsInAccessibleTree="False"/>
</StackLayout>
<StackLayout IsVisible="{Binding ShowOptions}">
<StackLayout
@@ -438,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}"

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Models;
@@ -209,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())
@@ -337,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)
{

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

View File

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

View File

@@ -9,6 +9,6 @@
}
public string Title { get; }
public string ItemCount { get; }
public string ItemCount { get; }
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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.ReportCrashLogs)
{
await _vm.LoggerReportingAsync();
}
else
{
var biometricName = AppResources.Biometrics;

View File

@@ -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.ReportCrashLogsDescription, 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.ReportCrashLogs,
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}";
}
}

View File

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

View File

@@ -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,19 @@ 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 _authenticatorPage;
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");
@@ -27,8 +33,16 @@ namespace Bit.App.Pages
Title = AppResources.MyVault,
IconImageSource = "lock.png"
};
Children.Add(_groupingsPage);
_authenticatorPage = new NavigationPage(new AuthenticatorPage(true, null, this))
{
Title = AppResources.Authenticator,
IconImageSource = "info.png"
};
Children.Add(_authenticatorPage);
_sendGroupingsPage = new NavigationPage(new SendGroupingsPage(true, null, null, appOptions))
{
Title = AppResources.Send,
@@ -78,12 +92,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 +121,7 @@ namespace Bit.App.Pages
{
CurrentPage = _generatorPage;
}
public void ResetToSendPage()
{
CurrentPage = _sendGroupingsPage;
@@ -131,5 +159,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);
}
}
}
}

View File

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

View File

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

View File

@@ -65,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");
@@ -249,7 +249,8 @@ namespace Bit.App.Pages
set => SetProperty(ref _showPassword, value,
additionalPropertyNames: new string[]
{
nameof(ShowPasswordIcon)
nameof(ShowPasswordIcon),
nameof(PasswordVisibilityAccessibilityText)
});
}
public bool ShowCardNumber
@@ -298,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()
{
@@ -350,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;
}
@@ -399,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)
{
@@ -451,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;
}
@@ -535,7 +537,7 @@ namespace Bit.App.Pages
AppResources.AnErrorHasOccurred);
}
}
catch(Exception genex)
catch (Exception genex)
{
_logger.Exception(genex);
await _deviceActionService.HideLoadingAsync();

View File

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

View File

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

View File

@@ -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>
@@ -88,6 +99,8 @@
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<!-- Android FAB -->
<Button
x:Name="_fab"
Image="plus.png"
@@ -99,6 +112,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>

View File

@@ -1,13 +1,12 @@
using Bit.App.Models;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using System;
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Controls;
using Bit.App.Models;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -15,7 +14,8 @@ namespace Bit.App.Pages
public partial class AutofillCiphersPage : BaseContentPage
{
private readonly AppOptions _appOptions;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IBroadcasterService _broadcasterService;
private readonly ISyncService _syncService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private AutofillCiphersPageViewModel _vm;
@@ -24,17 +24,23 @@ namespace Bit.App.Pages
{
_appOptions = appOptions;
InitializeComponent();
SetActivityIndicator(_mainContent);
_vm = BindingContext as AutofillCiphersPageViewModel;
_vm.Page = this;
_vm.Init(appOptions);
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
}
protected async override void OnAppearing()
{
base.OnAppearing();
if (_syncService.SyncInProgress)
{
IsBusy = true;
}
if (!await AppHelpers.IsVaultTimeoutImmediateAsync())
{
await _vaultTimeoutService.CheckVaultTimeoutAsync();
@@ -43,13 +49,37 @@ namespace Bit.App.Pages
{
return;
}
_accountAvatar?.OnAppearing();
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
_broadcasterService.Subscribe(nameof(AutofillCiphersPage), async (message) =>
{
if (message.Command == "syncStarted")
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
});
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
try
{
await _vm.LoadAsync();
}
catch (Exception e) when(e.Message.Contains("No key."))
catch (Exception e) when (e.Message.Contains("No key."))
{
await Task.Delay(1000);
await _vm.LoadAsync();
@@ -59,6 +89,11 @@ namespace Bit.App.Pages
protected override bool OnBackButtonPressed()
{
if (_accountListOverlay.IsVisible)
{
_accountListOverlay.HideAsync().FireAndForget();
return true;
}
if (Device.RuntimePlatform == Device.Android)
{
_appOptions.Uri = null;
@@ -66,6 +101,13 @@ namespace Bit.App.Pages
return base.OnBackButtonPressed();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
IsBusy = false;
_accountAvatar?.OnDisappearing();
}
private async void RowSelected(object sender, SelectionChangedEventArgs e)
{
((ExtendedCollectionView)sender).SelectedItem = null;

View File

@@ -1,4 +1,8 @@
using Bit.App.Abstractions;
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.App.Utilities;
@@ -8,9 +12,6 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
@@ -23,8 +24,10 @@ namespace Bit.App.Pages
private readonly ICipherService _cipherService;
private readonly IStateService _stateService;
private readonly IPasswordRepromptService _passwordRepromptService;
private readonly IMessagingService _messagingService;
private readonly ILogger _logger;
private AppOptions _appOptions;
private bool _showNoData;
private bool _showList;
private string _noDataText;
private bool _websiteIconsEnabled;
@@ -36,15 +39,30 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
GroupedItems = new ObservableRangeCollection<IGroupingsPageListItem>();
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
{
AllowAddAccountRow = false
};
}
public string Name { get; set; }
public string Uri { get; set; }
public Command CipherOptionsCommand { get; set; }
public bool LoadedOnce { get; set; }
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public bool ShowNoData
{
get => _showNoData;
set => SetProperty(ref _showNoData, value);
}
public bool ShowList
{
@@ -65,7 +83,6 @@ namespace Bit.App.Pages
public void Init(AppOptions appOptions)
{
_appOptions = appOptions;
Uri = appOptions?.Uri;
string name = null;
if (Uri?.StartsWith(Constants.AndroidAppProtocol) ?? false)
@@ -87,8 +104,10 @@ namespace Bit.App.Pages
public async Task LoadAsync()
{
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
LoadedOnce = true;
ShowList = false;
ShowNoData = false;
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
var groupedItems = new List<GroupingsPageListGroup>();
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
var matching = ciphers.Item1?.Select(c => new GroupingsPageListItem { Cipher = c }).ToList();
@@ -150,6 +169,7 @@ namespace Bit.App.Pages
}
}
ShowList = groupedItems.Any();
ShowNoData = !ShowList;
}
public async Task SelectCipherAsync(CipherView cipher, bool fuzzy)

View File

@@ -33,7 +33,9 @@
Text="&#xe5c4;"
VerticalOptions="CenterAndExpand"
Clicked="BackButton_Clicked"
x:Name="_backButton" />
x:Name="_backButton"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n TapToGoBack}"/>
<controls:ExtendedSearchBar
x:Name="_searchBar"
HorizontalOptions="FillAndExpand"

View File

@@ -1,10 +1,10 @@
using Bit.App.Abstractions;
using System;
using System.Linq;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Resources;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using System;
using System.Linq;
using Bit.App.Controls;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -17,8 +17,7 @@ namespace Bit.App.Pages
private CiphersPageViewModel _vm;
private bool _hasFocused;
public CiphersPage(Func<CipherView, bool> filter, bool folder = false, bool collection = false,
bool type = false, string autofillUrl = null, bool deleted = false)
public CiphersPage(Func<CipherView, bool> filter, string pageTitle = null, string autofillUrl = null, bool deleted = false)
{
InitializeComponent();
_vm = BindingContext as CiphersPageViewModel;
@@ -26,21 +25,9 @@ namespace Bit.App.Pages
_vm.Filter = filter;
_vm.AutofillUrl = _autofillUrl = autofillUrl;
_vm.Deleted = deleted;
if (deleted)
if (pageTitle != null)
{
_vm.PageTitle = AppResources.SearchTrash;
}
else if (folder)
{
_vm.PageTitle = AppResources.SearchFolder;
}
else if (collection)
{
_vm.PageTitle = AppResources.SearchCollection;
}
else if (type)
{
_vm.PageTitle = AppResources.SearchType;
_vm.PageTitle = string.Format(AppResources.SearchGroup, pageTitle);
}
else
{
@@ -53,6 +40,7 @@ namespace Bit.App.Pages
_searchBar.Placeholder = AppResources.Search;
_mainLayout.Children.Insert(0, _searchBar);
_mainLayout.Children.Insert(1, _separator);
ShowModalAnimationDelay = 0;
}
else
{

View File

@@ -1,4 +1,9 @@
using Bit.App.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
@@ -6,11 +11,6 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -107,7 +107,7 @@ namespace Bit.App.Pages
}
try
{
ciphers = await _searchService.SearchCiphersAsync(searchText,
ciphers = await _searchService.SearchCiphersAsync(searchText,
Filter ?? (c => c.IsDeleted == Deleted), null, cts.Token);
cts.Token.ThrowIfCancellationRequested();
}

View File

@@ -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;
namespace Bit.App.Pages
{

View File

@@ -6,6 +6,7 @@
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
x:DataType="pages:GroupingsPageViewModel"
Title="{Binding PageTitle}"
x:Name="_page">
@@ -106,6 +107,30 @@
GroupTemplate="{StaticResource groupTemplate}" />
<StackLayout x:Key="mainLayout" x:Name="_mainLayout">
<StackLayout
IsVisible="{Binding ShowVaultFilter}"
Orientation="Horizontal"
HorizontalOptions="FillAndExpand"
Margin="0,5,0,0">
<Label
Text="{Binding VaultFilterDescription}"
LineBreakMode="TailTruncation"
Margin="10,0"
StyleClass="text-md, text-muted"
VerticalOptions="Center"
HorizontalOptions="StartAndExpand"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Filter}" />
<controls:MiButton
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Command="{Binding VaultFilterCommand}"
VerticalOptions="Center"
HorizontalOptions="End"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Filter}" />
</StackLayout>
<StackLayout
VerticalOptions="CenterAndExpand"
Padding="20, 0"

View File

@@ -27,8 +27,8 @@ namespace Bit.App.Pages
private PreviousPageInfo _previousPage;
public GroupingsPage(bool mainPage, CipherType? type = null, string folderId = null,
string collectionId = null, string pageTitle = null, PreviousPageInfo previousPage = null,
bool deleted = false)
string collectionId = null, string pageTitle = null, string vaultFilterSelection = null,
PreviousPageInfo previousPage = null, bool deleted = false)
{
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
InitializeComponent();
@@ -52,6 +52,10 @@ namespace Bit.App.Pages
{
_vm.PageTitle = pageTitle;
}
if (vaultFilterSelection != null)
{
_vm.VaultFilterDescription = vaultFilterSelection;
}
if (Device.RuntimePlatform == Device.iOS)
{
@@ -117,7 +121,7 @@ namespace Bit.App.Pages
{
await _vm.LoadAsync();
}
catch (Exception e) when(e.Message.Contains("No key."))
catch (Exception e) when (e.Message.Contains("No key."))
{
await Task.Delay(1000);
await _vm.LoadAsync();
@@ -225,8 +229,7 @@ namespace Bit.App.Pages
await _accountListOverlay.HideAsync();
if (DoOnce())
{
var page = new CiphersPage(_vm.Filter, _vm.FolderId != null, _vm.CollectionId != null,
_vm.Type != null, deleted: _vm.Deleted);
var page = new CiphersPage(_vm.Filter, _vm.MainPage ? null : _vm.PageTitle, deleted: _vm.Deleted);
await Navigation.PushModalAsync(new NavigationPage(page));
}
}

Some files were not shown because too many files have changed in this diff Show More