diff --git a/src/Android/Autofill/AutofillHelpers.cs b/src/Android/Autofill/AutofillHelpers.cs index 6d08c51d4..8281452a9 100644 --- a/src/Android/Autofill/AutofillHelpers.cs +++ b/src/Android/Autofill/AutofillHelpers.cs @@ -58,12 +58,14 @@ namespace Bit.Android.Autofill return null; } - public static FillResponse BuildAuthResponse(Context context, FieldCollection fields) + public static FillResponse BuildAuthResponse(Context context, FieldCollection fields, string uri) { var responseBuilder = new FillResponse.Builder(); var view = BuildListView(context.PackageName, "Autofill with bitwarden", "Vault locked", Resource.Drawable.icon); - var intent = new Intent(context, typeof(AuthActivity)); + var intent = new Intent(context, typeof(MainActivity)); + intent.PutExtra("uri", uri); + intent.PutExtra("autofillFramework", true); var pendingIntent = PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent); responseBuilder.SetAuthentication(fields.AutofillIds.ToArray(), pendingIntent.IntentSender, view); return responseBuilder.Build(); diff --git a/src/Android/Autofill/AutofillService.cs b/src/Android/Autofill/AutofillService.cs index 72851c472..35a66e5c6 100644 --- a/src/Android/Autofill/AutofillService.cs +++ b/src/Android/Autofill/AutofillService.cs @@ -4,9 +4,7 @@ using Android.Content; using Android.OS; using Android.Runtime; using Android.Service.Autofill; -using Android.Views; using Bit.App.Abstractions; -using System.Collections.Generic; using System.Linq; using XLabs.Ioc; @@ -42,9 +40,10 @@ namespace Bit.Android.Autofill _lockService = Resolver.Resolve(); } - if(true) // if locked + var isLocked = (await _lockService.GetLockTypeAsync(false)) != App.Enums.LockType.None; + if(isLocked) { - var authResponse = AutofillHelpers.BuildAuthResponse(this, parser.FieldCollection); + var authResponse = AutofillHelpers.BuildAuthResponse(this, parser.FieldCollection, parser.Uri); callback.OnSuccess(authResponse); return; } diff --git a/src/Android/Autofill/CipherFilledItem.cs b/src/Android/Autofill/CipherFilledItem.cs index b99819793..f9ae98696 100644 --- a/src/Android/Autofill/CipherFilledItem.cs +++ b/src/Android/Autofill/CipherFilledItem.cs @@ -1,28 +1,45 @@ using System; -using System.Collections.Generic; using Android.Service.Autofill; -using Android.Views; using Android.Views.Autofill; using System.Linq; using Android.Text; using Bit.App.Models; using Bit.App.Enums; +using Bit.App.Models.Page; namespace Bit.Android.Autofill { public class CipherFilledItem : IFilledItem { - private readonly Cipher _cipher; + private Lazy _password; public CipherFilledItem(Cipher cipher) { - _cipher = cipher; Name = cipher.Name?.Decrypt() ?? "--"; + Type = cipher.Type; - switch(cipher.Type) + switch(Type) { case CipherType.Login: - Subtitle = _cipher.Login.Username?.Decrypt() ?? string.Empty; + Subtitle = cipher.Login.Username?.Decrypt() ?? string.Empty; + _password = new Lazy(() => cipher.Login.Password?.Decrypt()); + Icon = Resource.Drawable.login; + break; + default: + break; + } + } + + public CipherFilledItem(VaultListPageModel.Cipher cipher) + { + Name = cipher.Name ?? "--"; + Type = cipher.Type; + + switch(Type) + { + case CipherType.Login: + Subtitle = cipher.LoginUsername ?? string.Empty; + _password = cipher.LoginPassword; Icon = Resource.Drawable.login; break; default: @@ -33,10 +50,11 @@ namespace Bit.Android.Autofill public string Name { get; set; } public string Subtitle { get; set; } = string.Empty; public int Icon { get; set; } = Resource.Drawable.login; + public CipherType Type { get; set; } public bool ApplyToFields(FieldCollection fieldCollection, Dataset.Builder datasetBuilder) { - if(_cipher.Type == CipherType.Login && _cipher.Login != null) + if(Type == CipherType.Login) { var passwordField = fieldCollection.Fields.FirstOrDefault( f => f.InputType.HasFlag(InputTypes.TextVariationPassword)); @@ -51,13 +69,12 @@ namespace Bit.Android.Autofill return false; } - var password = _cipher.Login.Password?.Decrypt(); - if(string.IsNullOrWhiteSpace(password)) + if(string.IsNullOrWhiteSpace(_password.Value)) { return false; } - datasetBuilder.SetValue(passwordField.AutofillId, AutofillValue.ForText(password)); + datasetBuilder.SetValue(passwordField.AutofillId, AutofillValue.ForText(_password.Value)); var usernameField = fieldCollection.Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault(); if(usernameField != null) diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index 74ee00cd8..e6e399f57 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -19,6 +19,10 @@ using Android.Nfc; using Android.Views.InputMethods; using System.IO; using System.Linq; +using Android.Views.Autofill; +using Android.App.Assist; +using Bit.Android.Autofill; +using System.Collections.Generic; namespace Bit.Android { @@ -131,35 +135,71 @@ namespace Bit.Android private void ReturnCredentials(VaultListPageModel.Cipher cipher) { - Intent data = new Intent(); - if(cipher == null) + if(Intent.GetBooleanExtra("autofillFramework", false)) { - data.PutExtra("canceled", "true"); - } - else - { - var isPremium = Resolver.Resolve()?.TokenPremium ?? false; - var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false); - if(isPremium && autoCopyEnabled && _deviceActionService != null && cipher.LoginTotp?.Value != null) + if(cipher == null) { - _deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(cipher.LoginTotp.Value)); + SetResult(Result.Canceled); + Finish(); + return; } - data.PutExtra("uri", cipher.LoginUri); - data.PutExtra("username", cipher.LoginUsername); - data.PutExtra("password", cipher.LoginPassword?.Value ?? null); - } + var structure = Intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure) as AssistStructure; + if(structure == null) + { + SetResult(Result.Canceled); + Finish(); + return; + } - if(Parent == null) - { - SetResult(Result.Ok, data); + var parser = new Parser(structure); + parser.ParseForFill(); + if(!parser.FieldCollection.Fields.Any() || string.IsNullOrWhiteSpace(parser.Uri)) + { + SetResult(Result.Canceled); + Finish(); + return; + } + + var items = new List { new CipherFilledItem(cipher) }; + var response = AutofillHelpers.BuildFillResponse(this, parser.FieldCollection, items); + var replyIntent = new Intent(); + replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, response); + SetResult(Result.Ok, replyIntent); + Finish(); } else { - Parent.SetResult(Result.Ok, data); - } + var data = new Intent(); + if(cipher == null) + { + data.PutExtra("canceled", "true"); + } + else + { + var isPremium = Resolver.Resolve()?.TokenPremium ?? false; + var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false); + if(isPremium && autoCopyEnabled && _deviceActionService != null && cipher.LoginTotp?.Value != null) + { + _deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(cipher.LoginTotp.Value)); + } - Finish(); + data.PutExtra("uri", cipher.LoginUri); + data.PutExtra("username", cipher.LoginUsername); + data.PutExtra("password", cipher.LoginPassword?.Value ?? null); + } + + if(Parent == null) + { + SetResult(Result.Ok, data); + } + else + { + Parent.SetResult(Result.Ok, data); + } + + Finish(); + } } protected override void OnPause()