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