mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
[SG-601] Enhance experience when user denies camera access in Authenticator (#2205)
* [SG-601] Handle camera denied permissions * [SG-601] Code format * [SG-601] PR Fixes * [SG-601] Change resource text to singular * [SG-601] Remove horizontal and vertical options * [SG-601] Add start and stop scanner methods * [SG-601] Remove parameter from ScanPage * [SG-601] Move initialization to viewmodel * [SG-601] Fix zxing scanning bug * [SG-601] Move RunUIThread inside of method
This commit is contained in:
parent
eaa4f193ce
commit
2a60ff62d8
6 changed files with 149 additions and 28 deletions
|
@ -263,12 +263,6 @@ namespace Bit.App.Pages
|
|||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
var cameraPermission = await PermissionManager.CheckAndRequestPermissionAsync(new Permissions.Camera());
|
||||
if (cameraPermission != PermissionStatus.Granted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var page = new ScanPage(key =>
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
|
@ -277,6 +271,7 @@ namespace Bit.App.Pages
|
|||
await _vm.UpdateTotpKeyAsync(key);
|
||||
});
|
||||
});
|
||||
|
||||
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,16 +34,13 @@
|
|||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<zxing:ZXingScannerView
|
||||
x:Name="_zxing"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"
|
||||
<ContentView
|
||||
x:Name="_scannerContainer"
|
||||
AutomationId="zxingScannerView"
|
||||
IsVisible="{Binding ShowScanner}"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
OnScanResult="OnScanResult"/>
|
||||
Grid.RowSpan="3"/>
|
||||
<StackLayout
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
|
|
|
@ -8,8 +8,10 @@ using Bit.Core.Abstractions;
|
|||
using Bit.Core.Utilities;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.Views.Forms;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
using ZXing.Net.Mobile.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
|
@ -26,20 +28,15 @@ namespace Bit.App.Pages
|
|||
private bool _pageIsActive;
|
||||
private bool _qrcodeFound;
|
||||
private float _scale;
|
||||
|
||||
private ZXingScannerView _zxing;
|
||||
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||
|
||||
public ScanPage(Action<string> callback)
|
||||
{
|
||||
_callback = callback;
|
||||
InitializeComponent();
|
||||
_zxing.Options = new ZXing.Mobile.MobileBarcodeScanningOptions
|
||||
{
|
||||
UseNativeScanning = true,
|
||||
PossibleFormats = new List<ZXing.BarcodeFormat> { ZXing.BarcodeFormat.QR_CODE },
|
||||
AutoRotate = false,
|
||||
TryInverted = true
|
||||
};
|
||||
_callback = callback;
|
||||
ViewModel.InitScannerCommand = new Command(() => InitScanner());
|
||||
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
ToolbarItems.RemoveAt(0);
|
||||
|
@ -55,6 +52,53 @@ namespace Bit.App.Pages
|
|||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
StartScanner();
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
StopScanner().FireAndForget();
|
||||
base.OnDisappearing();
|
||||
}
|
||||
|
||||
// Fix known bug with DelayBetweenAnalyzingFrames & DelayBetweenContinuousScans: https://github.com/Redth/ZXing.Net.Mobile/issues/721
|
||||
private void InitScanner()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ViewModel.HasCameraPermission || !ViewModel.ShowScanner || _zxing != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_zxing = new ZXingScannerView();
|
||||
_zxing.Options = new ZXing.Mobile.MobileBarcodeScanningOptions
|
||||
{
|
||||
UseNativeScanning = true,
|
||||
PossibleFormats = new List<ZXing.BarcodeFormat> { ZXing.BarcodeFormat.QR_CODE },
|
||||
AutoRotate = false,
|
||||
TryInverted = true,
|
||||
DelayBetweenAnalyzingFrames = 5,
|
||||
DelayBetweenContinuousScans = 5
|
||||
};
|
||||
_scannerContainer.Content = _zxing;
|
||||
StartScanner();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Value.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartScanner()
|
||||
{
|
||||
if (_zxing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_zxing.OnScanResult -= OnScanResult;
|
||||
_zxing.OnScanResult += OnScanResult;
|
||||
_zxing.IsScanning = true;
|
||||
|
||||
// Fix for Autofocus, now it's done every 2 seconds so that the user does't have to do it
|
||||
|
@ -98,16 +142,21 @@ namespace Bit.App.Pages
|
|||
AnimationLoopAsync();
|
||||
}
|
||||
|
||||
protected override async void OnDisappearing()
|
||||
private async Task StopScanner()
|
||||
{
|
||||
if (_zxing == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_autofocusCts?.Cancel();
|
||||
if (_continuousAutofocusTask != null)
|
||||
{
|
||||
await _continuousAutofocusTask;
|
||||
}
|
||||
_zxing.IsScanning = false;
|
||||
_zxing.OnScanResult -= OnScanResult;
|
||||
_pageIsActive = false;
|
||||
base.OnDisappearing();
|
||||
}
|
||||
|
||||
private async void OnScanResult(ZXing.Result result)
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
using Bit.App.Resources;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
|
@ -9,13 +17,46 @@ namespace Bit.App.Pages
|
|||
{
|
||||
private bool _showScanner = true;
|
||||
private string _totpAuthenticationKey;
|
||||
private IPlatformUtilsService _platformUtilsService;
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private ILogger _logger;
|
||||
|
||||
public ScanPageViewModel()
|
||||
{
|
||||
ToggleScanModeCommand = new Command(() => ShowScanner = !ShowScanner);
|
||||
ToggleScanModeCommand = new AsyncCommand(ToggleScanMode, onException: HandleException);
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
InitAsync().FireAndForget();
|
||||
}
|
||||
|
||||
public Command ToggleScanModeCommand { get; set; }
|
||||
public async Task InitAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
var hasCameraPermission = await PermissionManager.CheckAndRequestPermissionAsync(new Permissions.Camera());
|
||||
HasCameraPermission = hasCameraPermission == PermissionStatus.Granted;
|
||||
ShowScanner = hasCameraPermission == PermissionStatus.Granted;
|
||||
});
|
||||
|
||||
if (!HasCameraPermission)
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitScannerCommand.Execute(null);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand ToggleScanModeCommand { get; set; }
|
||||
public ICommand InitScannerCommand { get; set; }
|
||||
|
||||
public bool HasCameraPermission { get; set; }
|
||||
public string ScanQrPageTitle => ShowScanner ? AppResources.ScanQrTitle : AppResources.AuthenticatorKeyScanner;
|
||||
public string CameraInstructionTop => ShowScanner ? AppResources.PointYourCameraAtTheQRCode : AppResources.OnceTheKeyIsSuccessfullyEntered;
|
||||
public string TotpAuthenticationKey
|
||||
|
@ -39,6 +80,23 @@ namespace Bit.App.Pages
|
|||
});
|
||||
}
|
||||
|
||||
private async Task ToggleScanMode()
|
||||
{
|
||||
var cameraPermission = await PermissionManager.CheckAndRequestPermissionAsync(new Permissions.Camera());
|
||||
HasCameraPermission = cameraPermission == PermissionStatus.Granted;
|
||||
if (!HasCameraPermission)
|
||||
{
|
||||
var openAppSettingsResult = await _platformUtilsService.ShowDialogAsync(AppResources.EnableCamerPermissionToUseTheScanner, title: string.Empty, confirmText: AppResources.Settings, cancelText: AppResources.NoThanks);
|
||||
if (openAppSettingsResult)
|
||||
{
|
||||
_deviceActionService.OpenAppSettings();
|
||||
}
|
||||
return;
|
||||
}
|
||||
ShowScanner = !ShowScanner;
|
||||
InitScannerCommand.Execute(null);
|
||||
}
|
||||
|
||||
public FormattedString ToggleScanModeLabel
|
||||
{
|
||||
get
|
||||
|
@ -57,5 +115,15 @@ namespace Bit.App.Pages
|
|||
return fs;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleException(Exception ex)
|
||||
{
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
|
||||
}).FireAndForget();
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
13
src/App/Resources/AppResources.Designer.cs
generated
13
src/App/Resources/AppResources.Designer.cs
generated
|
@ -2128,6 +2128,15 @@ namespace Bit.App.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enable camera permission to use the scanner.
|
||||
/// </summary>
|
||||
public static string EnableCamerPermissionToUseTheScanner {
|
||||
get {
|
||||
return ResourceManager.GetString("EnableCamerPermissionToUseTheScanner", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enabled.
|
||||
/// </summary>
|
||||
|
@ -3544,7 +3553,7 @@ namespace Bit.App.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enterprise Single Sign-On.
|
||||
/// Looks up a localized string similar to Enterprise single sign-on.
|
||||
/// </summary>
|
||||
public static string LogInSso {
|
||||
get {
|
||||
|
@ -3589,7 +3598,7 @@ namespace Bit.App.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log In with master password.
|
||||
/// Looks up a localized string similar to Log in with master password.
|
||||
/// </summary>
|
||||
public static string LogInWithMasterPassword {
|
||||
get {
|
||||
|
|
|
@ -2512,4 +2512,7 @@ Do you want to switch to this account?</value>
|
|||
<data name="ThisRequestIsNoLongerValid" xml:space="preserve">
|
||||
<value>This request is no longer valid</value>
|
||||
</data>
|
||||
<data name="EnableCamerPermissionToUseTheScanner" xml:space="preserve">
|
||||
<value>Enable camera permission to use the scanner</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
Loading…
Reference in a new issue