diff --git a/bitwarden-mobile.sln b/bitwarden-mobile.sln
index fcdc0b7ef..3f9a6d256 100644
--- a/bitwarden-mobile.sln
+++ b/bitwarden-mobile.sln
@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{83449CC4-1F76-4CFE-92B1-D2E13A62506F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.AndroidX.Credentials", "src\Xamarin.AndroidX.Credentials\Xamarin.AndroidX.Credentials.csproj", "{1201A3B6-EF37-420D-A8AD-2C322F6D7B2C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/src/App/App.csproj b/src/App/App.csproj
index 2c88cc36f..328c95964 100644
--- a/src/App/App.csproj
+++ b/src/App/App.csproj
@@ -119,6 +119,7 @@
+
diff --git a/src/App/Platforms/Android/Autofill/CredentialProviderConstants.cs b/src/App/Platforms/Android/Autofill/CredentialProviderConstants.cs
new file mode 100644
index 000000000..9104303d7
--- /dev/null
+++ b/src/App/Platforms/Android/Autofill/CredentialProviderConstants.cs
@@ -0,0 +1,9 @@
+namespace Bit.Droid.Autofill
+{
+ public class CredentialProviderConstants
+ {
+ public const string CredentialProviderCipherId = "credentialProviderCipherId";
+ public const string CredentialDataIntentExtra = "CREDENTIAL_DATA";
+ public const string CredentialIdIntentExtra = "credId";
+ }
+}
diff --git a/src/App/Platforms/Android/Autofill/CredentialProviderSelectionActivity.cs b/src/App/Platforms/Android/Autofill/CredentialProviderSelectionActivity.cs
new file mode 100644
index 000000000..1b7ffbb3a
--- /dev/null
+++ b/src/App/Platforms/Android/Autofill/CredentialProviderSelectionActivity.cs
@@ -0,0 +1,65 @@
+using System.Threading.Tasks;
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using AndroidX.Credentials.Provider;
+using AndroidX.Credentials.WebAuthn;
+using Bit.Core.Abstractions;
+using Bit.Core.Utilities;
+using Bit.App.Droid.Utilities;
+
+namespace Bit.Droid.Autofill
+{
+ [Activity(
+ NoHistory = true,
+ LaunchMode = LaunchMode.SingleTop)]
+ public class CredentialProviderSelectionActivity : MauiAppCompatActivity
+ {
+ protected override void OnCreate(Bundle bundle)
+ {
+ Intent?.Validate();
+ base.OnCreate(bundle);
+
+ var cipherId = Intent?.GetStringExtra(CredentialProviderConstants.CredentialProviderCipherId);
+ if (string.IsNullOrEmpty(cipherId))
+ {
+ SetResult(Result.Canceled);
+ Finish();
+ return;
+ }
+
+ GetCipherAndPerformPasskeyAuthAsync(cipherId).FireAndForget();
+ }
+
+ private async Task GetCipherAndPerformPasskeyAuthAsync(string cipherId)
+ {
+ // TODO this is a work in progress
+ // https://developer.android.com/training/sign-in/credential-provider#passkeys-implement
+
+ var getRequest = PendingIntentHandler.RetrieveProviderGetCredentialRequest(Intent);
+ // var publicKeyRequest = getRequest?.CredentialOptions as PublicKeyCredentialRequestOptions;
+
+ var requestInfo = Intent.GetBundleExtra(CredentialProviderConstants.CredentialDataIntentExtra);
+ var credIdEnc = requestInfo?.GetString(CredentialProviderConstants.CredentialIdIntentExtra);
+
+ var cipherService = ServiceContainer.Resolve();
+ var cipher = await cipherService.GetAsync(cipherId);
+ var decCipher = await cipher.DecryptAsync();
+
+ var passkey = decCipher.Login.Fido2Credentials.Find(f => f.CredentialId == credIdEnc);
+
+ var credId = Convert.FromBase64String(credIdEnc);
+ // var privateKey = Convert.FromBase64String(passkey.PrivateKey);
+ // var uid = Convert.FromBase64String(passkey.uid);
+
+ var origin = getRequest?.CallingAppInfo.Origin;
+ var packageName = getRequest?.CallingAppInfo.PackageName;
+
+ // --- continue WIP here (save TOTP copy as last step) ---
+
+ // Copy TOTP if needed
+ var autofillHandler = ServiceContainer.Resolve();
+ autofillHandler.Autofill(decCipher);
+ }
+ }
+}
diff --git a/src/App/Platforms/Android/Autofill/CredentialProviderService.cs b/src/App/Platforms/Android/Autofill/CredentialProviderService.cs
new file mode 100644
index 000000000..8355b0873
--- /dev/null
+++ b/src/App/Platforms/Android/Autofill/CredentialProviderService.cs
@@ -0,0 +1,147 @@
+using Android;
+using Android.App;
+using Android.Content;
+using Android.Graphics.Drawables;
+using Android.OS;
+using Android.Runtime;
+using AndroidX.Credentials.Provider;
+using Bit.Core.Abstractions;
+using Bit.Core.Utilities;
+using AndroidX.Credentials.Exceptions;
+using AndroidX.Credentials.WebAuthn;
+using Bit.Core.Models.View;
+using Resource = Microsoft.Maui.Resource;
+
+namespace Bit.Droid.Autofill
+{
+ [Service(Permission = Manifest.Permission.BindCredentialProviderService, Label = "Bitwarden", Exported = true)]
+ [IntentFilter(new string[] { "android.service.credentials.CredentialProviderService" })]
+ [MetaData("android.credentials.provider", Resource = "@xml/provider")]
+ [Register("com.x8bit.bitwarden.Autofill.CredentialProviderService")]
+ public class CredentialProviderService : AndroidX.Credentials.Provider.CredentialProviderService
+ {
+ private const string GetPasskeyIntentAction = "PACKAGE_NAME.GET_PASSKEY";
+ private const int UniqueRequestCode = 94556023;
+
+ private ICipherService _cipherService;
+ private IUserVerificationService _userVerificationService;
+ private IVaultTimeoutService _vaultTimeoutService;
+ private LazyResolve _logger = new LazyResolve("logger");
+
+ public override async void OnBeginCreateCredentialRequest(BeginCreateCredentialRequest request,
+ CancellationSignal cancellationSignal, IOutcomeReceiver callback) => throw new NotImplementedException();
+
+ public override async void OnBeginGetCredentialRequest(BeginGetCredentialRequest request,
+ CancellationSignal cancellationSignal, IOutcomeReceiver callback)
+ {
+ try
+ {
+ _vaultTimeoutService ??= ServiceContainer.Resolve();
+
+ await _vaultTimeoutService.CheckVaultTimeoutAsync();
+ var locked = await _vaultTimeoutService.IsLockedAsync();
+ if (!locked)
+ {
+ var response = await ProcessGetCredentialsRequestAsync(request);
+ callback.OnResult(response);
+ }
+ // TODO handle auth/unlock account flow
+ }
+ catch (GetCredentialException e)
+ {
+ _logger.Value.Exception(e);
+ callback.OnError(e.ErrorMessage ?? "Error getting credentials");
+ }
+ catch (Exception e)
+ {
+ _logger.Value.Exception(e);
+ throw;
+ }
+ }
+
+ private async Task ProcessGetCredentialsRequestAsync(
+ BeginGetCredentialRequest request)
+ {
+ IList credentialEntries = null;
+
+ foreach (var option in request.BeginGetCredentialOptions)
+ {
+ var credentialOption = option as BeginGetPublicKeyCredentialOption;
+ if (credentialOption != null)
+ {
+ credentialEntries ??= new List();
+ ((List)credentialEntries).AddRange(
+ await PopulatePasskeyDataAsync(request.CallingAppInfo, credentialOption));
+ }
+ }
+
+ if (credentialEntries == null)
+ {
+ return new BeginGetCredentialResponse();
+ }
+
+ return new BeginGetCredentialResponse.Builder()
+ .SetCredentialEntries(credentialEntries)
+ .Build();
+ }
+
+ private async Task> PopulatePasskeyDataAsync(CallingAppInfo callingAppInfo,
+ BeginGetPublicKeyCredentialOption option)
+ {
+ var packageName = callingAppInfo.PackageName;
+ var origin = callingAppInfo.Origin;
+ var signingInfo = callingAppInfo.SigningInfo;
+
+ var request = new PublicKeyCredentialRequestOptions(option.RequestJson);
+
+ var passkeyEntries = new List();
+
+ _cipherService ??= ServiceContainer.Resolve();
+ var ciphers = await _cipherService.GetAllDecryptedForUrlAsync(origin);
+ if (ciphers == null)
+ {
+ return passkeyEntries;
+ }
+
+ var passkeyCiphers = ciphers.Where(cipher => cipher.HasFido2Credential).ToList();
+ if (!passkeyCiphers.Any())
+ {
+ return passkeyEntries;
+ }
+
+ foreach (var cipher in passkeyCiphers)
+ {
+ var passkeyEntry = GetPasskey(cipher, option);
+ passkeyEntries.Add(passkeyEntry);
+ }
+
+ return passkeyEntries;
+ }
+
+ private PublicKeyCredentialEntry GetPasskey(CipherView cipher, BeginGetPublicKeyCredentialOption option)
+ {
+ var credDataBundle = new Bundle();
+ credDataBundle.PutString(CredentialProviderConstants.CredentialIdIntentExtra,
+ cipher.Login.MainFido2Credential.CredentialId);
+
+ var intent = new Intent(ApplicationContext, typeof(CredentialProviderSelectionActivity))
+ .SetAction(GetPasskeyIntentAction).SetPackage(Constants.PACKAGE_NAME);
+ intent.PutExtra(CredentialProviderConstants.CredentialDataIntentExtra, credDataBundle);
+ intent.PutExtra(CredentialProviderConstants.CredentialProviderCipherId, cipher.Id);
+ var pendingIntent = PendingIntent.GetActivity(ApplicationContext, UniqueRequestCode, intent,
+ PendingIntentFlags.Mutable | PendingIntentFlags.UpdateCurrent);
+
+ return new PublicKeyCredentialEntry.Builder(
+ ApplicationContext,
+ cipher.Login.Username ?? "No username",
+ pendingIntent,
+ option)
+ .SetDisplayName(cipher.Name)
+ .SetIcon(Icon.CreateWithResource(ApplicationContext, Resource.Drawable.icon))
+ .Build();
+ }
+
+ public override void OnClearCredentialStateRequest(ProviderClearCredentialStateRequest request,
+ CancellationSignal cancellationSignal, IOutcomeReceiver callback) => throw new NotImplementedException();
+ }
+}
diff --git a/src/App/Platforms/Android/Resources/xml/provider.xml b/src/App/Platforms/Android/Resources/xml/provider.xml
new file mode 100644
index 000000000..eb901638a
--- /dev/null
+++ b/src/App/Platforms/Android/Resources/xml/provider.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/App/Platforms/Android/Services/AutofillHandler.cs b/src/App/Platforms/Android/Services/AutofillHandler.cs
index 12429b841..239343b95 100644
--- a/src/App/Platforms/Android/Services/AutofillHandler.cs
+++ b/src/App/Platforms/Android/Services/AutofillHandler.cs
@@ -37,6 +37,23 @@ namespace Bit.Droid.Services
_eventService = eventService;
}
+ public bool CredentialProviderServiceEnabled()
+ {
+ if (Build.VERSION.SdkInt < BuildVersionCodes.UpsideDownCake)
+ {
+ return false;
+ }
+ try
+ {
+ // TODO - find a way to programmatically check if the credential provider service is enabled
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
public bool AutofillServiceEnabled()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
@@ -163,7 +180,14 @@ namespace Bit.Droid.Services
return Accessibility.AccessibilityHelpers.OverlayPermitted();
}
-
+ public void DisableCredentialProviderService()
+ {
+ try
+ {
+ // TODO - find a way to programmatically disable the provider service, or take the user to the settings page where they can do it
+ }
+ catch { }
+ }
public void DisableAutofillService()
{
diff --git a/src/App/Platforms/Android/Services/DeviceActionService.cs b/src/App/Platforms/Android/Services/DeviceActionService.cs
index 102f1de80..7e44c2abe 100644
--- a/src/App/Platforms/Android/Services/DeviceActionService.cs
+++ b/src/App/Platforms/Android/Services/DeviceActionService.cs
@@ -11,6 +11,7 @@ using Android.Text.Method;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
+using AndroidX.Credentials;
using Bit.App.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.App.Utilities;
@@ -490,6 +491,27 @@ namespace Bit.Droid.Services
}
}
+ public void OpenCredentialProviderSettings()
+ {
+ var activity = (MainActivity)Microsoft.Maui.ApplicationModel.Platform.CurrentActivity;
+ try
+ {
+ var pendingIntent = CredentialManager.Create(activity).CreateSettingsPendingIntent();
+ pendingIntent.Send();
+ }
+ catch (ActivityNotFoundException)
+ {
+ var alertBuilder = new AlertDialog.Builder(activity);
+ alertBuilder.SetMessage(AppResources.BitwardenCredentialProviderGoToSettings);
+ alertBuilder.SetCancelable(true);
+ alertBuilder.SetPositiveButton(AppResources.Ok, (sender, args) =>
+ {
+ (sender as AlertDialog)?.Cancel();
+ });
+ alertBuilder.Create().Show();
+ }
+ }
+
public void OpenAccessibilitySettings()
{
try
@@ -548,6 +570,8 @@ namespace Bit.Droid.Services
return true;
}
+ public bool SupportsCredentialProviderService() => Build.VERSION.SdkInt >= BuildVersionCodes.UpsideDownCake;
+
public bool SupportsAutofillServices() => Build.VERSION.SdkInt >= BuildVersionCodes.O;
public bool SupportsInlineAutofill() => Build.VERSION.SdkInt >= BuildVersionCodes.R;
diff --git a/src/Core/Abstractions/IAutofillHandler.cs b/src/Core/Abstractions/IAutofillHandler.cs
index 84c9489b9..81a8016f8 100644
--- a/src/Core/Abstractions/IAutofillHandler.cs
+++ b/src/Core/Abstractions/IAutofillHandler.cs
@@ -4,6 +4,7 @@ namespace Bit.Core.Abstractions
{
public interface IAutofillHandler
{
+ bool CredentialProviderServiceEnabled();
bool AutofillServicesEnabled();
bool SupportsAutofillService();
void Autofill(CipherView cipher);
@@ -11,6 +12,7 @@ namespace Bit.Core.Abstractions
bool AutofillAccessibilityServiceRunning();
bool AutofillAccessibilityOverlayPermitted();
bool AutofillServiceEnabled();
+ void DisableCredentialProviderService();
void DisableAutofillService();
}
}
diff --git a/src/Core/Abstractions/IDeviceActionService.cs b/src/Core/Abstractions/IDeviceActionService.cs
index 3a15fde80..8c78e3a1f 100644
--- a/src/Core/Abstractions/IDeviceActionService.cs
+++ b/src/Core/Abstractions/IDeviceActionService.cs
@@ -28,6 +28,7 @@ namespace Bit.App.Abstractions
bool SupportsNfc();
bool SupportsCamera();
bool SupportsFido2();
+ bool SupportsCredentialProviderService();
bool SupportsAutofillServices();
bool SupportsInlineAutofill();
bool SupportsDrawOver();
@@ -36,6 +37,7 @@ namespace Bit.App.Abstractions
void RateApp();
void OpenAccessibilitySettings();
void OpenAccessibilityOverlayPermissionSettings();
+ void OpenCredentialProviderSettings();
void OpenAutofillSettings();
long GetActiveTime();
void CloseMainApp();
diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
index f9d70cb33..1e9ff7e3b 100644
--- a/src/Core/Core.csproj
+++ b/src/Core/Core.csproj
@@ -49,6 +49,7 @@
+
diff --git a/src/Core/Pages/Settings/AutofillSettingsPage.xaml b/src/Core/Pages/Settings/AutofillSettingsPage.xaml
index d09643892..9799e6b1a 100644
--- a/src/Core/Pages/Settings/AutofillSettingsPage.xaml
+++ b/src/Core/Pages/Settings/AutofillSettingsPage.xaml
@@ -19,6 +19,15 @@
Text="{u:I18n Autofill}"
StyleClass="settings-header" />
+
+
DeviceInfo.Platform == DevicePlatform.Android && _deviceActionService.SupportsCredentialProviderService();
+
+ public bool UseCredentialProviderService
+ {
+ get => _useCredentialProviderService;
+ set
+ {
+ if (SetProperty(ref _useCredentialProviderService, value))
+ {
+ ((ICommand)ToggleUseCredentialProviderServiceCommand).Execute(null);
+ }
+ }
+ }
+
public bool SupportsAndroidAutofillServices => DeviceInfo.Platform == DevicePlatform.Android && _deviceActionService.SupportsAutofillServices();
public bool UseAutofillServices
@@ -84,6 +99,7 @@ namespace Bit.App.Pages
}
}
+ public AsyncRelayCommand ToggleUseCredentialProviderServiceCommand { get; private set; }
public AsyncRelayCommand ToggleUseAutofillServicesCommand { get; private set; }
public AsyncRelayCommand ToggleUseInlineAutofillCommand { get; private set; }
public AsyncRelayCommand ToggleUseAccessibilityCommand { get; private set; }
@@ -93,6 +109,7 @@ namespace Bit.App.Pages
private void InitAndroidCommands()
{
+ ToggleUseCredentialProviderServiceCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseCredentialProviderService()), () => _inited, allowsMultipleExecutions: false);
ToggleUseAutofillServicesCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseAutofillServices()), () => _inited, allowsMultipleExecutions: false);
ToggleUseInlineAutofillCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(() => ToggleUseInlineAutofillEnabledAsync()), () => _inited, allowsMultipleExecutions: false);
ToggleUseAccessibilityCommand = CreateDefaultAsyncRelayCommand(ToggleUseAccessibilityAsync, () => _inited, allowsMultipleExecutions: false);
@@ -115,6 +132,9 @@ namespace Bit.App.Pages
private async Task UpdateAndroidAutofillSettingsAsync()
{
+ // TODO - uncomment once _autofillHandler.CredentialProviderServiceEnabled() returns a real value
+ // _useCredentialProviderService =
+ // SupportsCredentialProviderService && _autofillHandler.CredentialProviderServiceEnabled();
_useAutofillServices =
_autofillHandler.SupportsAutofillService() && _autofillHandler.AutofillServiceEnabled();
_useAccessibility = _autofillHandler.AutofillAccessibilityServiceRunning();
@@ -123,6 +143,7 @@ namespace Bit.App.Pages
await MainThread.InvokeOnMainThreadAsync(() =>
{
+ TriggerPropertyChanged(nameof(UseCredentialProviderService));
TriggerPropertyChanged(nameof(UseAutofillServices));
TriggerPropertyChanged(nameof(UseAccessibility));
TriggerPropertyChanged(nameof(UseDrawOver));
@@ -130,6 +151,18 @@ namespace Bit.App.Pages
});
}
+ private void ToggleUseCredentialProviderService()
+ {
+ if (UseCredentialProviderService)
+ {
+ _deviceActionService.OpenCredentialProviderSettings();
+ }
+ else
+ {
+ _autofillHandler.DisableCredentialProviderService();
+ }
+ }
+
private void ToggleUseAutofillServices()
{
if (UseAutofillServices)
diff --git a/src/Core/Resources/Localization/AppResources.Designer.cs b/src/Core/Resources/Localization/AppResources.Designer.cs
index 8dc58b84f..de776fbc0 100644
--- a/src/Core/Resources/Localization/AppResources.Designer.cs
+++ b/src/Core/Resources/Localization/AppResources.Designer.cs
@@ -166,6 +166,15 @@ namespace Bit.Core.Resources.Localization {
}
}
+ ///
+ /// Looks up a localized string similar to Credential Provider service
+ ///
+ public static string CredentialProviderService {
+ get {
+ return ResourceManager.GetString("CredentialProviderService", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Bitwarden needs attention - See "Auto-fill Accessibility Service" from Bitwarden settings.
///
@@ -7667,6 +7676,15 @@ namespace Bit.Core.Resources.Localization {
}
}
+ ///
+ /// Looks up a localized string similar to We were unable to automatically open the Android credential provider settings menu for you. You can navigate to the credential provider settings menu manually from Android Settings > System > Passwords & accounts > Passwords, passkeys and data services.
+ ///
+ public static string BitwardenCredentialProviderGoToSettings {
+ get {
+ return ResourceManager.GetString("BitwardenCredentialProviderGoToSettings", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Word separator.
///
@@ -7685,6 +7703,15 @@ namespace Bit.Core.Resources.Localization {
}
}
+ ///
+ /// Looks up a localized string similar to The Android Credential Provider is used for managing passkeys for use with websites and other apps on your device.
+ ///
+ public static string CredentialProviderServiceExplanationLong {
+ get {
+ return ResourceManager.GetString("CredentialProviderServiceExplanationLong", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to {0} hours and one minute.
///
diff --git a/src/Core/Resources/Localization/AppResources.resx b/src/Core/Resources/Localization/AppResources.resx
index 4efdb26f7..325984903 100644
--- a/src/Core/Resources/Localization/AppResources.resx
+++ b/src/Core/Resources/Localization/AppResources.resx
@@ -1191,6 +1191,9 @@ Scanning will happen automatically.
Windows Hello
+
+ We were unable to automatically open the Android credential provider settings menu for you. You can navigate to the credential provider settings menu manually from Android Settings > System > Passwords & accounts > Passwords, passkeys and data services.
+
We were unable to automatically open the Android autofill settings menu for you. You can navigate to the autofill settings menu manually from Android Settings > System > Languages and input > Advanced > Autofill service.
@@ -1816,6 +1819,9 @@ Scanning will happen automatically.
Bitwarden needs attention - Turn on "Draw-Over" in "Auto-fill Services" from Bitwarden Settings
+
+ Credential Provider service
+
Auto-fill services
@@ -2799,6 +2805,9 @@ Do you want to switch to this account?
{0} hours
+
+ The Android Credential Provider is used for managing passkeys for use with websites and other apps on your device.
+
The Android Autofill Framework is used to assist in filling login information into other apps on your device.
diff --git a/src/Xamarin.AndroidX.Credentials/Additions/AboutAdditions.txt b/src/Xamarin.AndroidX.Credentials/Additions/AboutAdditions.txt
new file mode 100644
index 000000000..0b67abfbe
--- /dev/null
+++ b/src/Xamarin.AndroidX.Credentials/Additions/AboutAdditions.txt
@@ -0,0 +1,48 @@
+Additions allow you to add arbitrary C# to the generated classes
+before they are compiled. This can be helpful for providing convenience
+methods or adding pure C# classes.
+
+== Adding Methods to Generated Classes ==
+
+Let's say the library being bound has a Rectangle class with a constructor
+that takes an x and y position, and a width and length size. It will look like
+this:
+
+public partial class Rectangle
+{
+ public Rectangle (int x, int y, int width, int height)
+ {
+ // JNI bindings
+ }
+}
+
+Imagine we want to add a constructor to this class that takes a Point and
+Size structure instead of 4 ints. We can add a new file called Rectangle.cs
+with a partial class containing our new method:
+
+public partial class Rectangle
+{
+ public Rectangle (Point location, Size size) :
+ this (location.X, location.Y, size.Width, size.Height)
+ {
+ }
+}
+
+At compile time, the additions class will be added to the generated class
+and the final assembly will a Rectangle class with both constructors.
+
+
+== Adding C# Classes ==
+
+Another thing that can be done is adding fully C# managed classes to the
+generated library. In the above example, let's assume that there isn't a
+Point class available in Java or our library. The one we create doesn't need
+to interact with Java, so we'll create it like a normal class in C#.
+
+By adding a Point.cs file with this class, it will end up in the binding library:
+
+public class Point
+{
+ public int X { get; set; }
+ public int Y { get; set; }
+}
diff --git a/src/Xamarin.AndroidX.Credentials/Transforms/EnumFields.xml b/src/Xamarin.AndroidX.Credentials/Transforms/EnumFields.xml
new file mode 100644
index 000000000..1e775a13d
--- /dev/null
+++ b/src/Xamarin.AndroidX.Credentials/Transforms/EnumFields.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/src/Xamarin.AndroidX.Credentials/Transforms/EnumMethods.xml b/src/Xamarin.AndroidX.Credentials/Transforms/EnumMethods.xml
new file mode 100644
index 000000000..3b33e4d83
--- /dev/null
+++ b/src/Xamarin.AndroidX.Credentials/Transforms/EnumMethods.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/src/Xamarin.AndroidX.Credentials/Transforms/Metadata.xml b/src/Xamarin.AndroidX.Credentials/Transforms/Metadata.xml
new file mode 100644
index 000000000..2b28667f9
--- /dev/null
+++ b/src/Xamarin.AndroidX.Credentials/Transforms/Metadata.xml
@@ -0,0 +1,12 @@
+
+ AndroidX.Credentials
+ AndroidX.Credentials.Provider
+ AndroidX.Credentials.Exceptions
+ AndroidX.Credentials.WebAuthn
+
+
+ CompanionStatic
+
+ DefaultStatic
+
+
diff --git a/src/Xamarin.AndroidX.Credentials/Xamarin.AndroidX.Credentials.csproj b/src/Xamarin.AndroidX.Credentials/Xamarin.AndroidX.Credentials.csproj
new file mode 100644
index 000000000..f16aa48f0
--- /dev/null
+++ b/src/Xamarin.AndroidX.Credentials/Xamarin.AndroidX.Credentials.csproj
@@ -0,0 +1,18 @@
+
+
+ net8.0-android
+ 21
+
+
+
+
+ enable
+ enable
+ XamarinBinding.AndroidX.Credentials
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Xamarin.AndroidX.Credentials/credentials-1.2.0.aar b/src/Xamarin.AndroidX.Credentials/credentials-1.2.0.aar
new file mode 100644
index 000000000..a3fdcfb52
Binary files /dev/null and b/src/Xamarin.AndroidX.Credentials/credentials-1.2.0.aar differ
diff --git a/src/iOS.Core/Services/AutofillHandler.cs b/src/iOS.Core/Services/AutofillHandler.cs
index 78e677493..aa5325f2a 100644
--- a/src/iOS.Core/Services/AutofillHandler.cs
+++ b/src/iOS.Core/Services/AutofillHandler.cs
@@ -11,9 +11,11 @@ namespace Bit.iOS.Core.Services
{
public bool SupportsAutofillService() => false;
public bool AutofillServiceEnabled() => false;
+ public void DisableCredentialProviderService() => throw new NotImplementedException();
public void Autofill(CipherView cipher) => throw new NotImplementedException();
public bool AutofillAccessibilityOverlayPermitted() => false;
public bool AutofillAccessibilityServiceRunning() => false;
+ public bool CredentialProviderServiceEnabled() => throw new NotImplementedException();
public bool AutofillServicesEnabled() => false;
public void CloseAutofill() => throw new NotImplementedException();
public void DisableAutofillService() => throw new NotImplementedException();
diff --git a/src/iOS.Core/Services/DeviceActionService.cs b/src/iOS.Core/Services/DeviceActionService.cs
index 36f13679f..81c67baae 100644
--- a/src/iOS.Core/Services/DeviceActionService.cs
+++ b/src/iOS.Core/Services/DeviceActionService.cs
@@ -301,6 +301,8 @@ namespace Bit.iOS.Core.Services
throw new NotImplementedException();
}
+ public void OpenCredentialProviderSettings() => throw new NotImplementedException();
+
public void OpenAutofillSettings()
{
throw new NotImplementedException();
@@ -339,6 +341,8 @@ namespace Bit.iOS.Core.Services
return false;
}
+ public bool SupportsCredentialProviderService() => throw new NotImplementedException();
+
public bool SupportsAutofillServices() => UIDevice.CurrentDevice.CheckSystemVersion(12, 0);
public bool SupportsInlineAutofill() => false;
public bool SupportsDrawOver() => false;