mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 23:25:45 +03:00
new autofill feature settings
This commit is contained in:
parent
72d4952812
commit
ffb51c1515
8 changed files with 361 additions and 51 deletions
|
@ -6,6 +6,8 @@ using Android.App;
|
|||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Views.Accessibility;
|
||||
using Bit.App.Abstractions;
|
||||
using XLabs.Ioc;
|
||||
|
||||
namespace Bit.Android
|
||||
{
|
||||
|
@ -53,6 +55,14 @@ namespace Bit.Android
|
|||
new Browser("com.ksmobile.cb", "address_bar_edit_text")
|
||||
}.ToDictionary(n => n.PackageName);
|
||||
|
||||
private readonly IAppSettingsService _appSettings;
|
||||
private long _lastNotification = 0;
|
||||
|
||||
public AutofillService()
|
||||
{
|
||||
_appSettings = Resolver.Resolve<IAppSettingsService>();
|
||||
}
|
||||
|
||||
public override void OnAccessibilityEvent(AccessibilityEvent e)
|
||||
{
|
||||
try
|
||||
|
@ -71,18 +81,47 @@ namespace Bit.Android
|
|||
*/
|
||||
|
||||
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
|
||||
switch(e.EventType)
|
||||
{
|
||||
case EventTypes.WindowContentChanged:
|
||||
case EventTypes.WindowStateChanged:
|
||||
var cancelNotification = true;
|
||||
|
||||
if(e.PackageName == BitwardenPackage)
|
||||
switch(e.EventType)
|
||||
{
|
||||
case EventTypes.ViewFocused:
|
||||
if(!e.Source.Password || !_appSettings.AutofillPasswordField)
|
||||
{
|
||||
notificationManager?.Cancel(AutoFillNotificationId);
|
||||
break;
|
||||
}
|
||||
|
||||
if(e.PackageName == BitwardenPackage)
|
||||
{
|
||||
CancelNotification(notificationManager);
|
||||
break;
|
||||
}
|
||||
|
||||
if(ScanAndAutofill(root, e, notificationManager, cancelNotification))
|
||||
{
|
||||
CancelNotification(notificationManager);
|
||||
}
|
||||
break;
|
||||
case EventTypes.WindowContentChanged:
|
||||
case EventTypes.WindowStateChanged:
|
||||
if(_appSettings.AutofillPasswordField && e.Source.Password)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(_appSettings.AutofillPasswordField)
|
||||
{
|
||||
CancelNotification(notificationManager);
|
||||
break;
|
||||
}
|
||||
|
||||
if(e.PackageName == BitwardenPackage)
|
||||
{
|
||||
CancelNotification(notificationManager);
|
||||
break;
|
||||
}
|
||||
|
||||
if(_appSettings.AutofillPersistNotification)
|
||||
{
|
||||
var uri = GetUri(root);
|
||||
if(uri != null && !uri.Contains(BitwardenWebsite))
|
||||
{
|
||||
|
@ -111,8 +150,37 @@ namespace Bit.Android
|
|||
}
|
||||
|
||||
AutofillActivity.LastCredentials = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelNotification = ScanAndAutofill(root, e, notificationManager, cancelNotification);
|
||||
}
|
||||
|
||||
/*
|
||||
if(cancelNotification)
|
||||
{
|
||||
CancelNotification(notificationManager);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
notificationManager?.Dispose();
|
||||
root.Dispose();
|
||||
e.Dispose();
|
||||
}
|
||||
// Suppress exceptions so that service doesn't crash
|
||||
catch { }
|
||||
}
|
||||
|
||||
public override void OnInterrupt()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool ScanAndAutofill(AccessibilityNodeInfo root, AccessibilityEvent e,
|
||||
NotificationManager notificationManager, bool cancelNotification)
|
||||
{
|
||||
var passwordNodes = GetWindowNodes(root, e, n => n.Password, false);
|
||||
if(passwordNodes.Count > 0)
|
||||
{
|
||||
|
@ -139,30 +207,18 @@ namespace Bit.Android
|
|||
}
|
||||
|
||||
passwordNodes.Dispose();
|
||||
*/
|
||||
return cancelNotification;
|
||||
}
|
||||
|
||||
|
||||
if(cancelNotification)
|
||||
public void CancelNotification(NotificationManager notificationManager)
|
||||
{
|
||||
if(Java.Lang.JavaSystem.CurrentTimeMillis() - _lastNotification < 250)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
notificationManager?.Cancel(AutoFillNotificationId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
notificationManager?.Dispose();
|
||||
root.Dispose();
|
||||
e.Dispose();
|
||||
}
|
||||
// Suppress exceptions so that service doesn't crash
|
||||
catch { }
|
||||
}
|
||||
|
||||
public override void OnInterrupt()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private string GetUri(AccessibilityNodeInfo root)
|
||||
{
|
||||
|
@ -243,6 +299,7 @@ namespace Bit.Android
|
|||
return;
|
||||
}
|
||||
|
||||
var now = Java.Lang.JavaSystem.CurrentTimeMillis();
|
||||
var intent = new Intent(this, typeof(AutofillActivity));
|
||||
intent.PutExtra("uri", uri);
|
||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||
|
@ -253,7 +310,7 @@ namespace Bit.Android
|
|||
.SetContentTitle(App.Resources.AppResources.BitwardenAutofillService)
|
||||
.SetContentText(App.Resources.AppResources.BitwardenAutofillServiceNotificationContent)
|
||||
.SetTicker(App.Resources.AppResources.BitwardenAutofillServiceNotificationContent)
|
||||
.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis())
|
||||
.SetWhen(now)
|
||||
.SetContentIntent(pendingIntent);
|
||||
|
||||
if(Build.VERSION.SdkInt > BuildVersionCodes.KitkatWatch)
|
||||
|
@ -263,11 +320,12 @@ namespace Bit.Android
|
|||
Resource.Color.primary));
|
||||
}
|
||||
|
||||
if(Build.VERSION.SdkInt <= BuildVersionCodes.N)
|
||||
if(Build.VERSION.SdkInt <= BuildVersionCodes.N && _appSettings.AutofillPersistNotification)
|
||||
{
|
||||
builder.SetPriority(-1);
|
||||
}
|
||||
|
||||
_lastNotification = now;
|
||||
notificationManager.Notify(AutoFillNotificationId, builder.Build());
|
||||
|
||||
builder.Dispose();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:description="@string/AutoFillServiceDescription"
|
||||
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
|
||||
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeViewFocused"
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFlags="flagReportViewIds"
|
||||
android:notificationTimeout="100"
|
||||
|
|
|
@ -6,5 +6,7 @@ namespace Bit.App.Abstractions
|
|||
{
|
||||
bool Locked { get; set; }
|
||||
DateTime LastActivity { get; set; }
|
||||
bool AutofillPersistNotification { get; set; }
|
||||
bool AutofillPasswordField { get; set; }
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
public const string SettingPinUnlockOn = "setting:pinUnlockOn";
|
||||
public const string SettingLockSeconds = "setting:lockSeconds";
|
||||
public const string SettingGaOptOut = "setting:googleAnalyticsOptOut";
|
||||
public const string AutofillPersistNotification = "setting:persistNotification";
|
||||
public const string AutofillPasswordField = "setting:autofillPasswordField";
|
||||
|
||||
public const string PasswordGeneratorLength = "pwGenerator:length";
|
||||
public const string PasswordGeneratorUppercase = "pwGenerator:uppercase";
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Bit.App.Pages
|
|||
private readonly IAuthService _authService;
|
||||
private readonly IUserDialogs _userDialogs;
|
||||
private readonly ISettings _settings;
|
||||
private readonly IAppSettingsService _appSettings;
|
||||
private readonly IFingerprint _fingerprint;
|
||||
private readonly IPushNotification _pushNotification;
|
||||
private readonly IGoogleAnalyticsService _googleAnalyticsService;
|
||||
|
@ -25,6 +26,7 @@ namespace Bit.App.Pages
|
|||
_authService = Resolver.Resolve<IAuthService>();
|
||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||
_settings = Resolver.Resolve<ISettings>();
|
||||
_appSettings = Resolver.Resolve<IAppSettingsService>();
|
||||
_fingerprint = Resolver.Resolve<IFingerprint>();
|
||||
_pushNotification = Resolver.Resolve<IPushNotification>();
|
||||
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
||||
|
@ -35,6 +37,12 @@ namespace Bit.App.Pages
|
|||
private StackLayout StackLayout { get; set; }
|
||||
private ExtendedSwitchCell AnalyticsCell { get; set; }
|
||||
private Label AnalyticsLabel { get; set; }
|
||||
private ExtendedSwitchCell AutofillPersistNotificationCell { get; set; }
|
||||
private Label AutofillPersistNotificationLabel { get; set; }
|
||||
private ExtendedSwitchCell AutofillPasswordFieldCell { get; set; }
|
||||
private Label AutofillPasswordFieldLabel { get; set; }
|
||||
private ExtendedSwitchCell AutofillAlwaysCell { get; set; }
|
||||
private Label AutofillAlwaysLabel { get; set; }
|
||||
|
||||
private void Init()
|
||||
{
|
||||
|
@ -70,6 +78,84 @@ namespace Bit.App.Pages
|
|||
Spacing = 0
|
||||
};
|
||||
|
||||
if(Device.OS == TargetPlatform.Android)
|
||||
{
|
||||
AutofillAlwaysCell = new ExtendedSwitchCell
|
||||
{
|
||||
Text = AppResources.AutofillAlways,
|
||||
On = !_appSettings.AutofillPersistNotification && !_appSettings.AutofillPasswordField
|
||||
};
|
||||
|
||||
var autofillAlwaysTable = new FormTableView
|
||||
{
|
||||
Root = new TableRoot
|
||||
{
|
||||
new TableSection(AppResources.AutofillService)
|
||||
{
|
||||
AutofillAlwaysCell
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AutofillAlwaysLabel = new FormTableLabel(this)
|
||||
{
|
||||
Text = AppResources.AutofillAlwaysDescription
|
||||
};
|
||||
|
||||
AutofillPersistNotificationCell = new ExtendedSwitchCell
|
||||
{
|
||||
Text = AppResources.AutofillPersistNotification,
|
||||
On = _appSettings.AutofillPersistNotification
|
||||
};
|
||||
|
||||
var autofillPersistNotificationTable = new FormTableView
|
||||
{
|
||||
NoHeader = true,
|
||||
Root = new TableRoot
|
||||
{
|
||||
new TableSection(" ")
|
||||
{
|
||||
AutofillPersistNotificationCell
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AutofillPersistNotificationLabel = new FormTableLabel(this)
|
||||
{
|
||||
Text = AppResources.AutofillPersistNotificationDescription
|
||||
};
|
||||
|
||||
AutofillPasswordFieldCell = new ExtendedSwitchCell
|
||||
{
|
||||
Text = AppResources.AutofillPasswordField,
|
||||
On = _appSettings.AutofillPasswordField
|
||||
};
|
||||
|
||||
var autofillPasswordFieldTable = new FormTableView
|
||||
{
|
||||
NoHeader = true,
|
||||
Root = new TableRoot
|
||||
{
|
||||
new TableSection(" ")
|
||||
{
|
||||
AutofillPasswordFieldCell
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AutofillPasswordFieldLabel = new FormTableLabel(this)
|
||||
{
|
||||
Text = AppResources.AutofillPasswordFieldDescription
|
||||
};
|
||||
|
||||
StackLayout.Children.Add(autofillAlwaysTable);
|
||||
StackLayout.Children.Add(AutofillAlwaysLabel);
|
||||
StackLayout.Children.Add(autofillPasswordFieldTable);
|
||||
StackLayout.Children.Add(AutofillPasswordFieldLabel);
|
||||
StackLayout.Children.Add(autofillPersistNotificationTable);
|
||||
StackLayout.Children.Add(AutofillPersistNotificationLabel);
|
||||
}
|
||||
|
||||
var scrollView = new ScrollView
|
||||
{
|
||||
Content = StackLayout
|
||||
|
@ -91,6 +177,9 @@ namespace Bit.App.Pages
|
|||
|
||||
AnalyticsCell.OnChanged += AnalyticsCell_Changed;
|
||||
StackLayout.LayoutChanged += Layout_LayoutChanged;
|
||||
AutofillAlwaysCell.OnChanged += AutofillAlwaysCell_OnChanged;
|
||||
AutofillPasswordFieldCell.OnChanged += AutofillPasswordFieldCell_OnChanged;
|
||||
AutofillPersistNotificationCell.OnChanged += AutofillPersistNotificationCell_OnChanged;
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
|
@ -99,6 +188,9 @@ namespace Bit.App.Pages
|
|||
|
||||
AnalyticsCell.OnChanged -= AnalyticsCell_Changed;
|
||||
StackLayout.LayoutChanged -= Layout_LayoutChanged;
|
||||
AutofillAlwaysCell.OnChanged -= AutofillAlwaysCell_OnChanged;
|
||||
AutofillPasswordFieldCell.OnChanged -= AutofillPasswordFieldCell_OnChanged;
|
||||
AutofillPersistNotificationCell.OnChanged -= AutofillPersistNotificationCell_OnChanged;
|
||||
}
|
||||
|
||||
private void Layout_LayoutChanged(object sender, EventArgs e)
|
||||
|
@ -118,6 +210,55 @@ namespace Bit.App.Pages
|
|||
_googleAnalyticsService.SetAppOptOut(cell.On);
|
||||
}
|
||||
|
||||
private void AutofillAlwaysCell_OnChanged(object sender, ToggledEventArgs e)
|
||||
{
|
||||
var cell = sender as ExtendedSwitchCell;
|
||||
if(cell == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(cell.On)
|
||||
{
|
||||
AutofillPasswordFieldCell.On = false;
|
||||
AutofillPersistNotificationCell.On = false;
|
||||
_appSettings.AutofillPersistNotification = false;
|
||||
_appSettings.AutofillPasswordField = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AutofillPersistNotificationCell_OnChanged(object sender, ToggledEventArgs e)
|
||||
{
|
||||
var cell = sender as ExtendedSwitchCell;
|
||||
if(cell == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_appSettings.AutofillPersistNotification = cell.On;
|
||||
if(cell.On)
|
||||
{
|
||||
AutofillPasswordFieldCell.On = false;
|
||||
AutofillAlwaysCell.On = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AutofillPasswordFieldCell_OnChanged(object sender, ToggledEventArgs e)
|
||||
{
|
||||
var cell = sender as ExtendedSwitchCell;
|
||||
if(cell == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_appSettings.AutofillPasswordField = cell.On;
|
||||
if(cell.On)
|
||||
{
|
||||
AutofillPersistNotificationCell.On = false;
|
||||
AutofillAlwaysCell.On = false;
|
||||
}
|
||||
}
|
||||
|
||||
private class FormTableView : ExtendedTableView
|
||||
{
|
||||
public FormTableView()
|
||||
|
@ -130,5 +271,16 @@ namespace Bit.App.Pages
|
|||
NoFooter = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class FormTableLabel : Label
|
||||
{
|
||||
public FormTableLabel(Page page)
|
||||
{
|
||||
LineBreakMode = LineBreakMode.WordWrap;
|
||||
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label));
|
||||
Style = (Style)Application.Current.Resources["text-muted"];
|
||||
Margin = new Thickness(15, (page.IsLandscape() ? 5 : 0), 15, 25);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
54
src/App/Resources/AppResources.Designer.cs
generated
54
src/App/Resources/AppResources.Designer.cs
generated
|
@ -151,6 +151,24 @@ namespace Bit.App.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Always Scan.
|
||||
/// </summary>
|
||||
public static string AutofillAlways {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillAlways", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Always scan the screen for fields and only offer an auto-fill notification if password fields are found. This is the default setting..
|
||||
/// </summary>
|
||||
public static string AutofillAlwaysDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillAlwaysDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use the bitwarden accessibility service to auto-fill your logins across apps and the web..
|
||||
/// </summary>
|
||||
|
@ -169,6 +187,42 @@ namespace Bit.App.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Scan When Password Field Focused.
|
||||
/// </summary>
|
||||
public static string AutofillPasswordField {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillPasswordField", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Only scan the screen for fields and offer an auto-fill notification whenever you select a password field. This setting may help conserve battery life..
|
||||
/// </summary>
|
||||
public static string AutofillPasswordFieldDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillPasswordFieldDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Persist Notification.
|
||||
/// </summary>
|
||||
public static string AutofillPersistNotification {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillPersistNotification", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Always offer an auto-fill notification and only scan for fields after attempting an auto-fill. This setting may help conserve battery life..
|
||||
/// </summary>
|
||||
public static string AutofillPersistNotificationDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillPersistNotificationDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto-fill Service.
|
||||
/// </summary>
|
||||
|
|
|
@ -831,4 +831,22 @@
|
|||
<data name="Features" xml:space="preserve">
|
||||
<value>Features</value>
|
||||
</data>
|
||||
<data name="AutofillPasswordField" xml:space="preserve">
|
||||
<value>Scan When Password Field Focused</value>
|
||||
</data>
|
||||
<data name="AutofillPasswordFieldDescription" xml:space="preserve">
|
||||
<value>Only scan the screen for fields and offer an auto-fill notification whenever you select a password field. This setting may help conserve battery life.</value>
|
||||
</data>
|
||||
<data name="AutofillPersistNotification" xml:space="preserve">
|
||||
<value>Persist Notification</value>
|
||||
</data>
|
||||
<data name="AutofillPersistNotificationDescription" xml:space="preserve">
|
||||
<value>Always offer an auto-fill notification and only scan for fields after attempting an auto-fill. This setting may help conserve battery life.</value>
|
||||
</data>
|
||||
<data name="AutofillAlways" xml:space="preserve">
|
||||
<value>Always Scan</value>
|
||||
</data>
|
||||
<data name="AutofillAlwaysDescription" xml:space="preserve">
|
||||
<value>Always scan the screen for fields and only offer an auto-fill notification if password fields are found. This is the default setting.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -37,5 +37,29 @@ namespace Bit.App.Services
|
|||
_settings.AddOrUpdateValue(Constants.LastActivityDate, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutofillPersistNotification
|
||||
{
|
||||
get
|
||||
{
|
||||
return _settings.GetValueOrDefault(Constants.AutofillPersistNotification, false);
|
||||
}
|
||||
set
|
||||
{
|
||||
_settings.AddOrUpdateValue(Constants.AutofillPersistNotification, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutofillPasswordField
|
||||
{
|
||||
get
|
||||
{
|
||||
return _settings.GetValueOrDefault(Constants.AutofillPasswordField, false);
|
||||
}
|
||||
set
|
||||
{
|
||||
_settings.AddOrUpdateValue(Constants.AutofillPasswordField, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue