diff --git a/src/Android/Services/DeviceInfoService.cs b/src/Android/Services/DeviceInfoService.cs index 7b0c76c17..2b30f0ca4 100644 --- a/src/Android/Services/DeviceInfoService.cs +++ b/src/Android/Services/DeviceInfoService.cs @@ -46,7 +46,7 @@ namespace Bit.Android.Services public bool NfcEnabled => Utilities.NfcEnabled(); public bool HasCamera => Xamarin.Forms.Forms.Context.PackageManager.HasSystemFeature(PackageManager.FeatureCamera); public bool AutofillServiceSupported => AutofillSupported(); - + public bool HasFaceIdSupport => false; private bool AutofillSupported() { if(Build.VERSION.SdkInt < BuildVersionCodes.O) diff --git a/src/App/Abstractions/Services/IDeviceInfoService.cs b/src/App/Abstractions/Services/IDeviceInfoService.cs index e0d8df72f..6b55bdda0 100644 --- a/src/App/Abstractions/Services/IDeviceInfoService.cs +++ b/src/App/Abstractions/Services/IDeviceInfoService.cs @@ -8,5 +8,6 @@ bool NfcEnabled { get; } bool HasCamera { get; } bool AutofillServiceSupported { get; } + bool HasFaceIdSupport { get; } } } diff --git a/src/App/Pages/Lock/LockFingerprintPage.cs b/src/App/Pages/Lock/LockFingerprintPage.cs index 24c1eba3c..15dfb1754 100644 --- a/src/App/Pages/Lock/LockFingerprintPage.cs +++ b/src/App/Pages/Lock/LockFingerprintPage.cs @@ -15,6 +15,7 @@ namespace Bit.App.Pages private readonly IFingerprint _fingerprint; private readonly ISettings _settings; private readonly IAppSettingsService _appSettings; + private readonly IDeviceInfoService _deviceInfoService; private readonly bool _checkFingerprintImmediately; private DateTime? _lastAction; @@ -24,6 +25,7 @@ namespace Bit.App.Pages _fingerprint = Resolver.Resolve(); _settings = Resolver.Resolve(); _appSettings = Resolver.Resolve(); + _deviceInfoService = Resolver.Resolve(); Init(); } @@ -32,7 +34,7 @@ namespace Bit.App.Pages { var fingerprintIcon = new ExtendedButton { - Image = "fingerprint.png", + Image = _deviceInfoService.HasFaceIdSupport ? "smile.png" : "fingerprint.png", BackgroundColor = Color.Transparent, Command = new Command(async () => await CheckFingerprintAsync()), VerticalOptions = LayoutOptions.CenterAndExpand, @@ -41,7 +43,8 @@ namespace Bit.App.Pages var fingerprintButton = new ExtendedButton { - Text = AppResources.UseFingerprintToUnlock, + Text = _deviceInfoService.HasFaceIdSupport ? AppResources.UseFaceIDToUnlock : + AppResources.UseFingerprintToUnlock, Command = new Command(async () => await CheckFingerprintAsync()), VerticalOptions = LayoutOptions.EndAndExpand, Style = (Style)Application.Current.Resources["btn-primary"] @@ -64,7 +67,7 @@ namespace Bit.App.Pages Children = { fingerprintIcon, fingerprintButton, logoutButton } }; - Title = AppResources.VerifyFingerprint; + Title = _deviceInfoService.HasFaceIdSupport ? AppResources.VerifyFaceID : AppResources.VerifyFingerprint; Content = stackLayout; } diff --git a/src/App/Pages/Settings/SettingsPage.cs b/src/App/Pages/Settings/SettingsPage.cs index ad0f4a04a..2cf81aacf 100644 --- a/src/App/Pages/Settings/SettingsPage.cs +++ b/src/App/Pages/Settings/SettingsPage.cs @@ -20,6 +20,7 @@ namespace Bit.App.Pages private readonly IPushNotificationService _pushNotification; private readonly IGoogleAnalyticsService _googleAnalyticsService; private readonly IDeviceActionService _deviceActionService; + private readonly IDeviceInfoService _deviceInfoService; private readonly ILockService _lockService; // TODO: Model binding context? @@ -33,6 +34,7 @@ namespace Bit.App.Pages _pushNotification = Resolver.Resolve(); _googleAnalyticsService = Resolver.Resolve(); _deviceActionService = Resolver.Resolve(); + _deviceInfoService = Resolver.Resolve(); _lockService = Resolver.Resolve(); Init(); @@ -91,8 +93,9 @@ namespace Bit.App.Pages if((await _fingerprint.GetAvailabilityAsync()) == FingerprintAvailability.Available) { - var fingerprintName = Helpers.OnPlatform(iOS: AppResources.TouchID, Android: AppResources.Fingerprint, - Windows: AppResources.Fingerprint, WinPhone: AppResources.Fingerprint); + var fingerprintName = Helpers.OnPlatform( + iOS: _deviceInfoService.HasFaceIdSupport ? AppResources.FaceID : AppResources.TouchID, + Android: AppResources.Fingerprint, Windows: AppResources.Fingerprint, WinPhone: AppResources.Fingerprint); FingerprintCell = new ExtendedSwitchCell { Text = string.Format(AppResources.UnlockWith, fingerprintName), diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index ccf25293d..e9b5ba525 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -1303,6 +1303,24 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Face ID. + /// + public static string FaceID { + get { + return ResourceManager.GetString("FaceID", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use Face ID to verify.. + /// + public static string FaceIDDirection { + get { + return ResourceManager.GetString("FaceIDDirection", resourceCulture); + } + } + /// /// Looks up a localized string similar to Favorite. /// @@ -2905,6 +2923,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Use Face ID To Unlock. + /// + public static string UseFaceIDToUnlock { + get { + return ResourceManager.GetString("UseFaceIDToUnlock", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use Fingerprint to Unlock. /// @@ -2995,6 +3022,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Verify Face ID. + /// + public static string VerifyFaceID { + get { + return ResourceManager.GetString("VerifyFaceID", resourceCulture); + } + } + /// /// Looks up a localized string similar to Verify Fingerprint. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index 9178fc6df..c5d45321f 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -1218,4 +1218,17 @@ Open Autofill Settings + + Face ID + What Apple calls their facial recognition reader. + + + Use Face ID to verify. + + + Use Face ID To Unlock + + + Verify Face ID + \ No newline at end of file diff --git a/src/UWP/Services/DeviceInfoService.cs b/src/UWP/Services/DeviceInfoService.cs index c30758d67..fa8f79b92 100644 --- a/src/UWP/Services/DeviceInfoService.cs +++ b/src/UWP/Services/DeviceInfoService.cs @@ -41,5 +41,6 @@ namespace Bit.UWP.Services } public bool AutofillServiceSupported => false; + public bool HasFaceIdSupport => false; } } diff --git a/src/iOS.Core/Services/DeviceInfoService.cs b/src/iOS.Core/Services/DeviceInfoService.cs index 83ab7c924..4eb50cded 100644 --- a/src/iOS.Core/Services/DeviceInfoService.cs +++ b/src/iOS.Core/Services/DeviceInfoService.cs @@ -1,4 +1,6 @@ using Bit.App.Abstractions; +using Foundation; +using LocalAuthentication; using UIKit; namespace Bit.iOS.Core.Services @@ -25,5 +27,19 @@ namespace Bit.iOS.Core.Services public bool NfcEnabled => false; public bool HasCamera => true; public bool AutofillServiceSupported => false; + public bool HasFaceIdSupport + { + get + { + if(Version < 11) + { + return false; + } + + var context = new LAContext(); + return context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError e) && + context.BiometryType == LABiometryType.TypeFaceId; + } + } } } diff --git a/src/iOS/Resources/smile.png b/src/iOS/Resources/smile.png new file mode 100644 index 000000000..25f2f45b1 Binary files /dev/null and b/src/iOS/Resources/smile.png differ diff --git a/src/iOS/Resources/smile@2x.png b/src/iOS/Resources/smile@2x.png new file mode 100644 index 000000000..71dd21e35 Binary files /dev/null and b/src/iOS/Resources/smile@2x.png differ diff --git a/src/iOS/Resources/smile@3x.png b/src/iOS/Resources/smile@3x.png new file mode 100644 index 000000000..6e7189e69 Binary files /dev/null and b/src/iOS/Resources/smile@3x.png differ