From 42fc868b68d71ad6daafd5274554f5af04c4fd7d Mon Sep 17 00:00:00 2001 From: kspearrin Date: Mon, 24 Sep 2018 12:41:08 -0400 Subject: [PATCH] Search controller for autofill setup --- src/iOS.Autofill/LoginAddViewController.cs | 2 + src/iOS.Autofill/LoginListViewController.cs | 13 +- .../LoginListViewController.designer.cs | 4 + src/iOS.Autofill/LoginSearchViewController.cs | 161 ++++++++++++++++++ .../LoginSearchViewController.designer.cs | 64 +++++++ src/iOS.Autofill/MainInterface.storyboard | 106 +++++++++++- src/iOS.Autofill/iOS.Autofill.csproj | 4 + src/iOS.Core/Views/ExtensionTableSource.cs | 20 ++- 8 files changed, 359 insertions(+), 15 deletions(-) create mode 100644 src/iOS.Autofill/LoginSearchViewController.cs create mode 100644 src/iOS.Autofill/LoginSearchViewController.designer.cs 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))