From 783c4d104c5115017b8a1ef512b97e8d46213888 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 27 Nov 2017 17:27:11 -0500 Subject: [PATCH] add tools page for autofill service --- src/Android/Services/AppInfoService.cs | 18 ++- src/Android/Services/DeviceActionService.cs | 8 + src/Android/Services/DeviceInfoService.cs | 14 ++ .../Abstractions/Services/IAppInfoService.cs | 1 + .../Services/IDeviceActionService.cs | 1 + .../Services/IDeviceInfoService.cs | 1 + src/App/App.csproj | 1 + .../Pages/Tools/ToolsAutofillServicePage.cs | 2 +- .../Pages/Tools/ToolsAutofillServicePage2.cs | 153 ++++++++++++++++++ src/App/Pages/Tools/ToolsPage.cs | 11 +- src/App/Pages/Vault/VaultAddCipherPage.cs | 2 +- src/UWP/Services/AppInfoService.cs | 1 + src/UWP/Services/DeviceActionService.cs | 5 + src/UWP/Services/DeviceInfoService.cs | 2 + src/iOS.Core/Services/DeviceInfoService.cs | 1 + src/iOS/Services/AppInfoService.cs | 1 + src/iOS/Services/DeviceActionService.cs | 5 + 17 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 src/App/Pages/Tools/ToolsAutofillServicePage2.cs 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(); + } } }