added cards and other improvements to save

This commit is contained in:
Kyle Spearrin 2017-11-18 23:04:21 -05:00
parent c45a77d538
commit caff67b77d
11 changed files with 477 additions and 199 deletions

View file

@ -291,8 +291,7 @@
<Compile Include="Autofill\FieldCollection.cs" /> <Compile Include="Autofill\FieldCollection.cs" />
<Compile Include="Autofill\AutofillService.cs" /> <Compile Include="Autofill\AutofillService.cs" />
<Compile Include="Autofill\AutofillHelpers.cs" /> <Compile Include="Autofill\AutofillHelpers.cs" />
<Compile Include="Autofill\CipherFilledItem.cs" /> <Compile Include="Autofill\FilledItem.cs" />
<Compile Include="Autofill\IFilledItem.cs" />
<Compile Include="Autofill\SavedItem.cs" /> <Compile Include="Autofill\SavedItem.cs" />
<Compile Include="Controls\CustomLabelRenderer.cs" /> <Compile Include="Controls\CustomLabelRenderer.cs" />
<Compile Include="Controls\CustomSearchBarRenderer.cs" /> <Compile Include="Controls\CustomSearchBarRenderer.cs" />

View file

@ -13,9 +13,9 @@ namespace Bit.Android.Autofill
{ {
public static class AutofillHelpers public static class AutofillHelpers
{ {
public static async Task<List<IFilledItem>> GetFillItemsAsync(Parser parser, ICipherService service) public static async Task<List<FilledItem>> GetFillItemsAsync(Parser parser, ICipherService service)
{ {
var items = new List<IFilledItem>(); var items = new List<FilledItem>();
if(parser.FieldCollection.FillableForLogin) if(parser.FieldCollection.FillableForLogin)
{ {
@ -26,7 +26,7 @@ namespace Bit.Android.Autofill
allCiphers.AddRange(ciphers.Item2.ToList()); allCiphers.AddRange(ciphers.Item2.ToList());
foreach(var cipher in allCiphers) foreach(var cipher in allCiphers)
{ {
items.Add(new CipherFilledItem(cipher)); items.Add(new FilledItem(cipher));
} }
} }
} }
@ -35,14 +35,14 @@ namespace Bit.Android.Autofill
var ciphers = await service.GetAllAsync(); var ciphers = await service.GetAllAsync();
foreach(var cipher in ciphers.Where(c => c.Type == App.Enums.CipherType.Card)) foreach(var cipher in ciphers.Where(c => c.Type == App.Enums.CipherType.Card))
{ {
items.Add(new CipherFilledItem(cipher)); items.Add(new FilledItem(cipher));
} }
} }
return items; return items;
} }
public static FillResponse BuildFillResponse(Context context, Parser parser, List<IFilledItem> items) public static FillResponse BuildFillResponse(Context context, Parser parser, List<FilledItem> items)
{ {
var responseBuilder = new FillResponse.Builder(); var responseBuilder = new FillResponse.Builder();
if(items != null && items.Count > 0) if(items != null && items.Count > 0)
@ -62,7 +62,7 @@ namespace Bit.Android.Autofill
return responseBuilder.Build(); return responseBuilder.Build();
} }
public static Dataset BuildDataset(Context context, FieldCollection fields, IFilledItem filledItem) public static Dataset BuildDataset(Context context, FieldCollection fields, FilledItem filledItem)
{ {
var datasetBuilder = new Dataset.Builder( var datasetBuilder = new Dataset.Builder(
BuildListView(context.PackageName, filledItem.Name, filledItem.Subtitle, filledItem.Icon)); BuildListView(context.PackageName, filledItem.Name, filledItem.Subtitle, filledItem.Icon));
@ -100,13 +100,19 @@ namespace Bit.Android.Autofill
public static void AddSaveInfo(FillResponse.Builder responseBuilder, FieldCollection fields) public static void AddSaveInfo(FillResponse.Builder responseBuilder, FieldCollection fields)
{ {
var saveType = fields.SaveType; var saveType = fields.SaveType;
if(saveType == SaveDataType.Generic) var requiredIds = fields.GetRequiredSaveFields();
if(saveType == SaveDataType.Generic || requiredIds.Length == 0)
{ {
return; return;
} }
var saveInfo = new SaveInfo.Builder(saveType, fields.AutofillIds.ToArray()).Build(); var saveBuilder = new SaveInfo.Builder(saveType, requiredIds);
responseBuilder.SetSaveInfo(saveInfo); var optionalIds = fields.GetOptionalSaveIds();
if(optionalIds.Length > 0)
{
saveBuilder.SetOptionalIds(optionalIds);
}
responseBuilder.SetSaveInfo(saveBuilder.Build());
} }
} }
} }

View file

@ -34,8 +34,7 @@ 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.Uri == "androidapp://android" || !parser.FieldCollection.Fillable)
(!parser.FieldCollection.FillableForLogin && !parser.FieldCollection.FillableForCard))
{ {
return; return;
} }
@ -95,6 +94,13 @@ namespace Bit.Android.Autofill
intent.PutExtra("autofillFrameworkUsername", savedItem.Login.Username); intent.PutExtra("autofillFrameworkUsername", savedItem.Login.Username);
intent.PutExtra("autofillFrameworkPassword", savedItem.Login.Password); intent.PutExtra("autofillFrameworkPassword", savedItem.Login.Password);
break; break;
case CipherType.Card:
intent.PutExtra("autofillFrameworkCardName", savedItem.Card.Name);
intent.PutExtra("autofillFrameworkCardNumber", savedItem.Card.Number);
intent.PutExtra("autofillFrameworkCardExpMonth", savedItem.Card.ExpMonth);
intent.PutExtra("autofillFrameworkCardExpYear", savedItem.Card.ExpYear);
intent.PutExtra("autofillFrameworkCardCode", savedItem.Card.Code);
break;
default: default:
Toast.MakeText(this, "Unable to save this type of form.", ToastLength.Short).Show(); Toast.MakeText(this, "Unable to save this type of form.", ToastLength.Short).Show();
return; return;

View file

@ -1,146 +0,0 @@
using System;
using Android.Service.Autofill;
using Android.Views.Autofill;
using System.Linq;
using Bit.App.Models;
using Bit.App.Enums;
using Bit.App.Models.Page;
using Android.Views;
namespace Bit.Android.Autofill
{
public class CipherFilledItem : IFilledItem
{
private Lazy<string> _password;
private string _cardNumber;
private Lazy<string> _cardExpMonth;
private Lazy<string> _cardExpYear;
private Lazy<string> _cardCode;
public CipherFilledItem(Cipher cipher)
{
Name = cipher.Name?.Decrypt() ?? "--";
Type = cipher.Type;
switch(Type)
{
case CipherType.Login:
Subtitle = cipher.Login.Username?.Decrypt() ?? string.Empty;
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;
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:
break;
}
}
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(!fieldCollection?.Fields.Any() ?? true)
{
return false;
}
var setValues = false;
if(Type == CipherType.Login)
{
if(fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password.Value))
{
foreach(var f in fieldCollection.PasswordFields)
{
setValues = true;
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(_password.Value));
}
}
if(fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
{
foreach(var f in fieldCollection.UsernameFields)
{
setValues = true;
datasetBuilder.SetValue(f.AutofillId, AutofillValue.ForText(Subtitle));
}
}
}
else if(Type == CipherType.Card)
{
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;
}
}
}

View file

@ -33,6 +33,7 @@ namespace Bit.Android.Autofill
var autofillOptions = node.GetAutofillOptions(); var autofillOptions = node.GetAutofillOptions();
if(autofillOptions != null && autofillOptions.Length > 0) if(autofillOptions != null && autofillOptions.Length > 0)
{ {
ListValue = node.AutofillValue.ListValue;
TextValue = autofillOptions[node.AutofillValue.ListValue]; TextValue = autofillOptions[node.AutofillValue.ListValue];
} }
} }
@ -44,6 +45,10 @@ namespace Bit.Android.Autofill
{ {
TextValue = node.AutofillValue.TextValue; TextValue = node.AutofillValue.TextValue;
} }
else if(node.AutofillValue.IsToggle)
{
ToggleValue = node.AutofillValue.ToggleValue;
}
} }
} }
@ -69,24 +74,9 @@ namespace Bit.Android.Autofill
public List<string> AutofillOptions { get; set; } public List<string> AutofillOptions { get; set; }
public string TextValue { get; set; } public string TextValue { get; set; }
public long? DateValue { get; set; } public long? DateValue { get; set; }
public int? ListValue { get; set; }
public bool? ToggleValue { get; set; } public bool? ToggleValue { get; set; }
public int GetAutofillOptionIndex(string value)
{
if(AutofillOptions != null)
{
for(var i = 0; i < AutofillOptions.Count; i++)
{
if(AutofillOptions[i].Equals(value))
{
return i;
}
}
}
return -1;
}
private void UpdateSaveTypeFromHints() private void UpdateSaveTypeFromHints()
{ {
SaveType = SaveDataType.Generic; SaveType = SaveDataType.Generic;

View file

@ -58,7 +58,12 @@ namespace Bit.Android.Autofill
} }
else else
{ {
_passwordFields = Fields.Where(f => f.InputType.HasFlag(InputTypes.TextVariationPassword)).ToList(); _passwordFields = Fields
.Where(f =>
f.InputType.HasFlag(InputTypes.TextVariationPassword) ||
f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) ||
f.InputType.HasFlag(InputTypes.TextVariationWebPassword))
.ToList();
if(!_passwordFields.Any()) if(!_passwordFields.Any())
{ {
_passwordFields = Fields.Where(f => f.IdEntry?.ToLower().Contains("password") ?? false).ToList(); _passwordFields = Fields.Where(f => f.IdEntry?.ToLower().Contains("password") ?? false).ToList();
@ -116,6 +121,8 @@ namespace Bit.Android.Autofill
new string[] { View.AutofillHintName, View.AutofillHintPhone, View.AutofillHintPostalAddress, new string[] { View.AutofillHintName, View.AutofillHintPhone, View.AutofillHintPostalAddress,
View.AutofillHintPostalCode }); View.AutofillHintPostalCode });
public bool Fillable => FillableForLogin || FillableForCard || FillableForIdentity;
public void Add(Field field) public void Add(Field field)
{ {
if(Ids.Contains(field.Id)) if(Ids.Contains(field.Id))
@ -165,15 +172,12 @@ namespace Bit.Android.Autofill
Type = App.Enums.CipherType.Login, Type = App.Enums.CipherType.Login,
Login = new SavedItem.LoginItem Login = new SavedItem.LoginItem
{ {
Password = passwordField.TextValue Password = GetFieldValue(passwordField)
} }
}; };
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault(); var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault();
if(usernameField != null && !string.IsNullOrWhiteSpace(usernameField.TextValue)) savedItem.Login.Username = GetFieldValue(usernameField);
{
savedItem.Login.Username = usernameField.TextValue;
}
return savedItem; return savedItem;
} }
@ -184,9 +188,11 @@ namespace Bit.Android.Autofill
Type = App.Enums.CipherType.Card, Type = App.Enums.CipherType.Card,
Card = new SavedItem.CardItem Card = new SavedItem.CardItem
{ {
Number = HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber) ? Number = GetFieldValue(View.AutofillHintCreditCardNumber),
HintToFieldsMap[View.AutofillHintCreditCardNumber].FirstOrDefault( Name = GetFieldValue(View.AutofillHintName),
f => !string.IsNullOrWhiteSpace(f.TextValue))?.TextValue : null ExpMonth = GetFieldValue(View.AutofillHintCreditCardExpirationMonth, true),
ExpYear = GetFieldValue(View.AutofillHintCreditCardExpirationYear),
Code = GetFieldValue(View.AutofillHintCreditCardSecurityCode)
} }
}; };
@ -196,9 +202,105 @@ namespace Bit.Android.Autofill
return null; return null;
} }
public AutofillId[] GetOptionalSaveIds()
{
if(SaveType == SaveDataType.Password)
{
return UsernameFields.Select(f => f.AutofillId).ToArray();
}
else if(SaveType == SaveDataType.CreditCard)
{
var fieldList = new List<Field>();
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardSecurityCode))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardSecurityCode]);
}
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationYear))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationYear]);
}
if(HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardExpirationMonth))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintCreditCardExpirationMonth]);
}
if(HintToFieldsMap.ContainsKey(View.AutofillHintName))
{
fieldList.AddRange(HintToFieldsMap[View.AutofillHintName]);
}
return fieldList.Select(f => f.AutofillId).ToArray();
}
return new AutofillId[0];
}
public AutofillId[] GetRequiredSaveFields()
{
if(SaveType == SaveDataType.Password)
{
return PasswordFields.Select(f => f.AutofillId).ToArray();
}
else if(SaveType == SaveDataType.CreditCard && HintToFieldsMap.ContainsKey(View.AutofillHintCreditCardNumber))
{
return HintToFieldsMap[View.AutofillHintCreditCardNumber].Select(f => f.AutofillId).ToArray();
}
return new AutofillId[0];
}
private bool FocusedHintsContain(IEnumerable<string> hints) private bool FocusedHintsContain(IEnumerable<string> hints)
{ {
return hints.Any(h => FocusedHints.Contains(h)); return hints.Any(h => FocusedHints.Contains(h));
} }
private string GetFieldValue(string hint, bool monthValue = false)
{
if(HintToFieldsMap.ContainsKey(hint))
{
foreach(var field in HintToFieldsMap[hint])
{
var val = GetFieldValue(field, monthValue);
if(!string.IsNullOrWhiteSpace(val))
{
return val;
}
}
}
return null;
}
private string GetFieldValue(Field field, bool monthValue = false)
{
if(field == null)
{
return null;
}
if(!string.IsNullOrWhiteSpace(field.TextValue))
{
if(field.AutofillType == AutofillType.List && field.ListValue.HasValue && monthValue)
{
if(field.AutofillOptions.Count == 13)
{
return field.ListValue.ToString();
}
else if(field.AutofillOptions.Count == 12)
{
return (field.ListValue + 1).ToString();
}
}
return field.TextValue;
}
else if(field.DateValue.HasValue)
{
return field.DateValue.Value.ToString();
}
else if(field.ToggleValue.HasValue)
{
return field.ToggleValue.Value.ToString();
}
return null;
}
} }
} }

View file

@ -0,0 +1,291 @@
using System;
using Android.Service.Autofill;
using Android.Views.Autofill;
using System.Linq;
using Bit.App.Models;
using Bit.App.Enums;
using Bit.App.Models.Page;
using Android.Views;
namespace Bit.Android.Autofill
{
public class FilledItem
{
private Lazy<string> _password;
private Lazy<string> _cardName;
private string _cardNumber;
private Lazy<string> _cardExpMonth;
private Lazy<string> _cardExpYear;
private Lazy<string> _cardCode;
private Lazy<string> _idPhone;
private Lazy<string> _idEmail;
private Lazy<string> _idUsername;
private Lazy<string> _idAddress;
private Lazy<string> _idPostalCode;
public FilledItem(Cipher cipher)
{
Name = cipher.Name?.Decrypt(cipher.OrganizationId) ?? "--";
Type = cipher.Type;
switch(Type)
{
case CipherType.Login:
Subtitle = cipher.Login.Username?.Decrypt(cipher.OrganizationId) ?? string.Empty;
Icon = Resource.Drawable.login;
_password = new Lazy<string>(() => cipher.Login.Password?.Decrypt(cipher.OrganizationId));
break;
case CipherType.Card:
Subtitle = cipher.Card.Brand?.Decrypt(cipher.OrganizationId);
_cardNumber = cipher.Card.Number?.Decrypt(cipher.OrganizationId);
if(!string.IsNullOrWhiteSpace(_cardNumber) && _cardNumber.Length >= 4)
{
if(!string.IsNullOrWhiteSpace(_cardNumber))
{
Subtitle += ", ";
}
Subtitle += ("*" + _cardNumber.Substring(_cardNumber.Length - 4));
}
Icon = Resource.Drawable.card;
_cardName = new Lazy<string>(() => cipher.Card.CardholderName?.Decrypt(cipher.OrganizationId));
_cardCode = new Lazy<string>(() => cipher.Card.Code?.Decrypt(cipher.OrganizationId));
_cardExpMonth = new Lazy<string>(() => cipher.Card.ExpMonth?.Decrypt(cipher.OrganizationId));
_cardExpYear = new Lazy<string>(() => cipher.Card.ExpYear?.Decrypt(cipher.OrganizationId));
break;
case CipherType.Identity:
var firstName = cipher.Identity?.FirstName?.Decrypt(cipher.OrganizationId) ?? " ";
var lastName = cipher.Identity?.LastName?.Decrypt(cipher.OrganizationId) ?? " ";
Subtitle = " ";
if(!string.IsNullOrWhiteSpace(firstName))
{
Subtitle = firstName;
}
if(!string.IsNullOrWhiteSpace(lastName))
{
if(!string.IsNullOrWhiteSpace(Subtitle))
{
Subtitle += " ";
}
Subtitle += lastName;
}
Icon = Resource.Drawable.id;
_idPhone = new Lazy<string>(() => cipher.Identity.Phone?.Decrypt(cipher.OrganizationId));
_idEmail = new Lazy<string>(() => cipher.Identity.Email?.Decrypt(cipher.OrganizationId));
_idUsername = new Lazy<string>(() => cipher.Identity.Username?.Decrypt(cipher.OrganizationId));
_idAddress = new Lazy<string>(() =>
{
var address = cipher.Identity.Address1?.Decrypt(cipher.OrganizationId);
var address2 = cipher.Identity.Address2?.Decrypt(cipher.OrganizationId);
if(!string.IsNullOrWhiteSpace(address2))
{
if(!string.IsNullOrWhiteSpace(address))
{
address += ", ";
}
address += address2;
}
var address3 = cipher.Identity.Address3?.Decrypt(cipher.OrganizationId);
if(!string.IsNullOrWhiteSpace(address3))
{
if(!string.IsNullOrWhiteSpace(address))
{
address += ", ";
}
address += address3;
}
return address;
});
_idPostalCode = new Lazy<string>(() => cipher.Identity.PostalCode?.Decrypt(cipher.OrganizationId));
break;
default:
break;
}
}
public FilledItem(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:
break;
}
}
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(!fieldCollection?.Fields.Any() ?? true)
{
return false;
}
var setValues = false;
if(Type == CipherType.Login)
{
if(fieldCollection.PasswordFields.Any() && !string.IsNullOrWhiteSpace(_password.Value))
{
foreach(var f in fieldCollection.PasswordFields)
{
var val = ApplyValue(f, _password.Value);
if(val != null)
{
setValues = true;
datasetBuilder.SetValue(f.AutofillId, val);
}
}
}
if(fieldCollection.UsernameFields.Any() && !string.IsNullOrWhiteSpace(Subtitle))
{
foreach(var f in fieldCollection.UsernameFields)
{
var val = ApplyValue(f, Subtitle);
if(val != null)
{
setValues = true;
datasetBuilder.SetValue(f.AutofillId, val);
}
}
}
}
else if(Type == CipherType.Card)
{
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardNumber,
new Lazy<string>(() => _cardNumber)))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardSecurityCode, _cardCode))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardExpirationMonth, _cardExpMonth, true))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintCreditCardExpirationYear, _cardExpYear))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintName, _cardName))
{
setValues = true;
}
}
else if(Type == CipherType.Identity)
{
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintPhone, _idPhone))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintEmailAddress, _idEmail))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintUsername, _idUsername))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintPostalAddress, _idAddress))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintPostalCode, _idPostalCode))
{
setValues = true;
}
if(ApplyValue(datasetBuilder, fieldCollection, View.AutofillHintName, new Lazy<string>(() => Subtitle)))
{
setValues = true;
}
}
return setValues;
}
private static bool ApplyValue(Dataset.Builder builder, FieldCollection fieldCollection,
string hint, Lazy<string> value, bool monthValue = false)
{
bool setValues = false;
if(fieldCollection.HintToFieldsMap.ContainsKey(hint) && !string.IsNullOrWhiteSpace(value.Value))
{
foreach(var f in fieldCollection.HintToFieldsMap[hint])
{
var val = ApplyValue(f, value.Value, monthValue);
if(val != null)
{
setValues = true;
builder.SetValue(f.AutofillId, val);
}
}
}
return setValues;
}
private static AutofillValue ApplyValue(Field field, string value, bool monthValue = false)
{
switch(field.AutofillType)
{
case AutofillType.Date:
if(long.TryParse(value, out long dateValue))
{
return AutofillValue.ForDate(dateValue);
}
break;
case AutofillType.List:
if(field.AutofillOptions != null)
{
if(monthValue && int.TryParse(value, out int monthIndex))
{
if(field.AutofillOptions.Count == 13)
{
return AutofillValue.ForList(monthIndex);
}
else if(field.AutofillOptions.Count >= monthIndex)
{
return AutofillValue.ForList(monthIndex - 1);
}
}
for(var i = 0; i < field.AutofillOptions.Count; i++)
{
if(field.AutofillOptions[i].Equals(value))
{
return AutofillValue.ForList(i);
}
}
}
break;
case AutofillType.Text:
return AutofillValue.ForText(value);
case AutofillType.Toggle:
if(bool.TryParse(value, out bool toggleValue))
{
return AutofillValue.ForToggle(toggleValue);
}
break;
default:
break;
}
return null;
}
}
}

View file

@ -1,13 +0,0 @@
using Android.Service.Autofill;
using System;
namespace Bit.Android.Autofill
{
public interface IFilledItem
{
string Name { get; set; }
string Subtitle { get; set; }
int Icon { get; set; }
bool ApplyToFields(FieldCollection fieldCollection, Dataset.Builder datasetBuilder);
}
}

View file

@ -169,7 +169,7 @@ namespace Bit.Android
return; return;
} }
var items = new List<IFilledItem> { new CipherFilledItem(cipher) }; var items = new List<FilledItem> { new FilledItem(cipher) };
var response = AutofillHelpers.BuildFillResponse(this, parser, 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);
@ -441,6 +441,11 @@ namespace Bit.Android
options.SaveName = Intent.GetStringExtra("autofillFrameworkName"); options.SaveName = Intent.GetStringExtra("autofillFrameworkName");
options.SaveUsername = Intent.GetStringExtra("autofillFrameworkUsername"); options.SaveUsername = Intent.GetStringExtra("autofillFrameworkUsername");
options.SavePassword = Intent.GetStringExtra("autofillFrameworkPassword"); options.SavePassword = Intent.GetStringExtra("autofillFrameworkPassword");
options.SaveCardName = Intent.GetStringExtra("autofillFrameworkCardName");
options.SaveCardNumber = Intent.GetStringExtra("autofillFrameworkCardNumber");
options.SaveCardExpMonth = Intent.GetStringExtra("autofillFrameworkCardExpMonth");
options.SaveCardExpYear = Intent.GetStringExtra("autofillFrameworkCardExpYear");
options.SaveCardCode = Intent.GetStringExtra("autofillFrameworkCardCode");
} }
return options; return options;

View file

@ -11,5 +11,10 @@ namespace Bit.App.Models
public string SaveName { get; set; } public string SaveName { get; set; }
public string SaveUsername { get; set; } public string SaveUsername { get; set; }
public string SavePassword { get; set; } public string SavePassword { get; set; }
public string SaveCardName { get; set; }
public string SaveCardNumber { get; set; }
public string SaveCardExpMonth { get; set; }
public string SaveCardExpYear { get; set; }
public string SaveCardCode { get; set; }
} }
} }

View file

@ -33,6 +33,11 @@ namespace Bit.App.Pages
private readonly string _defaultName; private readonly string _defaultName;
private readonly string _defaultUsername; private readonly string _defaultUsername;
private readonly string _defaultPassword; private readonly string _defaultPassword;
private readonly string _defaultCardName;
private readonly string _defaultCardNumber;
private readonly int? _defaultCardExpMonth;
private readonly string _defaultCardExpYear;
private readonly string _defaultCardCode;
private readonly bool _fromAutofill; private readonly bool _fromAutofill;
private readonly bool _fromAutofillFramework; private readonly bool _fromAutofillFramework;
private DateTime? _lastAction; private DateTime? _lastAction;
@ -40,9 +45,17 @@ namespace Bit.App.Pages
public VaultAddCipherPage(AppOptions options) public VaultAddCipherPage(AppOptions options)
: this(options.SaveType.Value, options.Uri, options.SaveName, options.FromAutofillFramework, false) : this(options.SaveType.Value, options.Uri, options.SaveName, options.FromAutofillFramework, false)
{ {
_fromAutofillFramework = options.FromAutofillFramework;
_defaultUsername = options.SaveUsername; _defaultUsername = options.SaveUsername;
_defaultPassword = options.SavePassword; _defaultPassword = options.SavePassword;
_fromAutofillFramework = options.FromAutofillFramework; _defaultCardCode = options.SaveCardCode;
if(int.TryParse(options.SaveCardExpMonth, out int month) && month <= 12 && month >= 1)
{
_defaultCardExpMonth = month;
}
_defaultCardExpYear = options.SaveCardExpYear;
_defaultCardName = options.SaveCardName;
_defaultCardNumber = options.SaveCardNumber;
Init(); Init();
} }
@ -410,19 +423,39 @@ namespace Bit.App.Pages
{ {
CardCodeCell = new FormEntryCell(AppResources.SecurityCode, Keyboard.Numeric, CardCodeCell = new FormEntryCell(AppResources.SecurityCode, Keyboard.Numeric,
nextElement: NotesCell.Editor); nextElement: NotesCell.Editor);
if(!string.IsNullOrWhiteSpace(_defaultCardCode))
{
CardCodeCell.Entry.Text = _defaultCardCode;
}
CardExpYearCell = new FormEntryCell(AppResources.ExpirationYear, Keyboard.Numeric, CardExpYearCell = new FormEntryCell(AppResources.ExpirationYear, Keyboard.Numeric,
nextElement: CardCodeCell.Entry); nextElement: CardCodeCell.Entry);
if(!string.IsNullOrWhiteSpace(_defaultCardExpYear))
{
CardExpYearCell.Entry.Text = _defaultCardExpYear;
}
CardExpMonthCell = new FormPickerCell(AppResources.ExpirationMonth, new string[] { CardExpMonthCell = new FormPickerCell(AppResources.ExpirationMonth, new string[] {
"--", AppResources.January, AppResources.February, AppResources.March, AppResources.April, "--", AppResources.January, AppResources.February, AppResources.March, AppResources.April,
AppResources.May, AppResources.June, AppResources.July, AppResources.August, AppResources.September, AppResources.May, AppResources.June, AppResources.July, AppResources.August, AppResources.September,
AppResources.October, AppResources.November, AppResources.December AppResources.October, AppResources.November, AppResources.December
}); });
if(_defaultCardExpMonth.HasValue)
{
CardExpMonthCell.Picker.SelectedIndex = _defaultCardExpMonth.Value;
}
CardBrandCell = new FormPickerCell(AppResources.Brand, new string[] { CardBrandCell = new FormPickerCell(AppResources.Brand, new string[] {
"--", "Visa", "Mastercard", "American Express", "Discover", "Diners Club", "--", "Visa", "Mastercard", "American Express", "Discover", "Diners Club",
"JCB", "Maestro", "UnionPay", AppResources.Other "JCB", "Maestro", "UnionPay", AppResources.Other
}); });
CardNumberCell = new FormEntryCell(AppResources.Number, Keyboard.Numeric); CardNumberCell = new FormEntryCell(AppResources.Number, Keyboard.Numeric);
if(!string.IsNullOrWhiteSpace(_defaultCardNumber))
{
CardNumberCell.Entry.Text = _defaultCardNumber;
}
CardNameCell = new FormEntryCell(AppResources.CardholderName, nextElement: CardNumberCell.Entry); CardNameCell = new FormEntryCell(AppResources.CardholderName, nextElement: CardNumberCell.Entry);
if(!string.IsNullOrWhiteSpace(_defaultCardName))
{
CardNameCell.Entry.Text = _defaultCardName;
}
NameCell.NextElement = CardNameCell.Entry; NameCell.NextElement = CardNameCell.Entry;
// Build sections // Build sections