mirror of
https://github.com/bitwarden/mobile
synced 2025-12-29 14:43:50 +00:00
reset for v2
This commit is contained in:
@@ -1,183 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.Content;
|
||||
using Android.Service.Autofill;
|
||||
using Android.Widget;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Bit.App.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Enums;
|
||||
using Android.Views.Autofill;
|
||||
|
||||
namespace Bit.Android.Autofill
|
||||
{
|
||||
public static class AutofillHelpers
|
||||
{
|
||||
private static int _pendingIntentId = 0;
|
||||
|
||||
// These browser work natively with the autofill framework
|
||||
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
||||
{
|
||||
"org.mozilla.focus","org.mozilla.klar","com.duckduckgo.mobile.android"
|
||||
};
|
||||
|
||||
// These browsers work using the compatibility shim for the autofill framework
|
||||
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
||||
{
|
||||
"org.mozilla.firefox","org.mozilla.firefox_beta","com.microsoft.emmx","com.android.chrome",
|
||||
"com.chrome.beta","com.android.browser","com.brave.browser","com.opera.browser",
|
||||
"com.opera.browser.beta","com.opera.mini.native","com.chrome.dev","com.chrome.canary",
|
||||
"com.google.android.apps.chrome","com.google.android.apps.chrome_dev","com.yandex.browser",
|
||||
"com.sec.android.app.sbrowser","com.sec.android.app.sbrowser.beta","org.codeaurora.swe.browser",
|
||||
"com.amazon.cloud9","mark.via.gp","org.bromite.bromite","org.chromium.chrome","com.kiwibrowser.browser",
|
||||
"com.ecosia.android","com.opera.mini.native.beta","org.mozilla.fennec_aurora","com.qwant.liberty",
|
||||
"com.opera.touch"
|
||||
};
|
||||
|
||||
// The URLs are blacklisted from autofilling
|
||||
public static HashSet<string> BlacklistedUris = new HashSet<string>
|
||||
{
|
||||
"androidapp://android", "androidapp://com.x8bit.bitwarden", "androidapp://com.oneplus.applocker"
|
||||
};
|
||||
|
||||
public static async Task<List<FilledItem>> GetFillItemsAsync(Parser parser, ICipherService service)
|
||||
{
|
||||
var items = new List<FilledItem>();
|
||||
|
||||
if(parser.FieldCollection.FillableForLogin)
|
||||
{
|
||||
var ciphers = await service.GetAllAsync(parser.Uri);
|
||||
if(ciphers.Item1.Any() || ciphers.Item2.Any())
|
||||
{
|
||||
var allCiphers = ciphers.Item1.ToList();
|
||||
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||
foreach(var cipher in allCiphers)
|
||||
{
|
||||
items.Add(new FilledItem(cipher));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(parser.FieldCollection.FillableForCard)
|
||||
{
|
||||
var ciphers = await service.GetAllAsync();
|
||||
foreach(var cipher in ciphers.Where(c => c.Type == CipherType.Card))
|
||||
{
|
||||
items.Add(new FilledItem(cipher));
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public static FillResponse BuildFillResponse(Context context, Parser parser, List<FilledItem> items, bool locked)
|
||||
{
|
||||
var responseBuilder = new FillResponse.Builder();
|
||||
if(items != null && items.Count > 0)
|
||||
{
|
||||
foreach(var item in items)
|
||||
{
|
||||
var dataset = BuildDataset(context, parser.FieldCollection, item);
|
||||
if(dataset != null)
|
||||
{
|
||||
responseBuilder.AddDataset(dataset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
responseBuilder.AddDataset(BuildVaultDataset(context, parser.FieldCollection, parser.Uri, locked));
|
||||
AddSaveInfo(parser, responseBuilder, parser.FieldCollection);
|
||||
responseBuilder.SetIgnoredIds(parser.FieldCollection.IgnoreAutofillIds.ToArray());
|
||||
return responseBuilder.Build();
|
||||
}
|
||||
|
||||
public static Dataset BuildDataset(Context context, FieldCollection fields, FilledItem filledItem)
|
||||
{
|
||||
var datasetBuilder = new Dataset.Builder(
|
||||
BuildListView(context.PackageName, filledItem.Name, filledItem.Subtitle, filledItem.Icon));
|
||||
if(filledItem.ApplyToFields(fields, datasetBuilder))
|
||||
{
|
||||
return datasetBuilder.Build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Dataset BuildVaultDataset(Context context, FieldCollection fields, string uri, bool locked)
|
||||
{
|
||||
var intent = new Intent(context, typeof(MainActivity));
|
||||
intent.PutExtra("autofillFramework", true);
|
||||
if(fields.FillableForLogin)
|
||||
{
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Login);
|
||||
}
|
||||
else if(fields.FillableForCard)
|
||||
{
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Card);
|
||||
}
|
||||
else if(fields.FillableForIdentity)
|
||||
{
|
||||
intent.PutExtra("autofillFrameworkFillType", (int)CipherType.Identity);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
intent.PutExtra("autofillFrameworkUri", uri);
|
||||
var pendingIntent = PendingIntent.GetActivity(context, ++_pendingIntentId, intent,
|
||||
PendingIntentFlags.CancelCurrent);
|
||||
|
||||
var view = BuildListView(context.PackageName, AppResources.AutofillWithBitwarden,
|
||||
locked ? AppResources.VaultIsLocked : AppResources.GoToMyVault, Resource.Drawable.icon);
|
||||
|
||||
var datasetBuilder = new Dataset.Builder(view);
|
||||
datasetBuilder.SetAuthentication(pendingIntent.IntentSender);
|
||||
|
||||
// Dataset must have a value set. We will reset this in the main activity when the real item is chosen.
|
||||
foreach(var autofillId in fields.AutofillIds)
|
||||
{
|
||||
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
|
||||
}
|
||||
|
||||
return datasetBuilder.Build();
|
||||
}
|
||||
|
||||
public static RemoteViews BuildListView(string packageName, string text, string subtext, int iconId)
|
||||
{
|
||||
var view = new RemoteViews(packageName, Resource.Layout.autofill_listitem);
|
||||
view.SetTextViewText(Resource.Id.text, text);
|
||||
view.SetTextViewText(Resource.Id.text2, subtext);
|
||||
view.SetImageViewResource(Resource.Id.icon, iconId);
|
||||
return view;
|
||||
}
|
||||
|
||||
public static void AddSaveInfo(Parser parser, FillResponse.Builder responseBuilder, FieldCollection fields)
|
||||
{
|
||||
// Docs state that password fields cannot be reliably saved in Compat mode since they will show as
|
||||
// masked values.
|
||||
var compatBrowser = CompatBrowsers.Contains(parser.PackageName);
|
||||
if(compatBrowser && fields.SaveType == SaveDataType.Password)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var requiredIds = fields.GetRequiredSaveFields();
|
||||
if(fields.SaveType == SaveDataType.Generic || requiredIds.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var saveBuilder = new SaveInfo.Builder(fields.SaveType, requiredIds);
|
||||
var optionalIds = fields.GetOptionalSaveIds();
|
||||
if(optionalIds.Length > 0)
|
||||
{
|
||||
saveBuilder.SetOptionalIds(optionalIds);
|
||||
}
|
||||
if(compatBrowser)
|
||||
{
|
||||
saveBuilder.SetFlags(SaveFlags.SaveOnAllViewsInvisible);
|
||||
}
|
||||
responseBuilder.SetSaveInfo(saveBuilder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
using Android;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Service.Autofill;
|
||||
using Android.Widget;
|
||||
using Bit.App;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Enums;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using XLabs.Ioc;
|
||||
|
||||
namespace Bit.Android.Autofill
|
||||
{
|
||||
[Service(Permission = Manifest.Permission.BindAutofillService, Label = "Bitwarden")]
|
||||
[IntentFilter(new string[] { "android.service.autofill.AutofillService" })]
|
||||
[MetaData("android.autofill", Resource = "@xml/autofillservice")]
|
||||
[Register("com.x8bit.bitwarden.Autofill.AutofillService")]
|
||||
public class AutofillService : global::Android.Service.Autofill.AutofillService
|
||||
{
|
||||
private ICipherService _cipherService;
|
||||
private ILockService _lockService;
|
||||
|
||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
|
||||
{
|
||||
var structure = request.FillContexts?.LastOrDefault()?.Structure;
|
||||
if(structure == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var parser = new Parser(structure);
|
||||
parser.Parse();
|
||||
|
||||
if(!parser.ShouldAutofill)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(_lockService == null)
|
||||
{
|
||||
_lockService = Resolver.Resolve<ILockService>();
|
||||
}
|
||||
|
||||
List<FilledItem> items = null;
|
||||
var locked = (await _lockService.GetLockTypeAsync(false)) != LockType.None;
|
||||
if(!locked)
|
||||
{
|
||||
if(_cipherService == null)
|
||||
{
|
||||
_cipherService = Resolver.Resolve<ICipherService>();
|
||||
}
|
||||
|
||||
items = await AutofillHelpers.GetFillItemsAsync(parser, _cipherService);
|
||||
}
|
||||
|
||||
// build response
|
||||
var response = AutofillHelpers.BuildFillResponse(this, parser, items, locked);
|
||||
callback.OnSuccess(response);
|
||||
}
|
||||
|
||||
public override void OnSaveRequest(SaveRequest request, SaveCallback callback)
|
||||
{
|
||||
var structure = request.FillContexts?.LastOrDefault()?.Structure;
|
||||
if(structure == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var parser = new Parser(structure);
|
||||
parser.Parse();
|
||||
|
||||
var savedItem = parser.FieldCollection.GetSavedItem();
|
||||
if(savedItem == null)
|
||||
{
|
||||
Toast.MakeText(this, "Unable to save this form.", ToastLength.Short).Show();
|
||||
return;
|
||||
}
|
||||
|
||||
var intent = new Intent(this, typeof(MainActivity));
|
||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop);
|
||||
intent.PutExtra("autofillFramework", true);
|
||||
intent.PutExtra("autofillFrameworkSave", true);
|
||||
intent.PutExtra("autofillFrameworkType", (int)savedItem.Type);
|
||||
switch(savedItem.Type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
intent.PutExtra("autofillFrameworkName", parser.Uri
|
||||
.Replace(Constants.AndroidAppProtocol, string.Empty)
|
||||
.Replace("https://", string.Empty)
|
||||
.Replace("http://", string.Empty));
|
||||
intent.PutExtra("autofillFrameworkUri", parser.Uri);
|
||||
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;
|
||||
}
|
||||
StartActivity(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.Service.Autofill;
|
||||
using Android.Views;
|
||||
using Android.Views.Autofill;
|
||||
using static Android.App.Assist.AssistStructure;
|
||||
using Android.Text;
|
||||
using static Android.Views.ViewStructure;
|
||||
|
||||
namespace Bit.Android.Autofill
|
||||
{
|
||||
public class Field
|
||||
{
|
||||
private List<string> _hints;
|
||||
|
||||
public Field(ViewNode node)
|
||||
{
|
||||
Id = node.Id;
|
||||
TrackingId = $"{node.Id}_{node.GetHashCode()}";
|
||||
IdEntry = node.IdEntry;
|
||||
AutofillId = node.AutofillId;
|
||||
AutofillType = node.AutofillType;
|
||||
InputType = node.InputType;
|
||||
Focused = node.IsFocused;
|
||||
Selected = node.IsSelected;
|
||||
Clickable = node.IsClickable;
|
||||
Visible = node.Visibility == ViewStates.Visible;
|
||||
Hints = FilterForSupportedHints(node.GetAutofillHints());
|
||||
Hint = node.Hint;
|
||||
AutofillOptions = node.GetAutofillOptions()?.ToList();
|
||||
HtmlInfo = node.HtmlInfo;
|
||||
Node = node;
|
||||
|
||||
if(node.AutofillValue != null)
|
||||
{
|
||||
if(node.AutofillValue.IsList)
|
||||
{
|
||||
var autofillOptions = node.GetAutofillOptions();
|
||||
if(autofillOptions != null && autofillOptions.Length > 0)
|
||||
{
|
||||
ListValue = node.AutofillValue.ListValue;
|
||||
TextValue = autofillOptions[node.AutofillValue.ListValue];
|
||||
}
|
||||
}
|
||||
else if(node.AutofillValue.IsDate)
|
||||
{
|
||||
DateValue = node.AutofillValue.DateValue;
|
||||
}
|
||||
else if(node.AutofillValue.IsText)
|
||||
{
|
||||
TextValue = node.AutofillValue.TextValue;
|
||||
}
|
||||
else if(node.AutofillValue.IsToggle)
|
||||
{
|
||||
ToggleValue = node.AutofillValue.ToggleValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SaveDataType SaveType { get; set; } = SaveDataType.Generic;
|
||||
public List<string> Hints
|
||||
{
|
||||
get => _hints;
|
||||
set
|
||||
{
|
||||
_hints = value;
|
||||
UpdateSaveTypeFromHints();
|
||||
}
|
||||
}
|
||||
public string Hint { get; set; }
|
||||
public int Id { get; private set; }
|
||||
public string TrackingId { get; private set; }
|
||||
public string IdEntry { get; set; }
|
||||
public AutofillId AutofillId { get; private set; }
|
||||
public AutofillType AutofillType { get; private set; }
|
||||
public InputTypes InputType { get; private set; }
|
||||
public bool Focused { get; private set; }
|
||||
public bool Selected { get; private set; }
|
||||
public bool Clickable { get; private set; }
|
||||
public bool Visible { get; private set; }
|
||||
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 HtmlInfo HtmlInfo { get; private set; }
|
||||
public ViewNode Node { get; private set; }
|
||||
|
||||
private void UpdateSaveTypeFromHints()
|
||||
{
|
||||
SaveType = SaveDataType.Generic;
|
||||
if(_hints == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(var hint in _hints)
|
||||
{
|
||||
switch(hint)
|
||||
{
|
||||
case View.AutofillHintCreditCardExpirationDate:
|
||||
case View.AutofillHintCreditCardExpirationDay:
|
||||
case View.AutofillHintCreditCardExpirationMonth:
|
||||
case View.AutofillHintCreditCardExpirationYear:
|
||||
case View.AutofillHintCreditCardNumber:
|
||||
case View.AutofillHintCreditCardSecurityCode:
|
||||
SaveType |= SaveDataType.CreditCard;
|
||||
break;
|
||||
case View.AutofillHintEmailAddress:
|
||||
SaveType |= SaveDataType.EmailAddress;
|
||||
break;
|
||||
case View.AutofillHintPhone:
|
||||
case View.AutofillHintName:
|
||||
SaveType |= SaveDataType.Generic;
|
||||
break;
|
||||
case View.AutofillHintPassword:
|
||||
SaveType |= SaveDataType.Password;
|
||||
SaveType &= ~SaveDataType.EmailAddress;
|
||||
SaveType &= ~SaveDataType.Username;
|
||||
break;
|
||||
case View.AutofillHintPostalAddress:
|
||||
case View.AutofillHintPostalCode:
|
||||
SaveType |= SaveDataType.Address;
|
||||
break;
|
||||
case View.AutofillHintUsername:
|
||||
SaveType |= SaveDataType.Username;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ValueIsNull()
|
||||
{
|
||||
return TextValue == null && DateValue == null && ToggleValue == null;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if(this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(obj == null || GetType() != obj.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var field = obj as Field;
|
||||
if(TextValue != null ? !TextValue.Equals(field.TextValue) : field.TextValue != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(DateValue != null ? !DateValue.Equals(field.DateValue) : field.DateValue != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ToggleValue != null ? ToggleValue.Equals(field.ToggleValue) : field.ToggleValue == null;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var result = TextValue != null ? TextValue.GetHashCode() : 0;
|
||||
result = 31 * result + (DateValue != null ? DateValue.GetHashCode() : 0);
|
||||
result = 31 * result + (ToggleValue != null ? ToggleValue.GetHashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<string> FilterForSupportedHints(string[] hints)
|
||||
{
|
||||
return hints?.Where(h => IsValidHint(h)).ToList() ?? new List<string>();
|
||||
}
|
||||
|
||||
private static bool IsValidHint(string hint)
|
||||
{
|
||||
switch(hint)
|
||||
{
|
||||
case View.AutofillHintCreditCardExpirationDate:
|
||||
case View.AutofillHintCreditCardExpirationDay:
|
||||
case View.AutofillHintCreditCardExpirationMonth:
|
||||
case View.AutofillHintCreditCardExpirationYear:
|
||||
case View.AutofillHintCreditCardNumber:
|
||||
case View.AutofillHintCreditCardSecurityCode:
|
||||
case View.AutofillHintEmailAddress:
|
||||
case View.AutofillHintPhone:
|
||||
case View.AutofillHintName:
|
||||
case View.AutofillHintPassword:
|
||||
case View.AutofillHintPostalAddress:
|
||||
case View.AutofillHintPostalCode:
|
||||
case View.AutofillHintUsername:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Android.Service.Autofill;
|
||||
using Android.Views.Autofill;
|
||||
using System.Linq;
|
||||
using Android.Text;
|
||||
using Android.Views;
|
||||
|
||||
namespace Bit.Android.Autofill
|
||||
{
|
||||
public class FieldCollection
|
||||
{
|
||||
private List<Field> _passwordFields = null;
|
||||
private List<Field> _usernameFields = null;
|
||||
private HashSet<string> _ignoreSearchTerms = new HashSet<string> { "search", "find", "recipient", "edit" };
|
||||
private HashSet<string> _passwordTerms = new HashSet<string> { "password", "pswd" };
|
||||
|
||||
public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>();
|
||||
public SaveDataType SaveType
|
||||
{
|
||||
get
|
||||
{
|
||||
if(FillableForLogin)
|
||||
{
|
||||
return SaveDataType.Password;
|
||||
}
|
||||
else if(FillableForCard)
|
||||
{
|
||||
return SaveDataType.CreditCard;
|
||||
}
|
||||
|
||||
return SaveDataType.Generic;
|
||||
}
|
||||
}
|
||||
public HashSet<string> Hints { get; private set; } = new HashSet<string>();
|
||||
public HashSet<string> FocusedHints { get; private set; } = new HashSet<string>();
|
||||
public HashSet<string> FieldTrackingIds { get; private set; } = new HashSet<string>();
|
||||
public List<Field> Fields { get; private set; } = new List<Field>();
|
||||
public IDictionary<string, List<Field>> HintToFieldsMap { get; private set; } =
|
||||
new Dictionary<string, List<Field>>();
|
||||
public List<AutofillId> IgnoreAutofillIds { get; private set; } = new List<AutofillId>();
|
||||
|
||||
public List<Field> PasswordFields
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_passwordFields != null)
|
||||
{
|
||||
return _passwordFields;
|
||||
}
|
||||
|
||||
if(Hints.Any())
|
||||
{
|
||||
_passwordFields = new List<Field>();
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
|
||||
{
|
||||
_passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList();
|
||||
if(!_passwordFields.Any())
|
||||
{
|
||||
_passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return _passwordFields;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Field> UsernameFields
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_usernameFields != null)
|
||||
{
|
||||
return _usernameFields;
|
||||
}
|
||||
|
||||
_usernameFields = new List<Field>();
|
||||
if(Hints.Any())
|
||||
{
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintEmailAddress))
|
||||
{
|
||||
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintEmailAddress]);
|
||||
}
|
||||
if(HintToFieldsMap.ContainsKey(View.AutofillHintUsername))
|
||||
{
|
||||
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(var passwordField in PasswordFields)
|
||||
{
|
||||
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId)
|
||||
.LastOrDefault();
|
||||
if(usernameField != null)
|
||||
{
|
||||
_usernameFields.Add(usernameField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _usernameFields;
|
||||
}
|
||||
}
|
||||
|
||||
public bool FillableForLogin => FocusedHintsContain(
|
||||
new string[] { View.AutofillHintUsername, View.AutofillHintEmailAddress, View.AutofillHintPassword }) ||
|
||||
UsernameFields.Any(f => f.Focused) || PasswordFields.Any(f => f.Focused);
|
||||
public bool FillableForCard => FocusedHintsContain(
|
||||
new string[] { View.AutofillHintCreditCardNumber, View.AutofillHintCreditCardExpirationMonth,
|
||||
View.AutofillHintCreditCardExpirationYear, View.AutofillHintCreditCardSecurityCode});
|
||||
public bool FillableForIdentity => FocusedHintsContain(
|
||||
new string[] { View.AutofillHintName, View.AutofillHintPhone, View.AutofillHintPostalAddress,
|
||||
View.AutofillHintPostalCode });
|
||||
|
||||
public bool Fillable => FillableForLogin || FillableForCard || FillableForIdentity;
|
||||
|
||||
public void Add(Field field)
|
||||
{
|
||||
if(field == null || FieldTrackingIds.Contains(field.TrackingId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_passwordFields = _usernameFields = null;
|
||||
|
||||
FieldTrackingIds.Add(field.TrackingId);
|
||||
Fields.Add(field);
|
||||
AutofillIds.Add(field.AutofillId);
|
||||
|
||||
if(field.Hints != null)
|
||||
{
|
||||
foreach(var hint in field.Hints)
|
||||
{
|
||||
Hints.Add(hint);
|
||||
if(field.Focused)
|
||||
{
|
||||
FocusedHints.Add(hint);
|
||||
}
|
||||
|
||||
if(!HintToFieldsMap.ContainsKey(hint))
|
||||
{
|
||||
HintToFieldsMap.Add(hint, new List<Field>());
|
||||
}
|
||||
|
||||
HintToFieldsMap[hint].Add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SavedItem GetSavedItem()
|
||||
{
|
||||
if(SaveType == SaveDataType.Password)
|
||||
{
|
||||
var passwordField = PasswordFields.FirstOrDefault(f => !string.IsNullOrWhiteSpace(f.TextValue));
|
||||
if(passwordField == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var savedItem = new SavedItem
|
||||
{
|
||||
Type = App.Enums.CipherType.Login,
|
||||
Login = new SavedItem.LoginItem
|
||||
{
|
||||
Password = GetFieldValue(passwordField)
|
||||
}
|
||||
};
|
||||
|
||||
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
|
||||
savedItem.Login.Username = GetFieldValue(usernameField);
|
||||
|
||||
return savedItem;
|
||||
}
|
||||
else if(SaveType == SaveDataType.CreditCard)
|
||||
{
|
||||
var savedItem = new SavedItem
|
||||
{
|
||||
Type = App.Enums.CipherType.Card,
|
||||
Card = new SavedItem.CardItem
|
||||
{
|
||||
Number = GetFieldValue(View.AutofillHintCreditCardNumber),
|
||||
Name = GetFieldValue(View.AutofillHintName),
|
||||
ExpMonth = GetFieldValue(View.AutofillHintCreditCardExpirationMonth, true),
|
||||
ExpYear = GetFieldValue(View.AutofillHintCreditCardExpirationYear),
|
||||
Code = GetFieldValue(View.AutofillHintCreditCardSecurityCode)
|
||||
}
|
||||
};
|
||||
|
||||
return savedItem;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private bool FieldIsPassword(Field f)
|
||||
{
|
||||
var inputTypePassword = f.InputType.HasFlag(InputTypes.TextVariationPassword) ||
|
||||
f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) ||
|
||||
f.InputType.HasFlag(InputTypes.TextVariationWebPassword);
|
||||
|
||||
// For whatever reason, multi-line input types are coming through with TextVariationPassword flags
|
||||
if(inputTypePassword && f.InputType.HasFlag(InputTypes.TextVariationPassword) &&
|
||||
f.InputType.HasFlag(InputTypes.TextFlagMultiLine))
|
||||
{
|
||||
inputTypePassword = false;
|
||||
}
|
||||
|
||||
if(!inputTypePassword && f.HtmlInfo != null && f.HtmlInfo.Tag == "input" &&
|
||||
(f.HtmlInfo.Attributes?.Any() ?? false))
|
||||
{
|
||||
foreach(var a in f.HtmlInfo.Attributes)
|
||||
{
|
||||
var key = a.First as Java.Lang.String;
|
||||
var val = a.Second as Java.Lang.String;
|
||||
if(key != null && val != null && key.ToString() == "type" && val.ToString() == "password")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputTypePassword && !ValueContainsAnyTerms(f.IdEntry, _ignoreSearchTerms) &&
|
||||
!ValueContainsAnyTerms(f.Hint, _ignoreSearchTerms);
|
||||
}
|
||||
|
||||
private bool FieldHasPasswordTerms(Field f)
|
||||
{
|
||||
return ValueContainsAnyTerms(f.IdEntry, _passwordTerms) || ValueContainsAnyTerms(f.Hint, _passwordTerms);
|
||||
}
|
||||
|
||||
private bool ValueContainsAnyTerms(string value, HashSet<string> terms)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var lowerValue = value.ToLowerInvariant();
|
||||
return terms.Any(t => lowerValue.Contains(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
using System;
|
||||
using Android.Service.Autofill;
|
||||
using Android.Views.Autofill;
|
||||
using System.Linq;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Enums;
|
||||
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 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,136 +0,0 @@
|
||||
using static Android.App.Assist.AssistStructure;
|
||||
using Android.App.Assist;
|
||||
using Bit.App;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bit.Android.Autofill
|
||||
{
|
||||
public class Parser
|
||||
{
|
||||
|
||||
public static HashSet<string> ExcludedPackageIds = new HashSet<string>
|
||||
{
|
||||
"android"
|
||||
};
|
||||
|
||||
private readonly AssistStructure _structure;
|
||||
private string _uri;
|
||||
private string _packageName;
|
||||
private string _webDomain;
|
||||
|
||||
public Parser(AssistStructure structure)
|
||||
{
|
||||
_structure = structure;
|
||||
}
|
||||
|
||||
public FieldCollection FieldCollection { get; private set; } = new FieldCollection();
|
||||
public string Uri
|
||||
{
|
||||
get
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(_uri))
|
||||
{
|
||||
return _uri;
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(WebDomain) && string.IsNullOrWhiteSpace(PackageName))
|
||||
{
|
||||
_uri = null;
|
||||
}
|
||||
else if(!string.IsNullOrWhiteSpace(WebDomain))
|
||||
{
|
||||
_uri = string.Concat("http://", WebDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
_uri = string.Concat(Constants.AndroidAppProtocol, PackageName);
|
||||
}
|
||||
|
||||
return _uri;
|
||||
}
|
||||
}
|
||||
public string PackageName
|
||||
{
|
||||
get => _packageName;
|
||||
set
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
_packageName = _uri = null;
|
||||
}
|
||||
|
||||
_packageName = value;
|
||||
}
|
||||
}
|
||||
public string WebDomain
|
||||
{
|
||||
get => _webDomain;
|
||||
set
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
_webDomain = _uri = null;
|
||||
}
|
||||
|
||||
_webDomain = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldAutofill
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
|
||||
FieldCollection != null && FieldCollection.Fillable;
|
||||
}
|
||||
}
|
||||
|
||||
public void Parse()
|
||||
{
|
||||
for(var i = 0; i < _structure.WindowNodeCount; i++)
|
||||
{
|
||||
var node = _structure.GetWindowNodeAt(i);
|
||||
ParseNode(node.RootViewNode);
|
||||
}
|
||||
|
||||
if(!AutofillHelpers.TrustedBrowsers.Contains(PackageName) &&
|
||||
!AutofillHelpers.CompatBrowsers.Contains(PackageName))
|
||||
{
|
||||
WebDomain = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseNode(ViewNode node)
|
||||
{
|
||||
SetPackageAndDomain(node);
|
||||
var hints = node.GetAutofillHints();
|
||||
var isEditText = node.ClassName == "android.widget.EditText" || node?.HtmlInfo?.Tag == "input";
|
||||
if(isEditText || (hints?.Length ?? 0) > 0)
|
||||
{
|
||||
FieldCollection.Add(new Field(node));
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldCollection.IgnoreAutofillIds.Add(node.AutofillId);
|
||||
}
|
||||
|
||||
for(var i = 0; i < node.ChildCount; i++)
|
||||
{
|
||||
ParseNode(node.GetChildAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPackageAndDomain(ViewNode node)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(PackageName) && !string.IsNullOrWhiteSpace(node.IdPackage) &&
|
||||
!ExcludedPackageIds.Contains(node.IdPackage))
|
||||
{
|
||||
PackageName = node.IdPackage;
|
||||
}
|
||||
if(string.IsNullOrWhiteSpace(WebDomain) && !string.IsNullOrWhiteSpace(node.WebDomain))
|
||||
{
|
||||
WebDomain = node.WebDomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Bit.App.Enums;
|
||||
|
||||
namespace Bit.Android.Autofill
|
||||
{
|
||||
public class SavedItem
|
||||
{
|
||||
public CipherType Type { get; set; }
|
||||
public LoginItem Login { get; set; }
|
||||
public CardItem Card { get; set; }
|
||||
|
||||
public class LoginItem
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public class CardItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string ExpMonth { get; set; }
|
||||
public string ExpYear { get; set; }
|
||||
public string Code { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user