mirror of
https://github.com/bitwarden/mobile
synced 2025-12-11 05:43:30 +00:00
PS-70 added vibrate permission to manifest. refactored scanpage code. added manual authentication key feature in scanner.
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY"/>
|
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY"/>
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||||
|
|
||||||
|
|||||||
@@ -29,25 +29,31 @@
|
|||||||
<Grid
|
<Grid
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HorizontalOptions="FillAndExpand">
|
HorizontalOptions="FillAndExpand">
|
||||||
<zxing:ZXingScannerView
|
|
||||||
x:Name="_zxing"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
AutomationId="zxingScannerView"
|
|
||||||
OnScanResult="OnScanResult"/>
|
|
||||||
<Grid
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
IsVisible="{Binding ShowScanner, Converter={StaticResource inverseBool}}"
|
|
||||||
BackgroundColor="{DynamicResource BackgroundColor}">
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
<zxing:ZXingScannerView
|
||||||
|
x:Name="_zxing"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
AutomationId="zxingScannerView"
|
||||||
|
IsVisible="{Binding ShowScanner}"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.RowSpan="3"
|
||||||
|
OnScanResult="OnScanResult"/>
|
||||||
|
<BoxView
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
IsVisible="{Binding ShowScanner, Converter={StaticResource inverseBool}}"
|
||||||
|
BackgroundColor="{DynamicResource BackgroundColor}"/>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
|
IsVisible="{Binding ShowScanner, Converter={StaticResource inverseBool}}"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
@@ -59,28 +65,20 @@
|
|||||||
Text="{u:I18n AuthenticatorKey}"
|
Text="{u:I18n AuthenticatorKey}"
|
||||||
StyleClass="box-label" />
|
StyleClass="box-label" />
|
||||||
<controls:MonoEntry
|
<controls:MonoEntry
|
||||||
x:Name="_loginTotpEntry"
|
x:Name="_authenticationKeyEntry"
|
||||||
Text="{Binding TotpAuthenticationKey}"
|
Text="{Binding TotpAuthenticationKey}"
|
||||||
IsSpellCheckEnabled="False"
|
IsSpellCheckEnabled="False"
|
||||||
IsTextPredictionEnabled="False"
|
IsTextPredictionEnabled="False"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value" />
|
||||||
<Button
|
<Button
|
||||||
Text="{u:I18n AddTotp}"
|
Text="{u:I18n AddTotp}"
|
||||||
StyleClass="box-button-row"/>
|
StyleClass="box-button-row"
|
||||||
|
Clicked="AddAuthenticationKey_OnClicked"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</Grid>
|
|
||||||
<Grid
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
HorizontalOptions="FillAndExpand"
|
|
||||||
AutomationId="zxingDefaultOverlay">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<StackLayout
|
<StackLayout
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
|
IsVisible="{Binding ShowScanner}"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
@@ -124,7 +122,7 @@
|
|||||||
TextColor="White" />
|
TextColor="White" />
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n CameraInstructionBottom}"
|
Text="{u:I18n CameraInstructionBottom}"
|
||||||
AutomationId="zxingDefaultOverlay_TopTextLabel"
|
AutomationId="zxingDefaultOverlay_BottomTextLabel"
|
||||||
HorizontalOptions="Center"
|
HorizontalOptions="Center"
|
||||||
StyleClass="text-sm"
|
StyleClass="text-sm"
|
||||||
TextColor="White" />
|
TextColor="White" />
|
||||||
@@ -142,5 +140,4 @@
|
|||||||
</Label.GestureRecognizers>
|
</Label.GestureRecognizers>
|
||||||
</Label>
|
</Label>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
@@ -17,12 +17,15 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private ScanPageViewModel ViewModel => BindingContext as ScanPageViewModel;
|
private ScanPageViewModel ViewModel => BindingContext as ScanPageViewModel;
|
||||||
private readonly Action<string> _callback;
|
private readonly Action<string> _callback;
|
||||||
|
|
||||||
private CancellationTokenSource _autofocusCts;
|
private CancellationTokenSource _autofocusCts;
|
||||||
private Task _continuousAutofocusTask;
|
private Task _continuousAutofocusTask;
|
||||||
private Color _greenColor;
|
private readonly Color _greenColor;
|
||||||
private SKColor _blueSKColor;
|
private readonly SKColor _blueSKColor;
|
||||||
private SKColor _greenSKColor;
|
private readonly SKColor _greenSKColor;
|
||||||
|
private readonly Stopwatch _stopwatch;
|
||||||
|
private bool _pageIsActive;
|
||||||
|
private bool _qrcodeFound;
|
||||||
|
private float _scale;
|
||||||
|
|
||||||
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
||||||
|
|
||||||
@@ -45,6 +48,8 @@ namespace Bit.App.Pages
|
|||||||
_greenColor = ThemeManager.GetResourceColor("SuccessColor");
|
_greenColor = ThemeManager.GetResourceColor("SuccessColor");
|
||||||
_greenSKColor = _greenColor.ToSKColor();
|
_greenSKColor = _greenColor.ToSKColor();
|
||||||
_blueSKColor = ThemeManager.GetResourceColor("PrimaryColor").ToSKColor();
|
_blueSKColor = ThemeManager.GetResourceColor("PrimaryColor").ToSKColor();
|
||||||
|
_stopwatch = new Stopwatch();
|
||||||
|
_qrcodeFound = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected override void OnAppearing()
|
||||||
@@ -88,16 +93,14 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}, autofocusCts.Token);
|
}, autofocusCts.Token);
|
||||||
_pageIsActive = true;
|
_pageIsActive = true;
|
||||||
AnimationLoop();
|
AnimationLoopAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async void OnDisappearing()
|
protected override async void OnDisappearing()
|
||||||
{
|
{
|
||||||
_autofocusCts?.Cancel();
|
_autofocusCts?.Cancel();
|
||||||
|
|
||||||
await _continuousAutofocusTask;
|
await _continuousAutofocusTask;
|
||||||
_zxing.IsScanning = false;
|
_zxing.IsScanning = false;
|
||||||
|
|
||||||
_pageIsActive = false;
|
_pageIsActive = false;
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
}
|
}
|
||||||
@@ -111,7 +114,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (text.StartsWith("otpauth://totp"))
|
if (text.StartsWith("otpauth://totp"))
|
||||||
{
|
{
|
||||||
await QRCodeFound();
|
await QrCodeFoundAsync();
|
||||||
_callback(text);
|
_callback(text);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -123,7 +126,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (part.StartsWith("secret="))
|
if (part.StartsWith("secret="))
|
||||||
{
|
{
|
||||||
await QRCodeFound();
|
await QrCodeFoundAsync();
|
||||||
_callback(part.Substring(7)?.ToUpperInvariant());
|
_callback(part.Substring(7)?.ToUpperInvariant());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -133,7 +136,7 @@ namespace Bit.App.Pages
|
|||||||
_callback(null);
|
_callback(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task QRCodeFound()
|
private async Task QrCodeFoundAsync()
|
||||||
{
|
{
|
||||||
_qrcodeFound = true;
|
_qrcodeFound = true;
|
||||||
Vibration.Vibrate();
|
Vibration.Vibrate();
|
||||||
@@ -149,16 +152,47 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddAuthenticationKey_OnClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var text = ViewModel.TotpAuthenticationKey;
|
||||||
|
if (!string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
if (text.StartsWith("otpauth://totp"))
|
||||||
|
{
|
||||||
|
_callback(text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Uri.TryCreate(text, UriKind.Absolute, out Uri uri) &&
|
||||||
|
!string.IsNullOrWhiteSpace(uri?.Query))
|
||||||
|
{
|
||||||
|
var queryParts = uri.Query.Substring(1).ToLowerInvariant().Split('&');
|
||||||
|
foreach (var part in queryParts)
|
||||||
|
{
|
||||||
|
if (part.StartsWith("secret="))
|
||||||
|
{
|
||||||
|
_callback(part.Substring(7)?.ToUpperInvariant());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_callback(null);
|
||||||
|
}
|
||||||
|
|
||||||
private void ToggleScanMode_OnTapped(object sender, EventArgs e)
|
private void ToggleScanMode_OnTapped(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.ToggleScanModeCommand.Execute(null);
|
ViewModel.ToggleScanModeCommand.Execute(null);
|
||||||
|
if (!ViewModel.ShowScanner)
|
||||||
|
{
|
||||||
|
_authenticationKeyEntry.Focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
|
private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
|
||||||
{
|
{
|
||||||
SKImageInfo info = args.Info;
|
var info = args.Info;
|
||||||
SKSurface surface = args.Surface;
|
var surface = args.Surface;
|
||||||
SKCanvas canvas = surface.Canvas;
|
var canvas = surface.Canvas;
|
||||||
var margins = 20;
|
var margins = 20;
|
||||||
var maxSquareSize = (Math.Min(info.Height, info.Width)*0.9f - margins) * _scale;
|
var maxSquareSize = (Math.Min(info.Height, info.Width)*0.9f - margins) * _scale;
|
||||||
var squareSize = maxSquareSize;
|
var squareSize = maxSquareSize;
|
||||||
@@ -167,10 +201,10 @@ namespace Bit.App.Pages
|
|||||||
var startYPoint = (info.Height / 2) - (squareSize / 2);
|
var startYPoint = (info.Height / 2) - (squareSize / 2);
|
||||||
canvas.Clear(SKColors.Transparent);
|
canvas.Clear(SKColors.Transparent);
|
||||||
|
|
||||||
using (SKPaint strokePaint = new SKPaint
|
using (var strokePaint = new SKPaint
|
||||||
{
|
{
|
||||||
Color = _qrcodeFound ? _greenSKColor : _blueSKColor,
|
Color = _qrcodeFound ? _greenSKColor : _blueSKColor,
|
||||||
StrokeWidth = 9*_scale,
|
StrokeWidth = 9 * _scale,
|
||||||
StrokeCap = SKStrokeCap.Round,
|
StrokeCap = SKStrokeCap.Round,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@@ -189,12 +223,7 @@ namespace Bit.App.Pages
|
|||||||
canvas.DrawLine (startXPoint+squareSize, startYPoint+squareSize, startXPoint+squareSize, startYPoint+squareSize-lineSize, strokePaint);
|
canvas.DrawLine (startXPoint+squareSize, startYPoint+squareSize, startXPoint+squareSize, startYPoint+squareSize-lineSize, strokePaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async Task AnimationLoopAsync()
|
||||||
Stopwatch _stopwatch = new Stopwatch();
|
|
||||||
bool _pageIsActive;
|
|
||||||
bool _qrcodeFound = false;
|
|
||||||
float _scale;
|
|
||||||
async Task AnimationLoop()
|
|
||||||
{
|
{
|
||||||
_stopwatch.Start();
|
_stopwatch.Start();
|
||||||
while (_pageIsActive)
|
while (_pageIsActive)
|
||||||
@@ -203,7 +232,7 @@ namespace Bit.App.Pages
|
|||||||
_scale = (20-(1-(float)Math.Sin(4*Math.PI*t))) / 20;
|
_scale = (20-(1-(float)Math.Sin(4*Math.PI*t))) / 20;
|
||||||
SkCanvasView.InvalidateSurface();
|
SkCanvasView.InvalidateSurface();
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1.0 / 30));
|
await Task.Delay(TimeSpan.FromSeconds(1.0 / 30));
|
||||||
if (_qrcodeFound && _scale > 0.98f)
|
if(_qrcodeFound && _scale > 0.98f)
|
||||||
{
|
{
|
||||||
_checkIcon.TextColor = _greenColor;
|
_checkIcon.TextColor = _greenColor;
|
||||||
SkCanvasView.InvalidateSurface();
|
SkCanvasView.InvalidateSurface();
|
||||||
|
|||||||
Reference in New Issue
Block a user