2018-09-24 18:41:08 +02:00
|
|
|
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;
|
2018-09-24 21:08:16 +02:00
|
|
|
using System.Threading;
|
2018-09-24 21:30:05 +02:00
|
|
|
using System.Threading.Tasks;
|
2018-09-24 18:41:08 +02:00
|
|
|
|
|
|
|
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);
|
2018-09-24 20:51:37 +02:00
|
|
|
SearchBar.Delegate = new SearchDelegate(this);
|
2018-09-24 18:41:08 +02:00
|
|
|
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)
|
2018-09-24 20:51:37 +02:00
|
|
|
: base(controller.Context, controller)
|
2018-09-24 18:41:08 +02:00
|
|
|
{
|
|
|
|
_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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-24 20:51:37 +02:00
|
|
|
|
2018-09-24 21:08:16 +02:00
|
|
|
public class SearchDelegate : UISearchBarDelegate
|
2018-09-24 20:51:37 +02:00
|
|
|
{
|
2018-09-24 21:08:16 +02:00
|
|
|
private readonly LoginSearchViewController _controller;
|
2018-09-24 21:30:05 +02:00
|
|
|
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
2018-09-24 20:51:37 +02:00
|
|
|
|
2018-09-24 21:08:16 +02:00
|
|
|
public SearchDelegate(LoginSearchViewController controller)
|
|
|
|
{
|
|
|
|
_controller = controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void TextChanged(UISearchBar searchBar, string searchText)
|
|
|
|
{
|
2018-09-24 21:30:05 +02:00
|
|
|
var cts = new CancellationTokenSource();
|
2018-09-24 21:45:36 +02:00
|
|
|
Task.Run(() =>
|
2018-09-24 21:30:05 +02:00
|
|
|
{
|
2018-09-24 21:45:36 +02:00
|
|
|
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
2018-09-24 21:30:05 +02:00
|
|
|
{
|
2018-09-24 21:45:36 +02:00
|
|
|
if (!string.IsNullOrWhiteSpace(searchText))
|
2018-09-24 21:30:05 +02:00
|
|
|
{
|
2018-09-24 21:45:36 +02:00
|
|
|
await Task.Delay(300);
|
|
|
|
if (searchText != searchBar.Text)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_filterResultsCancellationTokenSource?.Cancel();
|
|
|
|
}
|
2018-09-24 21:30:05 +02:00
|
|
|
}
|
2018-09-24 21:45:36 +02:00
|
|
|
try
|
2018-09-24 21:30:05 +02:00
|
|
|
{
|
2018-09-24 21:45:36 +02:00
|
|
|
((TableSource)_controller.TableView.Source).FilterResults(searchText, cts.Token);
|
|
|
|
_controller.TableView.ReloadData();
|
2018-09-24 21:30:05 +02:00
|
|
|
}
|
2018-09-24 21:45:36 +02:00
|
|
|
catch (OperationCanceledException) { }
|
|
|
|
_filterResultsCancellationTokenSource = cts;
|
|
|
|
});
|
2018-09-24 21:30:05 +02:00
|
|
|
}, cts.Token);
|
2018-09-24 21:08:16 +02:00
|
|
|
}
|
2018-09-24 20:51:37 +02:00
|
|
|
}
|
|
|
|
}
|
2018-09-24 18:41:08 +02:00
|
|
|
}
|