mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 23:25:45 +03:00
added cards and other improvements to save
This commit is contained in:
parent
c45a77d538
commit
caff67b77d
11 changed files with 477 additions and 199 deletions
|
@ -291,8 +291,7 @@
|
|||
<Compile Include="Autofill\FieldCollection.cs" />
|
||||
<Compile Include="Autofill\AutofillService.cs" />
|
||||
<Compile Include="Autofill\AutofillHelpers.cs" />
|
||||
<Compile Include="Autofill\CipherFilledItem.cs" />
|
||||
<Compile Include="Autofill\IFilledItem.cs" />
|
||||
<Compile Include="Autofill\FilledItem.cs" />
|
||||
<Compile Include="Autofill\SavedItem.cs" />
|
||||
<Compile Include="Controls\CustomLabelRenderer.cs" />
|
||||
<Compile Include="Controls\CustomSearchBarRenderer.cs" />
|
||||
|
|
|
@ -13,9 +13,9 @@ namespace Bit.Android.Autofill
|
|||
{
|
||||
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)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ namespace Bit.Android.Autofill
|
|||
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
if(items != null && items.Count > 0)
|
||||
|
@ -62,7 +62,7 @@ namespace Bit.Android.Autofill
|
|||
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(
|
||||
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)
|
||||
{
|
||||
var saveType = fields.SaveType;
|
||||
if(saveType == SaveDataType.Generic)
|
||||
var requiredIds = fields.GetRequiredSaveFields();
|
||||
if(saveType == SaveDataType.Generic || requiredIds.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var saveInfo = new SaveInfo.Builder(saveType, fields.AutofillIds.ToArray()).Build();
|
||||
responseBuilder.SetSaveInfo(saveInfo);
|
||||
var saveBuilder = new SaveInfo.Builder(saveType, requiredIds);
|
||||
var optionalIds = fields.GetOptionalSaveIds();
|
||||
if(optionalIds.Length > 0)
|
||||
{
|
||||
saveBuilder.SetOptionalIds(optionalIds);
|
||||
}
|
||||
responseBuilder.SetSaveInfo(saveBuilder.Build());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,8 +34,7 @@ namespace Bit.Android.Autofill
|
|||
parser.Parse();
|
||||
|
||||
if(string.IsNullOrWhiteSpace(parser.Uri) || parser.Uri == "androidapp://com.x8bit.bitwarden" ||
|
||||
parser.Uri == "androidapp://android" ||
|
||||
(!parser.FieldCollection.FillableForLogin && !parser.FieldCollection.FillableForCard))
|
||||
parser.Uri == "androidapp://android" || !parser.FieldCollection.Fillable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -95,6 +94,13 @@ namespace Bit.Android.Autofill
|
|||
intent.PutExtra("autofillFrameworkUsername", savedItem.Login.Username);
|
||||
intent.PutExtra("autofillFrameworkPassword", savedItem.Login.Password);
|
||||
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:
|
||||
Toast.MakeText(this, "Unable to save this type of form.", ToastLength.Short).Show();
|
||||
return;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ namespace Bit.Android.Autofill
|
|||
var autofillOptions = node.GetAutofillOptions();
|
||||
if(autofillOptions != null && autofillOptions.Length > 0)
|
||||
{
|
||||
ListValue = node.AutofillValue.ListValue;
|
||||
TextValue = autofillOptions[node.AutofillValue.ListValue];
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +45,10 @@ namespace Bit.Android.Autofill
|
|||
{
|
||||
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 string TextValue { get; set; }
|
||||
public long? DateValue { get; set; }
|
||||
public int? ListValue { 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()
|
||||
{
|
||||
SaveType = SaveDataType.Generic;
|
||||
|
|
|
@ -58,7 +58,12 @@ namespace Bit.Android.Autofill
|
|||
}
|
||||
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())
|
||||
{
|
||||
_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,
|
||||
View.AutofillHintPostalCode });
|
||||
|
||||
public bool Fillable => FillableForLogin || FillableForCard || FillableForIdentity;
|
||||
|
||||
public void Add(Field field)
|
||||
{
|
||||
if(Ids.Contains(field.Id))
|
||||
|
@ -165,15 +172,12 @@ namespace Bit.Android.Autofill
|
|||
Type = App.Enums.CipherType.Login,
|
||||
Login = new SavedItem.LoginItem
|
||||
{
|
||||
Password = passwordField.TextValue
|
||||
Password = GetFieldValue(passwordField)
|
||||
}
|
||||
};
|
||||
|
||||
var usernameField = Fields.TakeWhile(f => f.Id != passwordField.Id).LastOrDefault();
|
||||
if(usernameField != null && !string.IsNullOrWhiteSpace(usernameField.TextValue))
|
||||
{
|
||||
savedItem.Login.Username = usernameField.TextValue;
|
||||
}
|
||||
savedItem.Login.Username = GetFieldValue(usernameField);
|
||||
|
||||
return savedItem;
|
||||
}
|
||||
|
@ -184,9 +188,11 @@ namespace Bit.Android.Autofill
|
|||
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
|
||||
Number = GetFieldValue(View.AutofillHintCreditCardNumber),
|
||||
Name = GetFieldValue(View.AutofillHintName),
|
||||
ExpMonth = GetFieldValue(View.AutofillHintCreditCardExpirationMonth, true),
|
||||
ExpYear = GetFieldValue(View.AutofillHintCreditCardExpirationYear),
|
||||
Code = GetFieldValue(View.AutofillHintCreditCardSecurityCode)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -196,9 +202,105 @@ namespace Bit.Android.Autofill
|
|||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
291
src/Android/Autofill/FilledItem.cs
Normal file
291
src/Android/Autofill/FilledItem.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -169,7 +169,7 @@ namespace Bit.Android
|
|||
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 replyIntent = new Intent();
|
||||
replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, response);
|
||||
|
@ -441,6 +441,11 @@ namespace Bit.Android
|
|||
options.SaveName = Intent.GetStringExtra("autofillFrameworkName");
|
||||
options.SaveUsername = Intent.GetStringExtra("autofillFrameworkUsername");
|
||||
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;
|
||||
|
|
|
@ -11,5 +11,10 @@ namespace Bit.App.Models
|
|||
public string SaveName { get; set; }
|
||||
public string SaveUsername { 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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,11 @@ namespace Bit.App.Pages
|
|||
private readonly string _defaultName;
|
||||
private readonly string _defaultUsername;
|
||||
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 _fromAutofillFramework;
|
||||
private DateTime? _lastAction;
|
||||
|
@ -40,9 +45,17 @@ namespace Bit.App.Pages
|
|||
public VaultAddCipherPage(AppOptions options)
|
||||
: this(options.SaveType.Value, options.Uri, options.SaveName, options.FromAutofillFramework, false)
|
||||
{
|
||||
_fromAutofillFramework = options.FromAutofillFramework;
|
||||
_defaultUsername = options.SaveUsername;
|
||||
_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();
|
||||
}
|
||||
|
||||
|
@ -410,19 +423,39 @@ namespace Bit.App.Pages
|
|||
{
|
||||
CardCodeCell = new FormEntryCell(AppResources.SecurityCode, Keyboard.Numeric,
|
||||
nextElement: NotesCell.Editor);
|
||||
if(!string.IsNullOrWhiteSpace(_defaultCardCode))
|
||||
{
|
||||
CardCodeCell.Entry.Text = _defaultCardCode;
|
||||
}
|
||||
CardExpYearCell = new FormEntryCell(AppResources.ExpirationYear, Keyboard.Numeric,
|
||||
nextElement: CardCodeCell.Entry);
|
||||
if(!string.IsNullOrWhiteSpace(_defaultCardExpYear))
|
||||
{
|
||||
CardExpYearCell.Entry.Text = _defaultCardExpYear;
|
||||
}
|
||||
CardExpMonthCell = new FormPickerCell(AppResources.ExpirationMonth, new string[] {
|
||||
"--", AppResources.January, AppResources.February, AppResources.March, AppResources.April,
|
||||
AppResources.May, AppResources.June, AppResources.July, AppResources.August, AppResources.September,
|
||||
AppResources.October, AppResources.November, AppResources.December
|
||||
});
|
||||
if(_defaultCardExpMonth.HasValue)
|
||||
{
|
||||
CardExpMonthCell.Picker.SelectedIndex = _defaultCardExpMonth.Value;
|
||||
}
|
||||
CardBrandCell = new FormPickerCell(AppResources.Brand, new string[] {
|
||||
"--", "Visa", "Mastercard", "American Express", "Discover", "Diners Club",
|
||||
"JCB", "Maestro", "UnionPay", AppResources.Other
|
||||
});
|
||||
CardNumberCell = new FormEntryCell(AppResources.Number, Keyboard.Numeric);
|
||||
if(!string.IsNullOrWhiteSpace(_defaultCardNumber))
|
||||
{
|
||||
CardNumberCell.Entry.Text = _defaultCardNumber;
|
||||
}
|
||||
CardNameCell = new FormEntryCell(AppResources.CardholderName, nextElement: CardNumberCell.Entry);
|
||||
if(!string.IsNullOrWhiteSpace(_defaultCardName))
|
||||
{
|
||||
CardNameCell.Entry.Text = _defaultCardName;
|
||||
}
|
||||
NameCell.NextElement = CardNameCell.Entry;
|
||||
|
||||
// Build sections
|
||||
|
|
Loading…
Reference in a new issue