password lock VC for extension

This commit is contained in:
Kyle Spearrin 2016-07-21 18:28:05 -04:00
parent c2cb5ac7c9
commit 047f5b100f
4 changed files with 208 additions and 35 deletions

View File

@ -8,13 +8,14 @@ namespace Bit.iOS.Core.Views
public FormEntryTableViewCell( public FormEntryTableViewCell(
string labelName = null, string labelName = null,
bool useTextView = false, bool useTextView = false,
nfloat? height = null) nfloat? height = null,
bool useLabelAsPlaceholder = false)
: base(UITableViewCellStyle.Default, nameof(FormEntryTableViewCell)) : base(UITableViewCellStyle.Default, nameof(FormEntryTableViewCell))
{ {
var descriptor = UIFontDescriptor.PreferredBody; var descriptor = UIFontDescriptor.PreferredBody;
var pointSize = descriptor.PointSize; var pointSize = descriptor.PointSize;
if(labelName != null) if(labelName != null && !useLabelAsPlaceholder)
{ {
Label = new UILabel Label = new UILabel
{ {
@ -42,7 +43,7 @@ namespace Bit.iOS.Core.Views
NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextView, NSLayoutAttribute.Bottom, 1f, 10f) NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextView, NSLayoutAttribute.Bottom, 1f, 10f)
}); });
if(labelName != null) if(labelName != null && !useLabelAsPlaceholder)
{ {
ContentView.AddConstraint( ContentView.AddConstraint(
NSLayoutConstraint.Create(TextView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, Label, NSLayoutAttribute.Bottom, 1f, 10f)); NSLayoutConstraint.Create(TextView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, Label, NSLayoutAttribute.Bottom, 1f, 10f));
@ -69,6 +70,11 @@ namespace Bit.iOS.Core.Views
ClearButtonMode = UITextFieldViewMode.WhileEditing ClearButtonMode = UITextFieldViewMode.WhileEditing
}; };
if(useLabelAsPlaceholder)
{
TextField.Placeholder = labelName;
}
ContentView.Add(TextField); ContentView.Add(TextField);
ContentView.AddConstraints(new NSLayoutConstraint[] { ContentView.AddConstraints(new NSLayoutConstraint[] {
NSLayoutConstraint.Create(TextField, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1f, 15f), NSLayoutConstraint.Create(TextField, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1f, 15f),
@ -76,7 +82,7 @@ namespace Bit.iOS.Core.Views
NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextField, NSLayoutAttribute.Bottom, 1f, 10f) NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextField, NSLayoutAttribute.Bottom, 1f, 10f)
}); });
if(labelName != null) if(labelName != null && !useLabelAsPlaceholder)
{ {
ContentView.AddConstraint( ContentView.AddConstraint(
NSLayoutConstraint.Create(TextField, NSLayoutAttribute.Top, NSLayoutRelation.Equal, Label, NSLayoutAttribute.Bottom, 1f, 10f)); NSLayoutConstraint.Create(TextField, NSLayoutAttribute.Top, NSLayoutRelation.Equal, Label, NSLayoutAttribute.Bottom, 1f, 10f));
@ -94,7 +100,7 @@ namespace Bit.iOS.Core.Views
} }
} }
if(labelName != null) if(labelName != null && !useLabelAsPlaceholder)
{ {
ContentView.AddConstraints(new NSLayoutConstraint[] { ContentView.AddConstraints(new NSLayoutConstraint[] {
NSLayoutConstraint.Create(Label, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1f, 15f), NSLayoutConstraint.Create(Label, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1f, 15f),

View File

@ -5,17 +5,27 @@ using XLabs.Ioc;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
using Foundation; using Foundation;
using MobileCoreServices; using MobileCoreServices;
using Bit.iOS.Core.Views;
using Bit.App.Resources;
using System.Threading.Tasks;
using Bit.iOS.Core.Utilities;
using Bit.App.Abstractions;
using System.Linq;
namespace Bit.iOS.Extension namespace Bit.iOS.Extension
{ {
public partial class LockPasswordViewController : UIViewController public partial class LockPasswordViewController : UITableViewController
{ {
private ISettings _settings; private ISettings _settings;
private IAuthService _authService;
private ICryptoService _cryptoService;
public LockPasswordViewController(IntPtr handle) : base(handle) public LockPasswordViewController(IntPtr handle) : base(handle)
{ } { }
public Context Context { get; set; } public Context Context { get; set; }
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
AppResources.MasterPassword, useLabelAsPlaceholder: true);
public override void ViewWillAppear(bool animated) public override void ViewWillAppear(bool animated)
{ {
@ -27,14 +37,72 @@ namespace Bit.iOS.Extension
public override void ViewDidLoad() public override void ViewDidLoad()
{ {
_settings = Resolver.Resolve<ISettings>(); _settings = Resolver.Resolve<ISettings>();
_authService = Resolver.Resolve<IAuthService>();
_cryptoService = Resolver.Resolve<ICryptoService>();
View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f);
var descriptor = UIFontDescriptor.PreferredBody; var descriptor = UIFontDescriptor.PreferredBody;
MasterPasswordCell.TextField.SecureTextEntry = true;
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
{
CheckPassword();
return true;
};
TableView.RowHeight = UITableView.AutomaticDimension;
TableView.EstimatedRowHeight = 70;
TableView.Source = new TableSource(this);
TableView.AllowsSelection = true;
base.ViewDidLoad(); base.ViewDidLoad();
} }
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
MasterPasswordCell.TextField.BecomeFirstResponder();
}
partial void SubmitButton_Activated(UIBarButtonItem sender)
{
CheckPassword();
}
private void CheckPassword()
{
if(string.IsNullOrWhiteSpace(MasterPasswordCell.TextField.Text))
{
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword), AppResources.Ok);
PresentViewController(alert, true, null);
return;
}
var key = _cryptoService.MakeKeyFromPassword(MasterPasswordCell.TextField.Text, _authService.Email);
if(key.SequenceEqual(_cryptoService.Key))
{
MasterPasswordCell.TextField.ResignFirstResponder();
DismissModalViewController(true);
}
else
{
// TODO: keep track of invalid attempts and logout?
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
string.Format(null, "Invalid Master Password. Try again."), AppResources.Ok, (a) =>
{
MasterPasswordCell.TextField.Text = string.Empty;
MasterPasswordCell.TextField.BecomeFirstResponder();
});
PresentViewController(alert, true, null);
}
}
partial void CancelButton_Activated(UIBarButtonItem sender) partial void CancelButton_Activated(UIBarButtonItem sender)
{ {
CompleteRequest(); CompleteRequest();
@ -48,5 +116,76 @@ namespace Bit.iOS.Extension
Context.ExtContext.CompleteRequest(returningItems, null); Context.ExtContext.CompleteRequest(returningItems, null);
} }
public class TableSource : UITableViewSource
{
private LockPasswordViewController _controller;
public TableSource(LockPasswordViewController controller)
{
_controller = controller;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
if(indexPath.Section == 0)
{
if(indexPath.Row == 0)
{
return _controller.MasterPasswordCell;
}
}
return new UITableViewCell();
}
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
return UITableView.AutomaticDimension;
}
public override nint NumberOfSections(UITableView tableView)
{
return 1;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
if(section == 0)
{
return 1;
}
return 0;
}
public override nfloat GetHeightForHeader(UITableView tableView, nint section)
{
return UITableView.AutomaticDimension;
}
public override string TitleForHeader(UITableView tableView, nint section)
{
return null;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow(indexPath, true);
tableView.EndEditing(true);
var cell = tableView.CellAt(indexPath);
if(cell == null)
{
return;
}
var selectableCell = cell as ISelectable;
if(selectableCell != null)
{
selectableCell.Select();
}
}
}
} }
} }

View File

@ -18,16 +18,38 @@ namespace Bit.iOS.Extension
[GeneratedCode ("iOS Designer", "1.0")] [GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem CancelButton { get; set; } UIKit.UIBarButtonItem CancelButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UITableView MainTableView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem SubmitButton { get; set; }
[Action ("CancelButton_Activated:")] [Action ("CancelButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")] [GeneratedCode ("iOS Designer", "1.0")]
partial void CancelButton_Activated (UIKit.UIBarButtonItem sender); partial void CancelButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("SubmitButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void SubmitButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets () void ReleaseDesignerOutlets ()
{ {
if (CancelButton != null) { if (CancelButton != null) {
CancelButton.Dispose (); CancelButton.Dispose ();
CancelButton = null; CancelButton = null;
} }
if (MainTableView != null) {
MainTableView.Dispose ();
MainTableView = null;
}
if (SubmitButton != null) {
SubmitButton.Dispose ();
SubmitButton = null;
}
} }
} }
} }

View File

@ -424,34 +424,6 @@
</objects> </objects>
<point key="canvasLocation" x="1886" y="1931"/> <point key="canvasLocation" x="1886" y="1931"/>
</scene> </scene>
<scene sceneID="6841">
<objects>
<viewController id="6842" sceneMemberID="viewController" customClass="LockPasswordViewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="6852"/>
<viewControllerLayoutGuide type="bottom" id="6850"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="6845">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<subviews/>
</view>
<navigationItem key="navigationItem" title="Verify Master Password" id="6843">
<barButtonItem key="leftBarButtonItem" id="6844" title="Cancel">
<connections>
<action selector="CancelButton_Activated:" destination="6842" id="7331"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="CancelButton" destination="6844" id="name-outlet-6844"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6853" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2653" y="2664"/>
</scene>
<scene sceneID="6854"> <scene sceneID="6854">
<objects> <objects>
<navigationController definesPresentationContext="YES" id="6855" sceneMemberID="viewController"> <navigationController definesPresentationContext="YES" id="6855" sceneMemberID="viewController">
@ -460,13 +432,47 @@
<rect key="frame" x="0.0" y="20" width="600" height="44"/> <rect key="frame" x="0.0" y="20" width="600" height="44"/>
</navigationBar> </navigationBar>
<connections> <connections>
<segue destination="6842" kind="relationship" relationship="rootViewController" id="6856"/> <segue id="8266" destination="7413" kind="relationship" relationship="rootViewController"/>
</connections> </connections>
</navigationController> </navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6858" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="6858" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="1878" y="2669"/> <point key="canvasLocation" x="1878" y="2669"/>
</scene> </scene>
<scene sceneID="7412">
<objects>
<tableViewController id="7413" sceneMemberID="viewController" customClass="LockPasswordViewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="7414">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<outlet property="dataSource" destination="7413" id="7415"/>
<outlet property="delegate" destination="7413" id="7416"/>
</connections>
</tableView>
<navigationItem title="Verify Master Password" id="8265" key="navigationItem">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="8268">
<connections>
<action selector="CancelButton_Activated:" destination="7413" id="8287"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="Submit" id="8269">
<connections>
<action selector="SubmitButton_Activated:" destination="7413" id="8288"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="SubmitButton" destination="8269" id="name-outlet-8269"/>
<outlet property="CancelButton" destination="8268" id="name-outlet-8268"/>
<outlet property="MainTableView" destination="7414" id="name-outlet-7414"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7419" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2532" y="2675"/>
</scene>
</scenes> </scenes>
<resources> <resources>
<image name="logo.png" width="282" height="44"/> <image name="logo.png" width="282" height="44"/>