diff --git a/src/iOS.Core/Views/FormEntryTableViewCell.cs b/src/iOS.Core/Views/FormEntryTableViewCell.cs index 44c3e6105..9088e7e0a 100644 --- a/src/iOS.Core/Views/FormEntryTableViewCell.cs +++ b/src/iOS.Core/Views/FormEntryTableViewCell.cs @@ -8,13 +8,14 @@ namespace Bit.iOS.Core.Views public FormEntryTableViewCell( string labelName = null, bool useTextView = false, - nfloat? height = null) + nfloat? height = null, + bool useLabelAsPlaceholder = false) : base(UITableViewCellStyle.Default, nameof(FormEntryTableViewCell)) { var descriptor = UIFontDescriptor.PreferredBody; var pointSize = descriptor.PointSize; - if(labelName != null) + if(labelName != null && !useLabelAsPlaceholder) { Label = new UILabel { @@ -42,7 +43,7 @@ namespace Bit.iOS.Core.Views NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextView, NSLayoutAttribute.Bottom, 1f, 10f) }); - if(labelName != null) + if(labelName != null && !useLabelAsPlaceholder) { ContentView.AddConstraint( NSLayoutConstraint.Create(TextView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, Label, NSLayoutAttribute.Bottom, 1f, 10f)); @@ -69,6 +70,11 @@ namespace Bit.iOS.Core.Views ClearButtonMode = UITextFieldViewMode.WhileEditing }; + if(useLabelAsPlaceholder) + { + TextField.Placeholder = labelName; + } + ContentView.Add(TextField); ContentView.AddConstraints(new NSLayoutConstraint[] { 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) }); - if(labelName != null) + if(labelName != null && !useLabelAsPlaceholder) { ContentView.AddConstraint( 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[] { NSLayoutConstraint.Create(Label, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1f, 15f), diff --git a/src/iOS.Extension/LockPasswordViewController.cs b/src/iOS.Extension/LockPasswordViewController.cs index 0d0062099..1369738ee 100644 --- a/src/iOS.Extension/LockPasswordViewController.cs +++ b/src/iOS.Extension/LockPasswordViewController.cs @@ -5,17 +5,27 @@ using XLabs.Ioc; using Plugin.Settings.Abstractions; using Foundation; 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 { - public partial class LockPasswordViewController : UIViewController + public partial class LockPasswordViewController : UITableViewController { private ISettings _settings; + private IAuthService _authService; + private ICryptoService _cryptoService; public LockPasswordViewController(IntPtr handle) : base(handle) { } public Context Context { get; set; } + public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell( + AppResources.MasterPassword, useLabelAsPlaceholder: true); public override void ViewWillAppear(bool animated) { @@ -27,14 +37,72 @@ namespace Bit.iOS.Extension public override void ViewDidLoad() { _settings = Resolver.Resolve(); + _authService = Resolver.Resolve(); + _cryptoService = Resolver.Resolve(); View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); 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(); } + 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) { CompleteRequest(); @@ -48,5 +116,76 @@ namespace Bit.iOS.Extension 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(); + } + } + } } } diff --git a/src/iOS.Extension/LockPasswordViewController.designer.cs b/src/iOS.Extension/LockPasswordViewController.designer.cs index 92b13c961..7e4c6a0c8 100644 --- a/src/iOS.Extension/LockPasswordViewController.designer.cs +++ b/src/iOS.Extension/LockPasswordViewController.designer.cs @@ -18,16 +18,38 @@ namespace Bit.iOS.Extension [GeneratedCode ("iOS Designer", "1.0")] 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:")] [GeneratedCode ("iOS Designer", "1.0")] partial void CancelButton_Activated (UIKit.UIBarButtonItem sender); + [Action ("SubmitButton_Activated:")] + [GeneratedCode ("iOS Designer", "1.0")] + partial void SubmitButton_Activated (UIKit.UIBarButtonItem sender); + void ReleaseDesignerOutlets () { if (CancelButton != null) { CancelButton.Dispose (); CancelButton = null; } + + if (MainTableView != null) { + MainTableView.Dispose (); + MainTableView = null; + } + + if (SubmitButton != null) { + SubmitButton.Dispose (); + SubmitButton = null; + } } } } \ No newline at end of file diff --git a/src/iOS.Extension/MainInterface.storyboard b/src/iOS.Extension/MainInterface.storyboard index 088e0d849..198591e0f 100644 --- a/src/iOS.Extension/MainInterface.storyboard +++ b/src/iOS.Extension/MainInterface.storyboard @@ -424,34 +424,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -460,13 +432,47 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +