mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
add support for card filling
This commit is contained in:
parent
4b24fe1bf4
commit
c45a77d538
8 changed files with 248 additions and 97 deletions
|
@ -2,26 +2,38 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Service.Autofill;
|
using Android.Service.Autofill;
|
||||||
using Android.Views;
|
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
|
||||||
namespace Bit.Android.Autofill
|
namespace Bit.Android.Autofill
|
||||||
{
|
{
|
||||||
public static class AutofillHelpers
|
public static class AutofillHelpers
|
||||||
{
|
{
|
||||||
public static async Task<List<IFilledItem>> GetFillItemsAsync(ICipherService service, string uri)
|
public static async Task<List<IFilledItem>> GetFillItemsAsync(Parser parser, ICipherService service)
|
||||||
{
|
{
|
||||||
var items = new List<IFilledItem>();
|
var items = new List<IFilledItem>();
|
||||||
var ciphers = await service.GetAllAsync(uri);
|
|
||||||
if(ciphers.Item1.Any() || ciphers.Item2.Any())
|
if(parser.FieldCollection.FillableForLogin)
|
||||||
{
|
{
|
||||||
var allCiphers = ciphers.Item1.ToList();
|
var ciphers = await service.GetAllAsync(parser.Uri);
|
||||||
allCiphers.AddRange(ciphers.Item2.ToList());
|
if(ciphers.Item1.Any() || ciphers.Item2.Any())
|
||||||
foreach(var cipher in allCiphers)
|
{
|
||||||
|
var allCiphers = ciphers.Item1.ToList();
|
||||||
|
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||||
|
foreach(var cipher in allCiphers)
|
||||||
|
{
|
||||||
|
items.Add(new CipherFilledItem(cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(parser.FieldCollection.FillableForCard)
|
||||||
|
{
|
||||||
|
var ciphers = await service.GetAllAsync();
|
||||||
|
foreach(var cipher in ciphers.Where(c => c.Type == App.Enums.CipherType.Card))
|
||||||
{
|
{
|
||||||
items.Add(new CipherFilledItem(cipher));
|
items.Add(new CipherFilledItem(cipher));
|
||||||
}
|
}
|
||||||
|
@ -30,14 +42,14 @@ namespace Bit.Android.Autofill
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FillResponse BuildFillResponse(Context context, FieldCollection fields, List<IFilledItem> items)
|
public static FillResponse BuildFillResponse(Context context, Parser parser, List<IFilledItem> items)
|
||||||
{
|
{
|
||||||
var responseBuilder = new FillResponse.Builder();
|
var responseBuilder = new FillResponse.Builder();
|
||||||
if(items != null && items.Count > 0)
|
if(items != null && items.Count > 0)
|
||||||
{
|
{
|
||||||
foreach(var item in items)
|
foreach(var item in items)
|
||||||
{
|
{
|
||||||
var dataset = BuildDataset(context, fields, item);
|
var dataset = BuildDataset(context, parser.FieldCollection, item);
|
||||||
if(dataset != null)
|
if(dataset != null)
|
||||||
{
|
{
|
||||||
responseBuilder.AddDataset(dataset);
|
responseBuilder.AddDataset(dataset);
|
||||||
|
@ -45,8 +57,8 @@ namespace Bit.Android.Autofill
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddSaveInfo(responseBuilder, fields);
|
AddSaveInfo(responseBuilder, parser.FieldCollection);
|
||||||
responseBuilder.SetIgnoredIds(fields.IgnoreAutofillIds.ToArray());
|
responseBuilder.SetIgnoredIds(parser.FieldCollection.IgnoreAutofillIds.ToArray());
|
||||||
return responseBuilder.Build();
|
return responseBuilder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +76,8 @@ namespace Bit.Android.Autofill
|
||||||
public static FillResponse BuildAuthResponse(Context context, FieldCollection fields, string uri)
|
public static FillResponse BuildAuthResponse(Context context, FieldCollection fields, string uri)
|
||||||
{
|
{
|
||||||
var responseBuilder = new FillResponse.Builder();
|
var responseBuilder = new FillResponse.Builder();
|
||||||
var view = BuildListView(context.PackageName, "Auto-fill with bitwarden",
|
var view = BuildListView(context.PackageName, AppResources.AutofillWithBitwarden,
|
||||||
"Vault is locked", Resource.Drawable.icon);
|
AppResources.VaultIsLocked, Resource.Drawable.icon);
|
||||||
var intent = new Intent(context, typeof(MainActivity));
|
var intent = new Intent(context, typeof(MainActivity));
|
||||||
intent.PutExtra("autofillFramework", true);
|
intent.PutExtra("autofillFramework", true);
|
||||||
intent.PutExtra("autofillFrameworkUri", uri);
|
intent.PutExtra("autofillFrameworkUri", uri);
|
||||||
|
@ -87,36 +99,14 @@ namespace Bit.Android.Autofill
|
||||||
|
|
||||||
public static void AddSaveInfo(FillResponse.Builder responseBuilder, FieldCollection fields)
|
public static void AddSaveInfo(FillResponse.Builder responseBuilder, FieldCollection fields)
|
||||||
{
|
{
|
||||||
var saveInfo = new SaveInfo.Builder(SaveDataType.Password, fields.AutofillIds.ToArray()).Build();
|
var saveType = fields.SaveType;
|
||||||
responseBuilder.SetSaveInfo(saveInfo);
|
if(saveType == SaveDataType.Generic)
|
||||||
}
|
|
||||||
|
|
||||||
public static List<string> FilterForSupportedHints(string[] hints)
|
|
||||||
{
|
|
||||||
return hints?.Where(h => IsValidHint(h)).ToList() ?? new List<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsValidHint(string hint)
|
|
||||||
{
|
|
||||||
switch(hint)
|
|
||||||
{
|
{
|
||||||
case View.AutofillHintCreditCardExpirationDate:
|
return;
|
||||||
case View.AutofillHintCreditCardExpirationDay:
|
|
||||||
case View.AutofillHintCreditCardExpirationMonth:
|
|
||||||
case View.AutofillHintCreditCardExpirationYear:
|
|
||||||
case View.AutofillHintCreditCardNumber:
|
|
||||||
case View.AutofillHintCreditCardSecurityCode:
|
|
||||||
case View.AutofillHintEmailAddress:
|
|
||||||
case View.AutofillHintPhone:
|
|
||||||
case View.AutofillHintName:
|
|
||||||
case View.AutofillHintPassword:
|
|
||||||
case View.AutofillHintPostalAddress:
|
|
||||||
case View.AutofillHintPostalCode:
|
|
||||||
case View.AutofillHintUsername:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var saveInfo = new SaveInfo.Builder(saveType, fields.AutofillIds.ToArray()).Build();
|
||||||
|
responseBuilder.SetSaveInfo(saveInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,7 +34,8 @@ namespace Bit.Android.Autofill
|
||||||
parser.Parse();
|
parser.Parse();
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(parser.Uri) || parser.Uri == "androidapp://com.x8bit.bitwarden" ||
|
if(string.IsNullOrWhiteSpace(parser.Uri) || parser.Uri == "androidapp://com.x8bit.bitwarden" ||
|
||||||
parser.Uri == "androidapp://android" || !parser.FieldCollection.FillableForLogin)
|
parser.Uri == "androidapp://android" ||
|
||||||
|
(!parser.FieldCollection.FillableForLogin && !parser.FieldCollection.FillableForCard))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -58,8 +59,8 @@ namespace Bit.Android.Autofill
|
||||||
}
|
}
|
||||||
|
|
||||||
// build response
|
// build response
|
||||||
var items = await AutofillHelpers.GetFillItemsAsync(_cipherService, parser.Uri);
|
var items = await AutofillHelpers.GetFillItemsAsync(parser, _cipherService);
|
||||||
var response = AutofillHelpers.BuildFillResponse(this, parser.FieldCollection, items);
|
var response = AutofillHelpers.BuildFillResponse(this, parser, items);
|
||||||
callback.OnSuccess(response);
|
callback.OnSuccess(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,17 @@ using System.Linq;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Enums;
|
using Bit.App.Enums;
|
||||||
using Bit.App.Models.Page;
|
using Bit.App.Models.Page;
|
||||||
|
using Android.Views;
|
||||||
|
|
||||||
namespace Bit.Android.Autofill
|
namespace Bit.Android.Autofill
|
||||||
{
|
{
|
||||||
public class CipherFilledItem : IFilledItem
|
public class CipherFilledItem : IFilledItem
|
||||||
{
|
{
|
||||||
private Lazy<string> _password;
|
private Lazy<string> _password;
|
||||||
|
private string _cardNumber;
|
||||||
|
private Lazy<string> _cardExpMonth;
|
||||||
|
private Lazy<string> _cardExpYear;
|
||||||
|
private Lazy<string> _cardCode;
|
||||||
|
|
||||||
public CipherFilledItem(Cipher cipher)
|
public CipherFilledItem(Cipher cipher)
|
||||||
{
|
{
|
||||||
|
@ -21,8 +26,24 @@ namespace Bit.Android.Autofill
|
||||||
{
|
{
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
Subtitle = cipher.Login.Username?.Decrypt() ?? string.Empty;
|
Subtitle = cipher.Login.Username?.Decrypt() ?? string.Empty;
|
||||||
_password = new Lazy<string>(() => cipher.Login.Password?.Decrypt());
|
|
||||||
Icon = Resource.Drawable.login;
|
Icon = Resource.Drawable.login;
|
||||||
|
_password = new Lazy<string>(() => cipher.Login.Password?.Decrypt());
|
||||||
|
break;
|
||||||
|
case CipherType.Card:
|
||||||
|
Subtitle = cipher.Card.Brand?.Decrypt();
|
||||||
|
_cardNumber = cipher.Card.Number?.Decrypt();
|
||||||
|
if(!string.IsNullOrWhiteSpace(_cardNumber) && _cardNumber.Length >= 4)
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(_cardNumber))
|
||||||
|
{
|
||||||
|
Subtitle += ", ";
|
||||||
|
}
|
||||||
|
Subtitle += ("*" + _cardNumber.Substring(_cardNumber.Length - 4));
|
||||||
|
}
|
||||||
|
Icon = Resource.Drawable.card;
|
||||||
|
_cardCode = new Lazy<string>(() => cipher.Card.Code?.Decrypt());
|
||||||
|
_cardExpMonth = new Lazy<string>(() => cipher.Card.ExpMonth?.Decrypt());
|
||||||
|
_cardExpYear = new Lazy<string>(() => cipher.Card.ExpYear?.Decrypt());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -58,32 +79,68 @@ namespace Bit.Android.Autofill
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var setValues = false;
|
||||||
if(Type == CipherType.Login)
|
if(Type == CipherType.Login)
|
||||||
{
|
{
|
||||||
if(!fieldCollection.PasswordFields.Any() || string.IsNullOrWhiteSpace(_password.Value))
|
if(fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password.Value))
|
||||||
{
|
{
|
||||||
return false;
|
foreach(var f in fieldCollection.PasswordFields)
|
||||||
}
|
{
|
||||||
|
setValues = true;
|
||||||
foreach(var passwordField in fieldCollection.PasswordFields)
|
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(_password.Value));
|
||||||
{
|
}
|
||||||
datasetBuilder.SetValue(passwordField.AutofillId, AutofillValue.ForText(_password.Value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
|
if(fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
|
||||||
{
|
{
|
||||||
foreach(var usernameField in fieldCollection.UsernameFields)
|
foreach(var f in fieldCollection.UsernameFields)
|
||||||
{
|
{
|
||||||
datasetBuilder.SetValue(usernameField.AutofillId, AutofillValue.ForText(Subtitle));
|
setValues = true;
|
||||||
|
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(Subtitle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else
|
else if(Type == CipherType.Card)
|
||||||
{
|
{
|
||||||
return false;
|
if(fieldCollection.HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber) &&
|
||||||
|
!string.IsNullOrWhiteSpace(_cardNumber))
|
||||||
|
{
|
||||||
|
foreach(var f in fieldCollection.HintToFieldsMap[View.AutofillHintCreditCardNumber])
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(_cardNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fieldCollection.HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardSecurityCode) &&
|
||||||
|
!string.IsNullOrWhiteSpace(_cardCode.Value))
|
||||||
|
{
|
||||||
|
foreach(var f in fieldCollection.HintToFieldsMap[View.AutofillHintCreditCardSecurityCode])
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(_cardCode.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fieldCollection.HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationMonth) &&
|
||||||
|
!string.IsNullOrWhiteSpace(_cardExpMonth.Value))
|
||||||
|
{
|
||||||
|
foreach(var f in fieldCollection.HintToFieldsMap[View.AutofillHintCreditCardExpirationMonth])
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(_cardExpMonth.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fieldCollection.HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationYear) &&
|
||||||
|
!string.IsNullOrWhiteSpace(_cardExpYear.Value))
|
||||||
|
{
|
||||||
|
foreach(var f in fieldCollection.HintToFieldsMap[View.AutofillHintCreditCardExpirationYear])
|
||||||
|
{
|
||||||
|
setValues = true;
|
||||||
|
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(_cardExpYear.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return setValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,7 +23,7 @@ namespace Bit.Android.Autofill
|
||||||
Selected = node.IsSelected;
|
Selected = node.IsSelected;
|
||||||
Clickable = node.IsClickable;
|
Clickable = node.IsClickable;
|
||||||
Visible = node.Visibility == ViewStates.Visible;
|
Visible = node.Visibility == ViewStates.Visible;
|
||||||
Hints = AutofillHelpers.FilterForSupportedHints(node.GetAutofillHints());
|
Hints = FilterForSupportedHints(node.GetAutofillHints());
|
||||||
AutofillOptions = node.GetAutofillOptions()?.ToList();
|
AutofillOptions = node.GetAutofillOptions()?.ToList();
|
||||||
|
|
||||||
if(node.AutofillValue != null)
|
if(node.AutofillValue != null)
|
||||||
|
@ -168,5 +168,33 @@ namespace Bit.Android.Autofill
|
||||||
result = 31 * result + (ToggleValue != null ? ToggleValue.GetHashCode() : 0);
|
result = 31 * result + (ToggleValue != null ? ToggleValue.GetHashCode() : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<string> FilterForSupportedHints(string[] hints)
|
||||||
|
{
|
||||||
|
return hints?.Where(h => IsValidHint(h)).ToList() ?? new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidHint(string hint)
|
||||||
|
{
|
||||||
|
switch(hint)
|
||||||
|
{
|
||||||
|
case View.AutofillHintCreditCardExpirationDate:
|
||||||
|
case View.AutofillHintCreditCardExpirationDay:
|
||||||
|
case View.AutofillHintCreditCardExpirationMonth:
|
||||||
|
case View.AutofillHintCreditCardExpirationYear:
|
||||||
|
case View.AutofillHintCreditCardNumber:
|
||||||
|
case View.AutofillHintCreditCardSecurityCode:
|
||||||
|
case View.AutofillHintEmailAddress:
|
||||||
|
case View.AutofillHintPhone:
|
||||||
|
case View.AutofillHintName:
|
||||||
|
case View.AutofillHintPassword:
|
||||||
|
case View.AutofillHintPostalAddress:
|
||||||
|
case View.AutofillHintPostalCode:
|
||||||
|
case View.AutofillHintUsername:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,24 @@ namespace Bit.Android.Autofill
|
||||||
|
|
||||||
public HashSet<int> Ids { get; private set; } = new HashSet<int>();
|
public HashSet<int> Ids { get; private set; } = new HashSet<int>();
|
||||||
public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>();
|
public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>();
|
||||||
public SaveDataType SaveType { get; private set; } = SaveDataType.Generic;
|
public SaveDataType SaveType
|
||||||
public List<string> Hints { get; private set; } = new List<string>();
|
{
|
||||||
public List<string> FocusedHints { get; private set; } = new List<string>();
|
get
|
||||||
|
{
|
||||||
|
if(FillableForLogin)
|
||||||
|
{
|
||||||
|
return SaveDataType.Password;
|
||||||
|
}
|
||||||
|
else if(FillableForCard)
|
||||||
|
{
|
||||||
|
return SaveDataType.CreditCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SaveDataType.Generic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public HashSet<string> Hints { get; private set; } = new HashSet<string>();
|
||||||
|
public HashSet<string> FocusedHints { get; private set; } = new HashSet<string>();
|
||||||
public List<Field> Fields { get; private set; } = new List<Field>();
|
public List<Field> Fields { get; private set; } = new List<Field>();
|
||||||
public IDictionary<int, Field> IdToFieldMap { get; private set; } =
|
public IDictionary<int, Field> IdToFieldMap { get; private set; } =
|
||||||
new Dictionary<int, Field>();
|
new Dictionary<int, Field>();
|
||||||
|
@ -35,7 +50,11 @@ namespace Bit.Android.Autofill
|
||||||
|
|
||||||
if(Hints.Any())
|
if(Hints.Any())
|
||||||
{
|
{
|
||||||
_passwordFields = Fields.Where(f => f.Hints.Contains(View.AutofillHintPassword)).ToList();
|
_passwordFields = new List<Field>();
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
|
||||||
|
{
|
||||||
|
_passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -59,15 +78,20 @@ namespace Bit.Android.Autofill
|
||||||
return _usernameFields;
|
return _usernameFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_usernameFields = new List<Field>();
|
||||||
if(Hints.Any())
|
if(Hints.Any())
|
||||||
{
|
{
|
||||||
_usernameFields = Fields
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintEmailAddress))
|
||||||
.Where(f => f.Hints.Any(fh => fh == View.AutofillHintEmailAddress || fh == View.AutofillHintUsername))
|
{
|
||||||
.ToList();
|
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintEmailAddress]);
|
||||||
|
}
|
||||||
|
if(HintToFieldsMap.ContainsKey(View.AutofillHintUsername))
|
||||||
|
{
|
||||||
|
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_usernameFields = new List<Field>();
|
|
||||||
foreach(var passwordField in PasswordFields)
|
foreach(var passwordField in PasswordFields)
|
||||||
{
|
{
|
||||||
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault();
|
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault();
|
||||||
|
@ -82,7 +106,15 @@ namespace Bit.Android.Autofill
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FillableForLogin => UsernameFields.Any(f => f.Focused) || PasswordFields.Any(f => f.Focused);
|
public bool FillableForLogin => FocusedHintsContain(
|
||||||
|
new string[] { View.AutofillHintUsername, View.AutofillHintEmailAddress, View.AutofillHintPassword }) ||
|
||||||
|
UsernameFields.Any(f => f.Focused) || PasswordFields.Any(f => f.Focused);
|
||||||
|
public bool FillableForCard => FocusedHintsContain(
|
||||||
|
new string[] { View.AutofillHintCreditCardNumber, View.AutofillHintCreditCardExpirationMonth,
|
||||||
|
View.AutofillHintCreditCardExpirationYear, View.AutofillHintCreditCardSecurityCode});
|
||||||
|
public bool FillableForIdentity => FocusedHintsContain(
|
||||||
|
new string[] { View.AutofillHintName, View.AutofillHintPhone, View.AutofillHintPostalAddress,
|
||||||
|
View.AutofillHintPostalCode });
|
||||||
|
|
||||||
public void Add(Field field)
|
public void Add(Field field)
|
||||||
{
|
{
|
||||||
|
@ -95,20 +127,19 @@ namespace Bit.Android.Autofill
|
||||||
|
|
||||||
Ids.Add(field.Id);
|
Ids.Add(field.Id);
|
||||||
Fields.Add(field);
|
Fields.Add(field);
|
||||||
SaveType |= field.SaveType;
|
|
||||||
AutofillIds.Add(field.AutofillId);
|
AutofillIds.Add(field.AutofillId);
|
||||||
IdToFieldMap.Add(field.Id, field);
|
IdToFieldMap.Add(field.Id, field);
|
||||||
|
|
||||||
if((field.Hints?.Count ?? 0) > 0)
|
if(field.Hints != null)
|
||||||
{
|
{
|
||||||
Hints.AddRange(field.Hints);
|
|
||||||
if(field.Focused)
|
|
||||||
{
|
|
||||||
FocusedHints.AddRange(field.Hints);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(var hint in field.Hints)
|
foreach(var hint in field.Hints)
|
||||||
{
|
{
|
||||||
|
Hints.Add(hint);
|
||||||
|
if(field.Focused)
|
||||||
|
{
|
||||||
|
FocusedHints.Add(hint);
|
||||||
|
}
|
||||||
|
|
||||||
if(!HintToFieldsMap.ContainsKey(hint))
|
if(!HintToFieldsMap.ContainsKey(hint))
|
||||||
{
|
{
|
||||||
HintToFieldsMap.Add(hint, new List<Field>());
|
HintToFieldsMap.Add(hint, new List<Field>());
|
||||||
|
@ -121,33 +152,53 @@ namespace Bit.Android.Autofill
|
||||||
|
|
||||||
public SavedItem GetSavedItem()
|
public SavedItem GetSavedItem()
|
||||||
{
|
{
|
||||||
if(!Fields?.Any() ?? true)
|
if(SaveType == SaveDataType.Password)
|
||||||
{
|
{
|
||||||
return null;
|
var passwordField = PasswordFields.FirstOrDefault(f => !string.IsNullOrWhiteSpace(f.TextValue));
|
||||||
}
|
if(passwordField == null)
|
||||||
|
|
||||||
var passwordField = PasswordFields.FirstOrDefault(f => !string.IsNullOrWhiteSpace(f.TextValue));
|
|
||||||
if(passwordField == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var savedItem = new SavedItem
|
|
||||||
{
|
|
||||||
Type = App.Enums.CipherType.Login,
|
|
||||||
Login = new SavedItem.LoginItem
|
|
||||||
{
|
{
|
||||||
Password = passwordField.TextValue
|
return null;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault();
|
var savedItem = new SavedItem
|
||||||
if(usernameField != null && !string.IsNullOrWhiteSpace(usernameField.TextValue))
|
{
|
||||||
|
Type = App.Enums.CipherType.Login,
|
||||||
|
Login = new SavedItem.LoginItem
|
||||||
|
{
|
||||||
|
Password = passwordField.TextValue
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault();
|
||||||
|
if(usernameField != null && !string.IsNullOrWhiteSpace(usernameField.TextValue))
|
||||||
|
{
|
||||||
|
savedItem.Login.Username = usernameField.TextValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return savedItem;
|
||||||
|
}
|
||||||
|
else if(SaveType == SaveDataType.CreditCard)
|
||||||
{
|
{
|
||||||
savedItem.Login.Username = usernameField.TextValue;
|
var savedItem = new SavedItem
|
||||||
|
{
|
||||||
|
Type = App.Enums.CipherType.Card,
|
||||||
|
Card = new SavedItem.CardItem
|
||||||
|
{
|
||||||
|
Number = HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber) ?
|
||||||
|
HintToFieldsMap[View.AutofillHintCreditCardNumber].FirstOrDefault(
|
||||||
|
f => !string.IsNullOrWhiteSpace(f.TextValue))?.TextValue : null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return savedItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
return savedItem;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FocusedHintsContain(IEnumerable<string> hints)
|
||||||
|
{
|
||||||
|
return hints.Any(h => FocusedHints.Contains(h));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -170,7 +170,7 @@ namespace Bit.Android
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = new List<IFilledItem> { new CipherFilledItem(cipher) };
|
var items = new List<IFilledItem> { new CipherFilledItem(cipher) };
|
||||||
var response = AutofillHelpers.BuildFillResponse(this, parser.FieldCollection, items);
|
var response = AutofillHelpers.BuildFillResponse(this, parser, items);
|
||||||
var replyIntent = new Intent();
|
var replyIntent = new Intent();
|
||||||
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, response);
|
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, response);
|
||||||
SetResult(Result.Ok, replyIntent);
|
SetResult(Result.Ok, replyIntent);
|
||||||
|
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
|
@ -376,6 +376,15 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Auto-fill with bitwarden.
|
||||||
|
/// </summary>
|
||||||
|
public static string AutofillWithBitwarden {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AutofillWithBitwarden", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Avoid Ambiguous Characters.
|
/// Looks up a localized string similar to Avoid Ambiguous Characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2860,6 +2869,15 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Vault is locked.
|
||||||
|
/// </summary>
|
||||||
|
public static string VaultIsLocked {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("VaultIsLocked", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Verification Code.
|
/// Looks up a localized string similar to Verification Code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1185,4 +1185,10 @@
|
||||||
<data name="IconsUrl" xml:space="preserve">
|
<data name="IconsUrl" xml:space="preserve">
|
||||||
<value>Icons Server URL</value>
|
<value>Icons Server URL</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AutofillWithBitwarden" xml:space="preserve">
|
||||||
|
<value>Auto-fill with bitwarden</value>
|
||||||
|
</data>
|
||||||
|
<data name="VaultIsLocked" xml:space="preserve">
|
||||||
|
<value>Vault is locked</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
Loading…
Reference in a new issue