diff --git a/src/Android/AutofillActivity.cs b/src/Android/AutofillActivity.cs
index f35a6eb3d..5e7319411 100644
--- a/src/Android/AutofillActivity.cs
+++ b/src/Android/AutofillActivity.cs
@@ -19,31 +19,49 @@ namespace Bit.Android
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
- _lastQueriedUri = Intent.GetStringExtra("uri");
+ LaunchMainActivity(Intent, 932473);
+ }
- var intent = new Intent(this, typeof(MainActivity));
- intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop);
- intent.PutExtra("uri", _lastQueriedUri);
- StartActivityForResult(intent, 123);
+ protected override void OnNewIntent(Intent intent)
+ {
+ base.OnNewIntent(intent);
+ LaunchMainActivity(intent, 489729);
+ }
+
+ protected override void OnDestroy()
+ {
+ base.OnDestroy();
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
+ if(data == null)
+ {
+ LastCredentials = null;
+ return;
+ }
try
{
- var uri = data.GetStringExtra("uri");
- var username = data.GetStringExtra("username");
- var password = data.GetStringExtra("password");
-
- LastCredentials = new AutofillCredentials
+ if(data.GetStringExtra("canceled") != null)
{
- Username = username,
- Password = password,
- Uri = uri,
- LastUri = _lastQueriedUri
- };
+ LastCredentials = null;
+ }
+ else
+ {
+ var uri = data.GetStringExtra("uri");
+ var username = data.GetStringExtra("username");
+ var password = data.GetStringExtra("password");
+
+ LastCredentials = new AutofillCredentials
+ {
+ Username = username,
+ Password = password,
+ Uri = uri,
+ LastUri = _lastQueriedUri
+ };
+ }
}
catch
{
@@ -54,5 +72,18 @@ namespace Bit.Android
Finish();
}
}
+
+ private void LaunchMainActivity(Intent callingIntent, int requestCode)
+ {
+ _lastQueriedUri = callingIntent?.GetStringExtra("uri");
+ if(_lastQueriedUri == null)
+ {
+ return;
+ }
+
+ var intent = new Intent(this, typeof(MainActivity));
+ intent.PutExtra("uri", _lastQueriedUri);
+ StartActivityForResult(intent, requestCode);
+ }
}
}
diff --git a/src/Android/AutofillService.cs b/src/Android/AutofillService.cs
index 4792159e7..cd24eae3e 100644
--- a/src/Android/AutofillService.cs
+++ b/src/Android/AutofillService.cs
@@ -15,17 +15,17 @@ namespace Bit.Android
public class AutofillService : AccessibilityService
{
private const int AutoFillNotificationId = 34573;
- private const string AndroidAppProtocol = "androidapp://";
private const string SystemUiPackage = "com.android.systemui";
private const string ChromePackage = "com.android.chrome";
private const string BrowserPackage = "com.android.browser";
+ private const string BitwardenPackage = "com.x8bit.bitwarden";
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
var eventType = e.EventType;
var packageName = e.PackageName;
- if(packageName == SystemUiPackage)
+ if(packageName == SystemUiPackage || packageName == BitwardenPackage)
{
return;
}
@@ -34,15 +34,14 @@ namespace Bit.Android
{
case EventTypes.WindowContentChanged:
case EventTypes.WindowStateChanged:
+ var cancelNotification = true;
var root = RootInActiveWindow;
var isChrome = root == null ? false : root.PackageName == ChromePackage;
- var cancelNotification = true;
- var avialablePasswordNodes = GetNodeOrChildren(root, n => AvailablePasswordField(n, isChrome));
+ var avialablePasswordNodes = GetWindowNodes(root, e, n => AvailablePasswordField(n, isChrome));
- if(avialablePasswordNodes.Any() && AnyNodeOrChildren(root, n => n.WindowId == e.WindowId &&
- !(n.ViewIdResourceName != null && n.ViewIdResourceName.StartsWith(SystemUiPackage))))
+ if(avialablePasswordNodes.Any())
{
- var uri = string.Concat(AndroidAppProtocol, root.PackageName);
+ var uri = string.Concat(App.Constants.AndroidAppProtocol, root.PackageName);
if(isChrome)
{
var addressNode = root.FindAccessibilityNodeInfosByViewId("com.android.chrome:id/url_bar")
@@ -57,23 +56,25 @@ namespace Bit.Android
uri = ExtractUriFromAddressField(uri, addressNode);
}
- var allEditTexts = GetNodeOrChildren(root, n => EditText(n));
- var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
-
- if(AutofillActivity.LastCredentials != null && SameUri(AutofillActivity.LastCredentials.LastUri, uri))
+ if(NeedToAutofill(AutofillActivity.LastCredentials, uri))
{
+ var allEditTexts = GetWindowNodes(root, e, n => EditText(n));
+ var usernameEditText = allEditTexts.TakeWhile(n => !n.Password).LastOrDefault();
FillCredentials(usernameEditText, avialablePasswordNodes);
}
else
{
- AskFillPassword(uri, usernameEditText, avialablePasswordNodes);
+ NotifyToAutofill(uri);
cancelNotification = false;
}
+
+ AutofillActivity.LastCredentials = null;
}
if(cancelNotification)
{
- ((NotificationManager)GetSystemService(NotificationService)).Cancel(AutoFillNotificationId);
+ var notificationManager = ((NotificationManager)GetSystemService(NotificationService));
+ notificationManager.Cancel(AutoFillNotificationId);
}
break;
default:
@@ -100,11 +101,21 @@ namespace Bit.Android
return uri;
}
- private bool SameUri(string uriString1, string uriString2)
+ ///
+ /// Check to make sure it is ok to autofill still on the current screen
+ ///
+ private bool NeedToAutofill(AutofillCredentials creds, string currentUriString)
{
- Uri uri1, uri2;
- if(Uri.TryCreate(uriString1, UriKind.RelativeOrAbsolute, out uri1) &&
- Uri.TryCreate(uriString2, UriKind.RelativeOrAbsolute, out uri2) && uri1.Host == uri2.Host)
+ if(creds == null)
+ {
+ return false;
+ }
+
+ Uri credsUri, lastUri, currentUri;
+ if(Uri.TryCreate(creds.Uri, UriKind.Absolute, out credsUri) &&
+ Uri.TryCreate(creds.LastUri, UriKind.Absolute, out lastUri) &&
+ Uri.TryCreate(currentUriString, UriKind.Absolute, out currentUri) &&
+ credsUri.Host == currentUri.Host && lastUri.Host == currentUri.Host)
{
return true;
}
@@ -124,8 +135,7 @@ namespace Bit.Android
return n.ClassName != null && n.ClassName.Contains("EditText");
}
- private void AskFillPassword(string uri, AccessibilityNodeInfo usernameNode,
- IEnumerable passwordNodes)
+ private void NotifyToAutofill(string uri)
{
var intent = new Intent(this, typeof(AutofillActivity));
intent.PutExtra("uri", uri);
@@ -134,10 +144,10 @@ namespace Bit.Android
var builder = new Notification.Builder(this);
builder.SetSmallIcon(Resource.Drawable.notification_sm)
- .SetContentText("Tap this notification to autofill a login from your bitwarden vault.")
.SetContentTitle("bitwarden Autofill Service")
- .SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis())
+ .SetContentText("Tap this notification to autofill a login from your bitwarden vault.")
.SetTicker("Tap this notification to autofill a login from your bitwarden vault.")
+ .SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis())
.SetVisibility(NotificationVisibility.Secret)
.SetContentIntent(pendingIntent);
@@ -148,39 +158,37 @@ namespace Bit.Android
private void FillCredentials(AccessibilityNodeInfo usernameNode, IEnumerable passwordNodes)
{
FillEditText(usernameNode, AutofillActivity.LastCredentials.Username);
- foreach(var pNode in passwordNodes)
+ foreach(var n in passwordNodes)
{
- FillEditText(pNode, AutofillActivity.LastCredentials.Password);
+ FillEditText(n, AutofillActivity.LastCredentials.Password);
}
-
- AutofillActivity.LastCredentials = null;
}
private static void FillEditText(AccessibilityNodeInfo editTextNode, string value)
{
+ if(editTextNode == null || value == null)
+ {
+ return;
+ }
+
var bundle = new Bundle();
bundle.PutString(AccessibilityNodeInfo.ActionArgumentSetTextCharsequence, value);
editTextNode.PerformAction(global::Android.Views.Accessibility.Action.SetText, bundle);
}
- private bool AnyNodeOrChildren(AccessibilityNodeInfo n, Func p)
- {
- return GetNodeOrChildren(n, p).Any();
- }
-
- private IEnumerable GetNodeOrChildren(AccessibilityNodeInfo n,
- Func p)
+ private IEnumerable GetWindowNodes(AccessibilityNodeInfo n,
+ AccessibilityEvent e, Func p)
{
if(n != null)
{
- if(p(n))
+ if(n.WindowId == e.WindowId && !(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false) && p(n))
{
yield return n;
}
for(int i = 0; i < n.ChildCount; i++)
{
- foreach(var node in GetNodeOrChildren(n.GetChild(i), p))
+ foreach(var node in GetWindowNodes(n.GetChild(i), e, p))
{
yield return node;
}
diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs
index d6d3e9f2a..35f45d1a2 100644
--- a/src/Android/MainActivity.cs
+++ b/src/Android/MainActivity.cs
@@ -89,9 +89,16 @@ namespace Bit.Android
private void ReturnCredentials(VaultListPageModel.Login login)
{
Intent data = new Intent();
- data.PutExtra("uri", login.Uri.Value);
- data.PutExtra("username", login.Username);
- data.PutExtra("password", login.Password.Value);
+ if(login == null)
+ {
+ data.PutExtra("canceled", "true");
+ }
+ else
+ {
+ data.PutExtra("uri", login.Uri.Value);
+ data.PutExtra("username", login.Username);
+ data.PutExtra("password", login.Password.Value);
+ }
if(Parent == null)
{
diff --git a/src/Android/Resources/xml/accessibilityservice.xml b/src/Android/Resources/xml/accessibilityservice.xml
index 0c63cc3b8..ea2613f83 100644
--- a/src/Android/Resources/xml/accessibilityservice.xml
+++ b/src/Android/Resources/xml/accessibilityservice.xml
@@ -1,8 +1,7 @@
\ No newline at end of file
+ android:canRetrieveWindowContent="true"/>
\ No newline at end of file
diff --git a/src/App/App.csproj b/src/App/App.csproj
index 5edd86f79..cda72aea4 100644
--- a/src/App/App.csproj
+++ b/src/App/App.csproj
@@ -72,6 +72,7 @@
+
diff --git a/src/App/Constants.cs b/src/App/Constants.cs
index 9b4c1fe59..9afca298f 100644
--- a/src/App/Constants.cs
+++ b/src/App/Constants.cs
@@ -2,6 +2,8 @@
{
public static class Constants
{
+ public const string AndroidAppProtocol = "androidapp://";
+
public const string SettingFingerprintUnlockOn = "setting:fingerprintUnlockOn";
public const string SettingPinUnlockOn = "setting:pinUnlockOn";
public const string SettingLockSeconds = "setting:lockSeconds";
diff --git a/src/App/Controls/VaultListViewCell.cs b/src/App/Controls/VaultListViewCell.cs
new file mode 100644
index 000000000..5733fc4b0
--- /dev/null
+++ b/src/App/Controls/VaultListViewCell.cs
@@ -0,0 +1,40 @@
+using Bit.App.Models.Page;
+using System;
+using Xamarin.Forms;
+
+namespace Bit.App.Controls
+{
+ public class VaultListViewCell : LabeledDetailCell
+ {
+ private Action _moreClickedAction;
+
+ public static readonly BindableProperty LoginParameterProperty = BindableProperty.Create(nameof(LoginParameter),
+ typeof(VaultListPageModel.Login), typeof(VaultListViewCell), null);
+
+ public VaultListViewCell(Action moreClickedAction)
+ {
+ _moreClickedAction = moreClickedAction;
+
+ SetBinding(LoginParameterProperty, new Binding("."));
+ Label.SetBinding(Label.TextProperty, s => s.Name);
+ Detail.SetBinding(Label.TextProperty, s => s.Username);
+
+ Button.Image = "more";
+ Button.Command = new Command(() => ShowMore());
+ Button.BackgroundColor = Color.Transparent;
+
+ BackgroundColor = Color.White;
+ }
+
+ public VaultListPageModel.Login LoginParameter
+ {
+ get { return GetValue(LoginParameterProperty) as VaultListPageModel.Login; }
+ set { SetValue(LoginParameterProperty, value); }
+ }
+
+ private void ShowMore()
+ {
+ _moreClickedAction?.Invoke(LoginParameter);
+ }
+ }
+}
diff --git a/src/App/Pages/Vault/VaultAddLoginPage.cs b/src/App/Pages/Vault/VaultAddLoginPage.cs
index 12e59cfef..879c6540e 100644
--- a/src/App/Pages/Vault/VaultAddLoginPage.cs
+++ b/src/App/Pages/Vault/VaultAddLoginPage.cs
@@ -23,9 +23,14 @@ namespace Bit.App.Pages
private readonly IConnectivity _connectivity;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
private readonly ISettings _settings;
+ private readonly string _defaultUri;
+ private readonly string _defaultName;
- public VaultAddLoginPage()
+ public VaultAddLoginPage(string defaultUri = null, string defaultName = null)
{
+ _defaultUri = defaultUri;
+ _defaultName = defaultName;
+
_loginService = Resolver.Resolve();
_folderService = Resolver.Resolve();
_userDialogs = Resolver.Resolve();
@@ -54,7 +59,16 @@ namespace Bit.App.Pages
usernameCell.Entry.Autocorrect = false;
var uriCell = new FormEntryCell(AppResources.URI, Keyboard.Url, nextElement: usernameCell.Entry);
+ if(!string.IsNullOrWhiteSpace(_defaultUri))
+ {
+ uriCell.Entry.Text = _defaultUri;
+ }
+
var nameCell = new FormEntryCell(AppResources.Name, nextElement: uriCell.Entry);
+ if(!string.IsNullOrWhiteSpace(_defaultName))
+ {
+ nameCell.Entry.Text = _defaultName;
+ }
var folderOptions = new List { AppResources.FolderNone };
var folders = _folderService.GetAllAsync().GetAwaiter().GetResult()
diff --git a/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs b/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs
index e73e6df80..9f97e0d27 100644
--- a/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs
+++ b/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs
@@ -12,49 +12,53 @@ using Bit.App.Utilities;
using PushNotification.Plugin.Abstractions;
using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions;
-using System.Collections.Generic;
using System.Threading;
using Bit.App.Models;
+using System.Collections.Generic;
namespace Bit.App.Pages
{
public class VaultAutofillListLoginsPage : ExtendedContentPage
{
- private readonly IFolderService _folderService;
private readonly ILoginService _loginService;
private readonly IUserDialogs _userDialogs;
- private readonly IConnectivity _connectivity;
private readonly IClipboardService _clipboardService;
- private readonly ISyncService _syncService;
- private readonly IPushNotification _pushNotification;
- private readonly IDeviceInfoService _deviceInfoService;
- private readonly ISettings _settings;
private CancellationTokenSource _filterResultsCancellationTokenSource;
private readonly DomainName _domainName;
+ private readonly string _uri;
+ private readonly string _name;
+ private readonly bool _androidApp = false;
public VaultAutofillListLoginsPage(string uriString)
: base(true)
{
+ _uri = uriString;
Uri uri;
- if(Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri) &&
- DomainName.TryParse(uri.Host, out _domainName)) { }
-
- _folderService = Resolver.Resolve();
+ if(!Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri) ||
+ !DomainName.TryParse(uri.Host, out _domainName))
+ {
+ if(uriString != null && uriString.StartsWith(Constants.AndroidAppProtocol))
+ {
+ _androidApp = true;
+ _name = uriString.Substring(Constants.AndroidAppProtocol.Length);
+ }
+ }
+ else
+ {
+ _name = _domainName.BaseDomain;
+ }
+
_loginService = Resolver.Resolve();
- _connectivity = Resolver.Resolve();
_userDialogs = Resolver.Resolve();
_clipboardService = Resolver.Resolve();
- _syncService = Resolver.Resolve();
- _pushNotification = Resolver.Resolve();
- _deviceInfoService = Resolver.Resolve();
- _settings = Resolver.Resolve();
Init();
}
public ExtendedObservableCollection PresentationLogins { get; private set; }
= new ExtendedObservableCollection();
-
+ public StackLayout NoDataStackLayout { get; set; }
public ListView ListView { get; set; }
+ public ActivityIndicator LoadingIndicator { get; set; }
private void Init()
{
@@ -66,13 +70,37 @@ namespace Bit.App.Pages
}
});
+ var noDataLabel = new Label
+ {
+ Text = string.Format(AppResources.NoLoginsForUri, _name ?? "--"),
+ HorizontalTextAlignment = TextAlignment.Center,
+ FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
+ Style = (Style)Application.Current.Resources["text-muted"]
+ };
+
+ var addLoginButton = new ExtendedButton
+ {
+ Text = AppResources.AddALogin,
+ Command = new Command(() => AddLoginAsync()),
+ Style = (Style)Application.Current.Resources["btn-primaryAccent"]
+ };
+
+ NoDataStackLayout = new StackLayout
+ {
+ Children = { noDataLabel, addLoginButton },
+ VerticalOptions = LayoutOptions.CenterAndExpand,
+ Padding = new Thickness(20, 0),
+ Spacing = 20
+ };
+
ToolbarItems.Add(new AddLoginToolBarItem(this));
ListView = new ListView(ListViewCachingStrategy.RecycleElement)
{
ItemsSource = PresentationLogins,
HasUnevenRows = true,
- ItemTemplate = new DataTemplate(() => new VaultListViewCell(this))
+ ItemTemplate = new DataTemplate(() => new VaultListViewCell(
+ (VaultListPageModel.Login l) => MoreClickedAsync(l)))
};
if(Device.OS == TargetPlatform.iOS)
@@ -82,9 +110,16 @@ namespace Bit.App.Pages
ListView.ItemSelected += LoginSelected;
- Title = AppResources.Logins;
+ Title = string.Format(AppResources.LoginsForUri, _name ?? "--");
- Content = ListView;
+ LoadingIndicator = new ActivityIndicator
+ {
+ IsRunning = true,
+ VerticalOptions = LayoutOptions.CenterAndExpand,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ Content = LoadingIndicator;
}
protected override void OnAppearing()
@@ -93,16 +128,27 @@ namespace Bit.App.Pages
_filterResultsCancellationTokenSource = FetchAndLoadVault();
}
+ protected override bool OnBackButtonPressed()
+ {
+ MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Login)null);
+ return true;
+ }
+
+ private void AdjustContent()
+ {
+ if(PresentationLogins.Count > 0)
+ {
+ Content = ListView;
+ }
+ else
+ {
+ Content = NoDataStackLayout;
+ }
+ }
+
private CancellationTokenSource FetchAndLoadVault()
{
var cts = new CancellationTokenSource();
- _settings.AddOrUpdateValue(Constants.FirstVaultLoad, false);
-
- if(PresentationLogins.Count > 0 && _syncService.SyncInProgress)
- {
- return cts;
- }
-
_filterResultsCancellationTokenSource?.Cancel();
Task.Run(async () =>
@@ -110,12 +156,16 @@ namespace Bit.App.Pages
var logins = await _loginService.GetAllAsync();
var filteredLogins = logins
.Select(s => new VaultListPageModel.Login(s))
- .Where(s => s.BaseDomain != null && s.BaseDomain == _domainName.BaseDomain)
+ .Where(s => (_androidApp && _domainName == null && s.Uri.Value == _uri) ||
+ (_domainName != null && s.BaseDomain != null && s.BaseDomain == _domainName.BaseDomain))
.OrderBy(s => s.Name)
- .ThenBy(s => s.Username)
- .ToArray();
+ .ThenBy(s => s.Username);
- PresentationLogins.ResetWithRange(filteredLogins);
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ PresentationLogins.ResetWithRange(filteredLogins);
+ AdjustContent();
+ });
}, cts.Token);
return cts;
@@ -127,12 +177,52 @@ namespace Bit.App.Pages
MessagingCenter.Send(Application.Current, "Autofill", login);
}
- private async void AddLogin()
+ private async void AddLoginAsync()
{
- var page = new VaultAddLoginPage();
+ var page = new VaultAddLoginPage(_uri, _name);
await Navigation.PushForDeviceAsync(page);
}
+ private async void MoreClickedAsync(VaultListPageModel.Login login)
+ {
+ var buttons = new List { AppResources.View, AppResources.Edit };
+ if(!string.IsNullOrWhiteSpace(login.Password.Value))
+ {
+ buttons.Add(AppResources.CopyPassword);
+ }
+ if(!string.IsNullOrWhiteSpace(login.Username))
+ {
+ buttons.Add(AppResources.CopyUsername);
+ }
+
+ var selection = await DisplayActionSheet(login.Name, AppResources.Cancel, null, buttons.ToArray());
+
+ if(selection == AppResources.View)
+ {
+ var page = new VaultViewLoginPage(login.Id);
+ await Navigation.PushForDeviceAsync(page);
+ }
+ else if(selection == AppResources.Edit)
+ {
+ var page = new VaultEditLoginPage(login.Id);
+ await Navigation.PushForDeviceAsync(page);
+ }
+ else if(selection == AppResources.CopyPassword)
+ {
+ Copy(login.Password.Value, AppResources.Password);
+ }
+ else if(selection == AppResources.CopyUsername)
+ {
+ Copy(login.Username, AppResources.Username);
+ }
+ }
+
+ private void Copy(string copyText, string alertLabel)
+ {
+ _clipboardService.CopyToClipboard(copyText);
+ _userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
+ }
+
private class AddLoginToolBarItem : ToolbarItem
{
private readonly VaultAutofillListLoginsPage _page;
@@ -147,32 +237,7 @@ namespace Bit.App.Pages
private void ClickedItem(object sender, EventArgs e)
{
- _page.AddLogin();
- }
- }
-
- private class VaultListViewCell : LabeledDetailCell
- {
- private VaultAutofillListLoginsPage _page;
-
- public static readonly BindableProperty LoginParameterProperty = BindableProperty.Create(nameof(LoginParameter),
- typeof(VaultListPageModel.Login), typeof(VaultListViewCell), null);
-
- public VaultListViewCell(VaultAutofillListLoginsPage page)
- {
- _page = page;
-
- SetBinding(LoginParameterProperty, new Binding("."));
- Label.SetBinding(Label.TextProperty, s => s.Name);
- Detail.SetBinding(Label.TextProperty, s => s.Username);
-
- BackgroundColor = Color.White;
- }
-
- public VaultListPageModel.Login LoginParameter
- {
- get { return GetValue(LoginParameterProperty) as VaultListPageModel.Login; }
- set { SetValue(LoginParameterProperty, value); }
+ _page.AddLoginAsync();
}
}
}
diff --git a/src/App/Pages/Vault/VaultListLoginsPage.cs b/src/App/Pages/Vault/VaultListLoginsPage.cs
index 3def0a9c2..a9bcd4715 100644
--- a/src/App/Pages/Vault/VaultListLoginsPage.cs
+++ b/src/App/Pages/Vault/VaultListLoginsPage.cs
@@ -83,7 +83,8 @@ namespace Bit.App.Pages
ItemsSource = PresentationFolders,
HasUnevenRows = true,
GroupHeaderTemplate = new DataTemplate(() => new VaultListHeaderViewCell(this)),
- ItemTemplate = new DataTemplate(() => new VaultListViewCell(this))
+ ItemTemplate = new DataTemplate(() => new VaultListViewCell(
+ (VaultListPageModel.Login l) => MoreClickedAsync(l)))
};
if(Device.OS == TargetPlatform.iOS)
@@ -439,40 +440,6 @@ namespace Bit.App.Pages
}
}
- private class VaultListViewCell : LabeledDetailCell
- {
- private VaultListLoginsPage _page;
-
- public static readonly BindableProperty LoginParameterProperty = BindableProperty.Create(nameof(LoginParameter),
- typeof(VaultListPageModel.Login), typeof(VaultListViewCell), null);
-
- public VaultListViewCell(VaultListLoginsPage page)
- {
- _page = page;
-
- SetBinding(LoginParameterProperty, new Binding("."));
- Label.SetBinding(Label.TextProperty, s => s.Name);
- Detail.SetBinding(Label.TextProperty, s => s.Username);
-
- Button.Image = "more";
- Button.Command = new Command(() => ShowMore());
- Button.BackgroundColor = Color.Transparent;
-
- BackgroundColor = Color.White;
- }
-
- public VaultListPageModel.Login LoginParameter
- {
- get { return GetValue(LoginParameterProperty) as VaultListPageModel.Login; }
- set { SetValue(LoginParameterProperty, value); }
- }
-
- private void ShowMore()
- {
- _page.MoreClickedAsync(LoginParameter);
- }
- }
-
private class VaultListHeaderViewCell : ExtendedViewCell
{
public VaultListHeaderViewCell(VaultListLoginsPage page)
diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs
index dcde522d2..077ee54f3 100644
--- a/src/App/Resources/AppResources.Designer.cs
+++ b/src/App/Resources/AppResources.Designer.cs
@@ -1015,6 +1015,15 @@ namespace Bit.App.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to Logins for {0}.
+ ///
+ public static string LoginsForUri {
+ get {
+ return ResourceManager.GetString("LoginsForUri", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Login updated..
///
@@ -1222,6 +1231,15 @@ namespace Bit.App.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to There are no logins in your vault for {0}..
+ ///
+ public static string NoLoginsForUri {
+ get {
+ return ResourceManager.GetString("NoLoginsForUri", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to There are no logins in your vault for this website. Tap to add one..
///
diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx
index 3a27c4170..88fe9e26c 100644
--- a/src/App/Resources/AppResources.resx
+++ b/src/App/Resources/AppResources.resx
@@ -757,4 +757,12 @@
Translations
+
+ Logins for {0}
+ This is used for the autofill service. ex. "Logins for twitter.com"
+
+
+ There are no logins in your vault for {0}.
+ This is used for the autofill service. ex. "There are no logins in your vault for twitter.com".
+
\ No newline at end of file