diff --git a/src/Android/Services/AppInfoService.cs b/src/Android/Services/AppInfoService.cs
index c46f79714..137d7fd67 100644
--- a/src/Android/Services/AppInfoService.cs
+++ b/src/Android/Services/AppInfoService.cs
@@ -1,4 +1,5 @@
using Android.App;
+using Android.Views.Autofill;
using Bit.App.Abstractions;
using System.Linq;
using AndroidApp = Android.App.Application;
@@ -13,14 +14,27 @@ namespace Bit.Android.Services
public string Build => AndroidApp.Context.ApplicationContext.PackageManager
.GetPackageInfo(AndroidApp.Context.PackageName, 0).VersionCode.ToString();
- public bool AutofillServiceEnabled => AutofillRunning();
+ public bool AutofillAccessibilityServiceEnabled => AutofillAccessibilityRunning();
+ public bool AutofillServiceEnabled => AutofillEnabled();
- private bool AutofillRunning()
+ private bool AutofillAccessibilityRunning()
{
var manager = ((ActivityManager)Xamarin.Forms.Forms.Context.GetSystemService("activity"));
var services = manager.GetRunningServices(int.MaxValue);
return services.Any(s => s.Process.ToLowerInvariant().Contains("bitwarden") &&
s.Service.ClassName.ToLowerInvariant().Contains("autofill"));
}
+
+ private bool AutofillEnabled()
+ {
+ if(global::Android.OS.Build.VERSION.SdkInt < global::Android.OS.BuildVersionCodes.O)
+ {
+ return false;
+ }
+
+ var activity = (MainActivity)Xamarin.Forms.Forms.Context;
+ var afm = (AutofillManager)activity.GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager)));
+ return afm.IsEnabled;
+ }
}
}
diff --git a/src/Android/Services/DeviceActionService.cs b/src/Android/Services/DeviceActionService.cs
index fd92ee235..db711bef3 100644
--- a/src/Android/Services/DeviceActionService.cs
+++ b/src/Android/Services/DeviceActionService.cs
@@ -407,5 +407,13 @@ namespace Bit.Android.Services
ActivityCompat.RequestPermissions(CrossCurrentActivity.Current.Activity, new string[] { permission },
Constants.SelectFilePermissionRequestCode);
}
+
+ public void OpenAutofillSettings()
+ {
+ var activity = (MainActivity)Forms.Context;
+ var intent = new Intent(Settings.ActionRequestSetAutofillService);
+ intent.SetData(global::Android.Net.Uri.Parse("package:com.x8bit.bitwarden"));
+ activity.StartActivity(intent);
+ }
}
}
diff --git a/src/Android/Services/DeviceInfoService.cs b/src/Android/Services/DeviceInfoService.cs
index 222df7b2c..7b0c76c17 100644
--- a/src/Android/Services/DeviceInfoService.cs
+++ b/src/Android/Services/DeviceInfoService.cs
@@ -1,6 +1,7 @@
using Android.App;
using Android.Content.PM;
using Android.OS;
+using Android.Views.Autofill;
using Bit.App.Abstractions;
namespace Bit.Android.Services
@@ -44,5 +45,18 @@ namespace Bit.Android.Services
}
public bool NfcEnabled => Utilities.NfcEnabled();
public bool HasCamera => Xamarin.Forms.Forms.Context.PackageManager.HasSystemFeature(PackageManager.FeatureCamera);
+ public bool AutofillServiceSupported => AutofillSupported();
+
+ private bool AutofillSupported()
+ {
+ if(Build.VERSION.SdkInt < BuildVersionCodes.O)
+ {
+ return false;
+ }
+
+ var activity = (MainActivity)Xamarin.Forms.Forms.Context;
+ var afm = (AutofillManager)activity.GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager)));
+ return afm.IsAutofillSupported;
+ }
}
}
diff --git a/src/App/Abstractions/Services/IAppInfoService.cs b/src/App/Abstractions/Services/IAppInfoService.cs
index 11756ee50..8761f0e65 100644
--- a/src/App/Abstractions/Services/IAppInfoService.cs
+++ b/src/App/Abstractions/Services/IAppInfoService.cs
@@ -4,6 +4,7 @@
{
string Build { get; }
string Version { get; }
+ bool AutofillAccessibilityServiceEnabled { get; }
bool AutofillServiceEnabled { get; }
}
}
diff --git a/src/App/Abstractions/Services/IDeviceActionService.cs b/src/App/Abstractions/Services/IDeviceActionService.cs
index e4b83aa16..991f01031 100644
--- a/src/App/Abstractions/Services/IDeviceActionService.cs
+++ b/src/App/Abstractions/Services/IDeviceActionService.cs
@@ -16,6 +16,7 @@ namespace Bit.App.Abstractions
void RateApp();
void DismissKeyboard();
void OpenAccessibilitySettings();
+ void OpenAutofillSettings();
void LaunchApp(string appName);
}
}
diff --git a/src/App/Abstractions/Services/IDeviceInfoService.cs b/src/App/Abstractions/Services/IDeviceInfoService.cs
index 706a4cda1..e0d8df72f 100644
--- a/src/App/Abstractions/Services/IDeviceInfoService.cs
+++ b/src/App/Abstractions/Services/IDeviceInfoService.cs
@@ -7,5 +7,6 @@
float Scale { get; }
bool NfcEnabled { get; }
bool HasCamera { get; }
+ bool AutofillServiceSupported { get; }
}
}
diff --git a/src/App/App.csproj b/src/App/App.csproj
index b24de87f2..8e31b095e 100644
--- a/src/App/App.csproj
+++ b/src/App/App.csproj
@@ -182,6 +182,7 @@
+
diff --git a/src/App/Pages/Tools/ToolsAutofillServicePage.cs b/src/App/Pages/Tools/ToolsAutofillServicePage.cs
index 63e2be338..0f71f4ba2 100644
--- a/src/App/Pages/Tools/ToolsAutofillServicePage.cs
+++ b/src/App/Pages/Tools/ToolsAutofillServicePage.cs
@@ -200,7 +200,7 @@ namespace Bit.App.Pages
private void UpdateEnabled()
{
- ScrollView.Content = _appInfoService.AutofillServiceEnabled ? EnabledStackLayout : DisabledStackLayout;
+ ScrollView.Content = _appInfoService.AutofillAccessibilityServiceEnabled ? EnabledStackLayout : DisabledStackLayout;
}
private Label BuildServiceLabel()
diff --git a/src/App/Pages/Tools/ToolsAutofillServicePage2.cs b/src/App/Pages/Tools/ToolsAutofillServicePage2.cs
new file mode 100644
index 000000000..58d8bac24
--- /dev/null
+++ b/src/App/Pages/Tools/ToolsAutofillServicePage2.cs
@@ -0,0 +1,153 @@
+using System;
+using Bit.App.Controls;
+using Xamarin.Forms;
+using XLabs.Ioc;
+using Bit.App.Abstractions;
+using Bit.App.Resources;
+
+namespace Bit.App.Pages
+{
+ public class ToolsAutofillServicePage2 : ExtendedContentPage
+ {
+ private readonly IGoogleAnalyticsService _googleAnalyticsService;
+ private readonly IAppInfoService _appInfoService;
+ private readonly IDeviceActionService _deviceActionService;
+ private bool _pageDisappeared = false;
+
+ public ToolsAutofillServicePage2()
+ {
+ _googleAnalyticsService = Resolver.Resolve();
+ _appInfoService = Resolver.Resolve();
+ _deviceActionService = Resolver.Resolve();
+
+ Init();
+ }
+
+ public StackLayout EnabledStackLayout { get; set; }
+ public StackLayout DisabledStackLayout { get; set; }
+ public ScrollView ScrollView { get; set; }
+
+ public void Init()
+ {
+ var enabledFs = new FormattedString();
+ var statusSpan = new Span { Text = string.Concat(AppResources.Status, " ") };
+ enabledFs.Spans.Add(statusSpan);
+ enabledFs.Spans.Add(new Span
+ {
+ Text = AppResources.Enabled,
+ ForegroundColor = Color.Green,
+ FontAttributes = FontAttributes.Bold,
+ FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label))
+ });
+
+ var statusEnabledLabel = new Label
+ {
+ FormattedText = enabledFs,
+ HorizontalTextAlignment = TextAlignment.Center,
+ LineBreakMode = LineBreakMode.WordWrap,
+ FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
+ TextColor = Color.Black
+ };
+
+ var disabledFs = new FormattedString();
+ disabledFs.Spans.Add(statusSpan);
+ disabledFs.Spans.Add(new Span
+ {
+ Text = AppResources.Disabled,
+ ForegroundColor = Color.FromHex("c62929"),
+ FontAttributes = FontAttributes.Bold,
+ FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label))
+ });
+
+ var statusDisabledLabel = new Label
+ {
+ FormattedText = disabledFs,
+ HorizontalTextAlignment = TextAlignment.Center,
+ LineBreakMode = LineBreakMode.WordWrap,
+ FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
+ TextColor = Color.Black
+ };
+
+ DisabledStackLayout = new StackLayout
+ {
+ Children = { BuildServiceLabel(), statusDisabledLabel, BuildGoButton() },
+ Orientation = StackOrientation.Vertical,
+ Spacing = 20,
+ Padding = new Thickness(20, 30),
+ VerticalOptions = LayoutOptions.FillAndExpand
+ };
+
+ EnabledStackLayout = new StackLayout
+ {
+ Children = { BuildServiceLabel(), statusEnabledLabel },
+ Orientation = StackOrientation.Vertical,
+ Spacing = 20,
+ Padding = new Thickness(20, 30),
+ VerticalOptions = LayoutOptions.FillAndExpand
+ };
+
+ ScrollView = new ScrollView { Content = DisabledStackLayout };
+
+ UpdateEnabled();
+ Device.StartTimer(new TimeSpan(0, 0, 3), () =>
+ {
+ if(_pageDisappeared)
+ {
+ return false;
+ }
+
+ UpdateEnabled();
+ return true;
+ });
+
+ Title = AppResources.AutofillService;
+ Content = ScrollView;
+ }
+
+ protected override void OnAppearing()
+ {
+ _pageDisappeared = false;
+ base.OnAppearing();
+ }
+
+ protected override void OnDisappearing()
+ {
+ _pageDisappeared = true;
+ base.OnDisappearing();
+ }
+
+ private void UpdateEnabled()
+ {
+ ScrollView.Content = _appInfoService.AutofillServiceEnabled ? EnabledStackLayout : DisabledStackLayout;
+ }
+
+ private Label BuildServiceLabel()
+ {
+ return new Label
+ {
+ Text = AppResources.AutofillDescription,
+ VerticalOptions = LayoutOptions.Start,
+ HorizontalTextAlignment = TextAlignment.Center,
+ LineBreakMode = LineBreakMode.WordWrap,
+ FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label))
+ };
+ }
+
+ private ExtendedButton BuildGoButton()
+ {
+ return new ExtendedButton
+ {
+ Text = AppResources.BitwardenAutofillServiceOpenSettings,
+ Command = new Command(() =>
+ {
+ _googleAnalyticsService.TrackAppEvent("OpenAutofillSettings");
+ _deviceActionService.OpenAutofillSettings();
+ }),
+ VerticalOptions = LayoutOptions.End,
+ HorizontalOptions = LayoutOptions.Fill,
+ Style = (Style)Application.Current.Resources["btn-primary"],
+ FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Button))
+ };
+ }
+ }
+}
diff --git a/src/App/Pages/Tools/ToolsPage.cs b/src/App/Pages/Tools/ToolsPage.cs
index f41c81ea8..d13e4a20b 100644
--- a/src/App/Pages/Tools/ToolsPage.cs
+++ b/src/App/Pages/Tools/ToolsPage.cs
@@ -15,11 +15,13 @@ namespace Bit.App.Pages
{
private readonly IUserDialogs _userDialogs;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
+ private readonly IDeviceInfoService _deviceInfoService;
public ToolsPage()
{
_userDialogs = Resolver.Resolve();
_googleAnalyticsService = Resolver.Resolve();
+ _deviceInfoService = Resolver.Resolve();
Init();
}
@@ -116,7 +118,14 @@ namespace Bit.App.Pages
private void AutofillCell_Tapped(object sender, EventArgs e)
{
- Navigation.PushModalAsync(new ExtendedNavigationPage(new ToolsAutofillServicePage()));
+ if(_deviceInfoService.AutofillServiceSupported)
+ {
+ Navigation.PushModalAsync(new ExtendedNavigationPage(new ToolsAutofillServicePage2()));
+ }
+ else
+ {
+ Navigation.PushModalAsync(new ExtendedNavigationPage(new ToolsAutofillServicePage()));
+ }
}
private void ExtensionCell_Tapped(object sender, EventArgs e)
diff --git a/src/App/Pages/Vault/VaultAddCipherPage.cs b/src/App/Pages/Vault/VaultAddCipherPage.cs
index d72ceb44f..456675c74 100644
--- a/src/App/Pages/Vault/VaultAddCipherPage.cs
+++ b/src/App/Pages/Vault/VaultAddCipherPage.cs
@@ -242,7 +242,7 @@ namespace Bit.App.Pages
DisplayAlert(AppResources.BitwardenAppExtension, AppResources.BitwardenAppExtensionAlert,
AppResources.Ok);
}
- else if(Device.RuntimePlatform == Device.Android && !_appInfoService.AutofillServiceEnabled)
+ else if(Device.RuntimePlatform == Device.Android && !_appInfoService.AutofillAccessibilityServiceEnabled)
{
DisplayAlert(AppResources.BitwardenAutofillService, AppResources.BitwardenAutofillServiceAlert,
AppResources.Ok);
diff --git a/src/UWP/Services/AppInfoService.cs b/src/UWP/Services/AppInfoService.cs
index c50068b55..ab39f0c7e 100644
--- a/src/UWP/Services/AppInfoService.cs
+++ b/src/UWP/Services/AppInfoService.cs
@@ -16,6 +16,7 @@ namespace Bit.UWP.Services
}
}
+ public bool AutofillAccessibilityServiceEnabled => false;
public bool AutofillServiceEnabled => false;
}
}
diff --git a/src/UWP/Services/DeviceActionService.cs b/src/UWP/Services/DeviceActionService.cs
index 9e15d1f1c..5f79d2d18 100644
--- a/src/UWP/Services/DeviceActionService.cs
+++ b/src/UWP/Services/DeviceActionService.cs
@@ -55,6 +55,11 @@ namespace Bit.UWP.Services
}
}
+ public void OpenAutofillSettings()
+ {
+ throw new NotImplementedException();
+ }
+
public Task SelectFileAsync()
{
var picker = new Windows.Storage.Pickers.FileOpenPicker
diff --git a/src/UWP/Services/DeviceInfoService.cs b/src/UWP/Services/DeviceInfoService.cs
index cd9835191..c30758d67 100644
--- a/src/UWP/Services/DeviceInfoService.cs
+++ b/src/UWP/Services/DeviceInfoService.cs
@@ -39,5 +39,7 @@ namespace Bit.UWP.Services
return cameraList?.Any() ?? false;
}
}
+
+ public bool AutofillServiceSupported => false;
}
}
diff --git a/src/iOS.Core/Services/DeviceInfoService.cs b/src/iOS.Core/Services/DeviceInfoService.cs
index e6dc774bc..83ab7c924 100644
--- a/src/iOS.Core/Services/DeviceInfoService.cs
+++ b/src/iOS.Core/Services/DeviceInfoService.cs
@@ -24,5 +24,6 @@ namespace Bit.iOS.Core.Services
public float Scale => (float)UIScreen.MainScreen.Scale;
public bool NfcEnabled => false;
public bool HasCamera => true;
+ public bool AutofillServiceSupported => false;
}
}
diff --git a/src/iOS/Services/AppInfoService.cs b/src/iOS/Services/AppInfoService.cs
index d382a4a26..0f372c50f 100644
--- a/src/iOS/Services/AppInfoService.cs
+++ b/src/iOS/Services/AppInfoService.cs
@@ -8,6 +8,7 @@ namespace Bit.iOS.Services
{
public string Build => NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString();
public string Version => NSBundle.MainBundle.InfoDictionary["CFBundleShortVersionString"].ToString();
+ public bool AutofillAccessibilityServiceEnabled => false;
public bool AutofillServiceEnabled => false;
}
}
diff --git a/src/iOS/Services/DeviceActionService.cs b/src/iOS/Services/DeviceActionService.cs
index 9109cbf80..db198479d 100644
--- a/src/iOS/Services/DeviceActionService.cs
+++ b/src/iOS/Services/DeviceActionService.cs
@@ -250,5 +250,10 @@ namespace Bit.iOS.Services
{
throw new NotImplementedException();
}
+
+ public void OpenAutofillSettings()
+ {
+ throw new NotImplementedException();
+ }
}
}