mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 23:25:45 +03:00
Search controller for autofill setup
This commit is contained in:
parent
e9ec02929d
commit
42fc868b68
8 changed files with 359 additions and 15 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
161
src/iOS.Autofill/LoginSearchViewController.cs
Normal file
161
src/iOS.Autofill/LoginSearchViewController.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
src/iOS.Autofill/LoginSearchViewController.designer.cs
generated
Normal file
64
src/iOS.Autofill/LoginSearchViewController.designer.cs
generated
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,8 +32,8 @@
|
|||
<segue destination="6512" kind="presentation" identifier="lockFingerprintSegue" id="8446"/>
|
||||
<segue destination="6815" kind="presentation" identifier="lockPinSegue" id="8924"/>
|
||||
<segue destination="6855" kind="presentation" identifier="lockPasswordSegue" id="9874"/>
|
||||
<segue destination="1845" kind="presentation" identifier="newLoginSegue" modalPresentationStyle="fullScreen" modalTransitionStyle="coverVertical" id="10498"/>
|
||||
<segue id="11089" destination="10580" kind="presentation" modalTransitionStyle="coverVertical" identifier="setupSegue"/>
|
||||
<segue id="12959" destination="11552" kind="show" identifier="loginSearchSegue"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="45" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
@ -171,12 +171,20 @@
|
|||
<action selector="CancelBarButton_Activated:" destination="2304" id="3750"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" systemItem="add" id="3736">
|
||||
<rightBarButtonItems>
|
||||
<barButtonItem systemItem="add" id="3736">
|
||||
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<action selector="AddBarButton_Activated:" destination="2304" id="3749"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem id="13195" systemItem="search">
|
||||
<color key="tintColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
|
||||
<connections>
|
||||
<action selector="SearchBarButton_Activated:" destination="2304" id="13400"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</rightBarButtonItems>
|
||||
</navigationItem>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
|
@ -184,6 +192,7 @@
|
|||
<outlet property="CancelBarButton" destination="3735" id="name-outlet-3735"/>
|
||||
<outlet property="NavItem" destination="3734" id="name-outlet-3734"/>
|
||||
<segue destination="1845" kind="presentation" identifier="loginAddSegue" modalPresentationStyle="fullScreen" modalTransitionStyle="coverVertical" id="3731"/>
|
||||
<segue id="12574" destination="11552" kind="show" identifier="loginSearchFromListSegue"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="2310" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
@ -548,7 +557,7 @@
|
|||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="11094" translatesAutoresizingMaskIntoConstraints="NO" image="check.png" misplaced="YES" ambiguous="YES">
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="11094" translatesAutoresizingMaskIntoConstraints="NO" image="check.png" misplaced="YES">
|
||||
<rect key="frame" x="255" y="205.5" width="90" height="90"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
|
@ -604,9 +613,90 @@
|
|||
</objects>
|
||||
<point key="canvasLocation" x="362" y="-267"/>
|
||||
</scene>
|
||||
<scene sceneID="11542">
|
||||
<objects>
|
||||
<tableViewController id="11543" sceneMemberID="viewController" customClass="LoginSearchViewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="11545">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="11548">
|
||||
<rect key="frame" x="0.0" y="72" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="11548" id="11549">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="11543" id="11546"/>
|
||||
<outlet property="delegate" destination="11543" id="11547"/>
|
||||
</connections>
|
||||
<searchBar contentMode="redraw" id="13084" key="tableHeaderView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="11543" id="13085"/>
|
||||
</connections>
|
||||
</searchBar>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Search Logins" id="11544">
|
||||
<barButtonItem key="leftBarButtonItem" id="11950" title="Cancel">
|
||||
<color key="tintColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
|
||||
<connections>
|
||||
<action selector="CancelBarButton_Activated:" destination="11543" id="12044"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" id="11951" systemItem="add">
|
||||
<color key="tintColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
|
||||
<connections>
|
||||
<action selector="AddBarButton_Activated:" destination="11543" id="12045"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="CancelBarButton" destination="11950" id="name-outlet-11950"/>
|
||||
<outlet property="NavItem" destination="11544" id="name-outlet-11544"/>
|
||||
<segue id="12738" destination="1845" kind="show" identifier="loginAddFromSearchSegue"/>
|
||||
<outlet property="searchDisplayController" destination="13087" id="13086"/>
|
||||
<outlet property="SearchBar" destination="13084" id="name-outlet-13084"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="11550" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<searchDisplayController id="13087">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="11543" id="13088"/>
|
||||
<outlet property="searchBar" destination="13084" id="13089"/>
|
||||
<outlet property="searchContentsController" destination="11543" id="13090"/>
|
||||
<outlet property="searchResultsDataSource" destination="11543" id="13091"/>
|
||||
<outlet property="searchResultsDelegate" destination="11543" id="13092"/>
|
||||
</connections>
|
||||
</searchDisplayController>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2513" y="907"/>
|
||||
</scene>
|
||||
<scene sceneID="11551">
|
||||
<objects>
|
||||
<navigationController id="11552" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="11554">
|
||||
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="11543" kind="relationship" relationship="rootViewController" id="11553"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="11555" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1920" y="908"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="fingerprint.png" width="91" height="92"/>
|
||||
<image name="logo.png" width="282" height="44"/>
|
||||
<image name="check.png" width="90" height="90"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -241,6 +241,10 @@
|
|||
<Compile Include="PasswordGeneratorViewController.designer.cs">
|
||||
<DependentUpon>PasswordGeneratorViewController.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="LoginSearchViewController.cs" />
|
||||
<Compile Include="LoginSearchViewController.designer.cs">
|
||||
<DependentUpon>LoginSearchViewController.cs</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
|
|
|
@ -35,19 +35,27 @@ 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<Cipher>();
|
||||
|
||||
if (urlFilter)
|
||||
{
|
||||
var logins = await _cipherService.GetAllAsync(_context.UrlString);
|
||||
if(logins?.Item1 != null)
|
||||
if (logins?.Item1 != null)
|
||||
{
|
||||
combinedLogins.AddRange(logins.Item1);
|
||||
}
|
||||
if(logins?.Item2 != null)
|
||||
if (logins?.Item2 != null)
|
||||
{
|
||||
combinedLogins.AddRange(logins.Item2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var logins = await _cipherService.GetAllAsync();
|
||||
combinedLogins.AddRange(logins);
|
||||
}
|
||||
|
||||
_tableItems = combinedLogins.Select(s => new CipherViewModel(s))
|
||||
.OrderBy(s => s.Name)
|
||||
|
|
Loading…
Reference in a new issue