mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-11 14:37:51 +01:00
added cards and other improvements to save
This commit is contained in:
parent
c45a77d538
commit
caff67b77d
@ -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" />
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
@ -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();
|
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;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
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;
|
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;
|
||||||
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user