diff --git a/src/iOS.Autofill/LoginAddViewController.cs b/src/iOS.Autofill/LoginAddViewController.cs
index e5d1b4099..dade7eb12 100644
--- a/src/iOS.Autofill/LoginAddViewController.cs
+++ b/src/iOS.Autofill/LoginAddViewController.cs
@@ -10,6 +10,7 @@ namespace Bit.iOS.Autofill
{ }
public LoginListViewController LoginListController { get; set; }
+ public LoginSearchViewController LoginSearchController { get; set; }
public override UINavigationItem BaseNavItem => NavItem;
public override UIBarButtonItem BaseCancelButton => CancelBarButton;
@@ -19,6 +20,7 @@ namespace Bit.iOS.Autofill
{
_googleAnalyticsService.TrackAutofillExtensionEvent("CreatedLogin");
LoginListController?.DismissModal();
+ LoginSearchController?.DismissModal();
};
partial void CancelBarButton_Activated(UIBarButtonItem sender)
diff --git a/src/iOS.Autofill/LoginListViewController.cs b/src/iOS.Autofill/LoginListViewController.cs
index 33d3db4dc..2935bc5dd 100644
--- a/src/iOS.Autofill/LoginListViewController.cs
+++ b/src/iOS.Autofill/LoginListViewController.cs
@@ -39,7 +39,7 @@ namespace Bit.iOS.Autofill
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
- CPViewController.CompleteRequest(null, null, null);
+ CPViewController.CompleteRequest();
}
partial void AddBarButton_Activated(UIBarButtonItem sender)
@@ -47,17 +47,28 @@ namespace Bit.iOS.Autofill
PerformSegue("loginAddSegue", this);
}
+ partial void SearchBarButton_Activated(UIBarButtonItem sender)
+ {
+ PerformSegue("loginSearchFromListSegue", this);
+ }
+
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
var navController = segue.DestinationViewController as UINavigationController;
if(navController != null)
{
+ var searchLoginController = navController.TopViewController as LoginSearchViewController;
var addLoginController = navController.TopViewController as LoginAddViewController;
if(addLoginController != null)
{
addLoginController.Context = Context;
addLoginController.LoginListController = this;
}
+ if(searchLoginController != null)
+ {
+ searchLoginController.Context = Context;
+ searchLoginController.CPViewController = CPViewController;
+ }
}
}
diff --git a/src/iOS.Autofill/LoginListViewController.designer.cs b/src/iOS.Autofill/LoginListViewController.designer.cs
index 3e0636502..6e1f5265a 100644
--- a/src/iOS.Autofill/LoginListViewController.designer.cs
+++ b/src/iOS.Autofill/LoginListViewController.designer.cs
@@ -34,6 +34,10 @@ namespace Bit.iOS.Autofill
[GeneratedCode ("iOS Designer", "1.0")]
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
+ [Action ("SearchBarButton_Activated:")]
+ [GeneratedCode ("iOS Designer", "1.0")]
+ partial void SearchBarButton_Activated (UIKit.UIBarButtonItem sender);
+
void ReleaseDesignerOutlets ()
{
if (AddBarButton != null) {
diff --git a/src/iOS.Autofill/LoginSearchViewController.cs b/src/iOS.Autofill/LoginSearchViewController.cs
new file mode 100644
index 000000000..0a7af7f03
--- /dev/null
+++ b/src/iOS.Autofill/LoginSearchViewController.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Linq;
+using Bit.iOS.Autofill.Models;
+using Foundation;
+using UIKit;
+using Bit.iOS.Core.Utilities;
+using Bit.iOS.Core.Controllers;
+using Bit.App.Resources;
+using Bit.iOS.Core.Views;
+
+namespace Bit.iOS.Autofill
+{
+ public partial class LoginSearchViewController : ExtendedUITableViewController
+ {
+ public LoginSearchViewController(IntPtr handle) : base(handle)
+ { }
+
+ public Context Context { get; set; }
+ public CredentialProviderViewController CPViewController { get; set; }
+
+ public override void ViewWillAppear(bool animated)
+ {
+ UINavigationBar.Appearance.ShadowImage = new UIImage();
+ UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
+ base.ViewWillAppear(animated);
+ }
+
+ public async override void ViewDidLoad()
+ {
+ base.ViewDidLoad();
+ NavItem.Title = AppResources.SearchVault;
+ CancelBarButton.Title = AppResources.Cancel;
+
+ TableView.RowHeight = UITableView.AutomaticDimension;
+ TableView.EstimatedRowHeight = 44;
+ TableView.Source = new TableSource(this);
+ await ((TableSource)TableView.Source).LoadItemsAsync(false, SearchBar.Text);
+ }
+
+ partial void CancelBarButton_Activated(UIBarButtonItem sender)
+ {
+ CPViewController.CompleteRequest();
+ }
+
+ partial void AddBarButton_Activated(UIBarButtonItem sender)
+ {
+ PerformSegue("loginAddFromSearchSegue", this);
+ }
+
+ public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
+ {
+ var navController = segue.DestinationViewController as UINavigationController;
+ if(navController != null)
+ {
+ var addLoginController = navController.TopViewController as LoginAddViewController;
+ if(addLoginController != null)
+ {
+ addLoginController.Context = Context;
+ addLoginController.LoginSearchController = this;
+ }
+ }
+ }
+
+ public void DismissModal()
+ {
+ DismissViewController(true, async () =>
+ {
+ await ((TableSource)TableView.Source).LoadItemsAsync(false, SearchBar.Text);
+ TableView.ReloadData();
+ });
+ }
+
+ public class TableSource : ExtensionTableSource
+ {
+ private Context _context;
+ private LoginSearchViewController _controller;
+
+ public TableSource(LoginSearchViewController controller)
+ :base(controller.Context, controller)
+ {
+ _context = controller.Context;
+ _controller = controller;
+ }
+
+ public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
+ {
+ tableView.DeselectRow(indexPath, true);
+ tableView.EndEditing(true);
+
+ if(_tableItems == null || _tableItems.Count() == 0)
+ {
+ _controller.PerformSegue("loginAddFromSearchSegue", this);
+ return;
+ }
+
+ var item = _tableItems.ElementAt(indexPath.Row);
+ if(item == null)
+ {
+ _controller.CPViewController.CompleteRequest(null, null, null);
+ return;
+ }
+
+ if(!string.IsNullOrWhiteSpace(item.Password))
+ {
+ string totp = null;
+ if(!_settings.GetValueOrDefault(App.Constants.SettingDisableTotpCopy, false))
+ {
+ totp = GetTotp(item);
+ }
+
+ _controller.CPViewController.CompleteRequest(item.Username, item.Password, totp);
+ }
+ else if(!string.IsNullOrWhiteSpace(item.Username) || !string.IsNullOrWhiteSpace(item.Totp.Value))
+ {
+ var sheet = Dialogs.CreateActionSheet(item.Name, _controller);
+ if(!string.IsNullOrWhiteSpace(item.Username))
+ {
+ sheet.AddAction(UIAlertAction.Create(AppResources.CopyUsername, UIAlertActionStyle.Default, a =>
+ {
+ UIPasteboard clipboard = UIPasteboard.General;
+ clipboard.String = item.Username;
+ var alert = Dialogs.CreateMessageAlert(AppResources.CopyUsername);
+ _controller.PresentViewController(alert, true, () =>
+ {
+ _controller.DismissViewController(true, null);
+ });
+ }));
+ }
+
+ if(!string.IsNullOrWhiteSpace(item.Totp.Value))
+ {
+ sheet.AddAction(UIAlertAction.Create(AppResources.CopyTotp, UIAlertActionStyle.Default, a =>
+ {
+ var totp = GetTotp(item);
+ if(string.IsNullOrWhiteSpace(totp))
+ {
+ return;
+ }
+
+ UIPasteboard clipboard = UIPasteboard.General;
+ clipboard.String = totp;
+ var alert = Dialogs.CreateMessageAlert(AppResources.CopiedTotp);
+ _controller.PresentViewController(alert, true, () =>
+ {
+ _controller.DismissViewController(true, null);
+ });
+ }));
+ }
+
+ sheet.AddAction(UIAlertAction.Create(AppResources.Cancel, UIAlertActionStyle.Cancel, null));
+ _controller.PresentViewController(sheet, true, null);
+ }
+ else
+ {
+ var alert = Dialogs.CreateAlert(null, AppResources.NoUsernamePasswordConfigured, AppResources.Ok);
+ _controller.PresentViewController(alert, true, null);
+ }
+ }
+ }
+ }
+}
diff --git a/src/iOS.Autofill/LoginSearchViewController.designer.cs b/src/iOS.Autofill/LoginSearchViewController.designer.cs
new file mode 100644
index 000000000..9994c09f9
--- /dev/null
+++ b/src/iOS.Autofill/LoginSearchViewController.designer.cs
@@ -0,0 +1,64 @@
+// WARNING
+//
+// This file has been generated automatically by Visual Studio from the outlets and
+// actions declared in your storyboard file.
+// Manual changes to this file will not be maintained.
+//
+using Foundation;
+using System;
+using System.CodeDom.Compiler;
+using UIKit;
+
+namespace Bit.iOS.Autofill
+{
+ [Register ("LoginSearchViewController")]
+ partial class LoginSearchViewController
+ {
+ [Outlet]
+ [GeneratedCode ("iOS Designer", "1.0")]
+ UIKit.UIBarButtonItem CancelBarButton { get; set; }
+
+ [Outlet]
+ [GeneratedCode ("iOS Designer", "1.0")]
+ UIKit.UINavigationItem NavItem { get; set; }
+
+ [Outlet]
+ [GeneratedCode ("iOS Designer", "1.0")]
+ UIKit.UISearchBar SearchBar { get; set; }
+
+ [Outlet]
+ [GeneratedCode ("iOS Designer", "1.0")]
+ UIKit.UISearchDisplayController searchDisplayController { get; set; }
+
+ [Action ("AddBarButton_Activated:")]
+ [GeneratedCode ("iOS Designer", "1.0")]
+ partial void AddBarButton_Activated (UIKit.UIBarButtonItem sender);
+
+ [Action ("CancelBarButton_Activated:")]
+ [GeneratedCode ("iOS Designer", "1.0")]
+ partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
+
+ void ReleaseDesignerOutlets ()
+ {
+ if (CancelBarButton != null) {
+ CancelBarButton.Dispose ();
+ CancelBarButton = null;
+ }
+
+ if (NavItem != null) {
+ NavItem.Dispose ();
+ NavItem = null;
+ }
+
+ if (SearchBar != null) {
+ SearchBar.Dispose ();
+ SearchBar = null;
+ }
+
+ if (searchDisplayController != null) {
+ searchDisplayController.Dispose ();
+ searchDisplayController = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/iOS.Autofill/MainInterface.storyboard b/src/iOS.Autofill/MainInterface.storyboard
index e0c5819a5..3975af572 100644
--- a/src/iOS.Autofill/MainInterface.storyboard
+++ b/src/iOS.Autofill/MainInterface.storyboard
@@ -32,8 +32,8 @@
-
+
@@ -171,12 +171,20 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -184,6 +192,7 @@
+
@@ -548,7 +557,7 @@
-
+
@@ -604,9 +613,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/iOS.Autofill/iOS.Autofill.csproj b/src/iOS.Autofill/iOS.Autofill.csproj
index d53f88de8..e508cc3f6 100644
--- a/src/iOS.Autofill/iOS.Autofill.csproj
+++ b/src/iOS.Autofill/iOS.Autofill.csproj
@@ -241,6 +241,10 @@
PasswordGeneratorViewController.cs
+
+
+ LoginSearchViewController.cs
+
diff --git a/src/iOS.Core/Views/ExtensionTableSource.cs b/src/iOS.Core/Views/ExtensionTableSource.cs
index 13ab6da6b..84fc7d016 100644
--- a/src/iOS.Core/Views/ExtensionTableSource.cs
+++ b/src/iOS.Core/Views/ExtensionTableSource.cs
@@ -35,18 +35,26 @@ namespace Bit.iOS.Core.Views
_controller = controller;
}
- public async Task LoadItemsAsync()
+ public async Task LoadItemsAsync(bool urlFilter = true, string searchFilter = null)
{
var combinedLogins = new List();
- var logins = await _cipherService.GetAllAsync(_context.UrlString);
- if(logins?.Item1 != null)
+ if (urlFilter)
{
- combinedLogins.AddRange(logins.Item1);
+ var logins = await _cipherService.GetAllAsync(_context.UrlString);
+ if (logins?.Item1 != null)
+ {
+ combinedLogins.AddRange(logins.Item1);
+ }
+ if (logins?.Item2 != null)
+ {
+ combinedLogins.AddRange(logins.Item2);
+ }
}
- if(logins?.Item2 != null)
+ else
{
- combinedLogins.AddRange(logins.Item2);
+ var logins = await _cipherService.GetAllAsync();
+ combinedLogins.AddRange(logins);
}
_tableItems = combinedLogins.Select(s => new CipherViewModel(s))