Add passphrase generator to iOS Extensions (#1230)

* Add passphrase generator options to iOS extension

* Set custom indentation on WordSeparator control

* Set correct RowsInSection for passphrase controls

* Fix RowsInSection for password controls

* Add avoid ambiguous characters control
This commit is contained in:
Thomas Rittson 2021-01-26 06:23:50 +10:00 committed by GitHub
parent 10677f3705
commit 9b6bf136f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 133 additions and 27 deletions

View file

@ -10,18 +10,23 @@ using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic;
namespace Bit.iOS.Core.Controllers namespace Bit.iOS.Core.Controllers
{ {
public abstract class PasswordGeneratorViewController : ExtendedUIViewController public abstract class PasswordGeneratorViewController : ExtendedUIViewController
{ {
private IPasswordGenerationService _passwordGenerationService; private IPasswordGenerationService _passwordGenerationService;
private string _passType;
public PasswordGeneratorViewController(IntPtr handle) public PasswordGeneratorViewController(IntPtr handle)
: base(handle) : base(handle)
{ } { }
public UITableViewController OptionsTableViewController { get; set; } public UITableViewController OptionsTableViewController { get; set; }
public PickerTableViewCell TypePickerCell { get; set; } = new PickerTableViewCell(
AppResources.Type);
public SwitchTableViewCell UppercaseCell { get; set; } = new SwitchTableViewCell("A-Z"); public SwitchTableViewCell UppercaseCell { get; set; } = new SwitchTableViewCell("A-Z");
public SwitchTableViewCell LowercaseCell { get; set; } = new SwitchTableViewCell("a-z"); public SwitchTableViewCell LowercaseCell { get; set; } = new SwitchTableViewCell("a-z");
public SwitchTableViewCell NumbersCell { get; set; } = new SwitchTableViewCell("0-9"); public SwitchTableViewCell NumbersCell { get; set; } = new SwitchTableViewCell("0-9");
@ -32,7 +37,20 @@ namespace Bit.iOS.Core.Controllers
AppResources.MinSpecial, 1, 0, 5, 1); AppResources.MinSpecial, 1, 0, 5, 1);
public SliderTableViewCell LengthCell { get; set; } = new SliderTableViewCell( public SliderTableViewCell LengthCell { get; set; } = new SliderTableViewCell(
AppResources.Length, 10, 5, 64); AppResources.Length, 10, 5, 64);
public SwitchTableViewCell AmbiguousCell { get; set; } = new SwitchTableViewCell(
AppResources.AvoidAmbiguousCharacters);
public StepperTableViewCell NumWordsCell { get; set; } = new StepperTableViewCell(
AppResources.NumberOfWords, 3, 3, 20, 1);
public FormEntryTableViewCell WordSeparatorCell { get; set; } = new FormEntryTableViewCell(
AppResources.WordSeparator, leadingConstant: 20f);
public SwitchTableViewCell CapitalizeCell { get; set; } = new SwitchTableViewCell(
AppResources.Capitalize);
public SwitchTableViewCell IncludeNumberCell { get; set; } = new SwitchTableViewCell(
AppResources.IncludeNumber);
public List<string> TypeOptions { get; set; } = new List<string> {
AppResources.Password, AppResources.Passphrase };
public PasswordGenerationOptions PasswordOptions { get; set; } public PasswordGenerationOptions PasswordOptions { get; set; }
public abstract UINavigationItem BaseNavItem { get; } public abstract UINavigationItem BaseNavItem { get; }
public abstract UIBarButtonItem BaseCancelButton { get; } public abstract UIBarButtonItem BaseCancelButton { get; }
@ -51,7 +69,7 @@ namespace Bit.iOS.Core.Controllers
var descriptor = UIFontDescriptor.PreferredBody; var descriptor = UIFontDescriptor.PreferredBody;
BasePasswordLabel.Font = UIFont.FromName("Menlo-Regular", descriptor.PointSize * 1.3f); BasePasswordLabel.Font = UIFont.FromName("Menlo-Regular", descriptor.PointSize * 1.3f);
BasePasswordLabel.LineBreakMode = UILineBreakMode.TailTruncation; BasePasswordLabel.LineBreakMode = UILineBreakMode.TailTruncation;
BasePasswordLabel.Lines = 1; BasePasswordLabel.Lines = 0;
BasePasswordLabel.AdjustsFontSizeToFitWidth = false; BasePasswordLabel.AdjustsFontSizeToFitWidth = false;
BasePasswordLabel.TextColor = ThemeHelpers.TextColor; BasePasswordLabel.TextColor = ThemeHelpers.TextColor;
@ -71,6 +89,10 @@ namespace Bit.iOS.Core.Controllers
OptionsTableViewController.TableView.SeparatorColor = ThemeHelpers.SeparatorColor; OptionsTableViewController.TableView.SeparatorColor = ThemeHelpers.SeparatorColor;
} }
TypePickerCell.Items = TypeOptions;
TypePickerCell.ValueChanged += Type_ValueChanged;
SetPassType();
var (options, enforcedPolicyOptions) = await _passwordGenerationService.GetOptionsAsync(); var (options, enforcedPolicyOptions) = await _passwordGenerationService.GetOptionsAsync();
UppercaseCell.Switch.On = options.Uppercase.GetValueOrDefault(); UppercaseCell.Switch.On = options.Uppercase.GetValueOrDefault();
LowercaseCell.Switch.On = options.Lowercase.GetValueOrDefault(true); LowercaseCell.Switch.On = options.Lowercase.GetValueOrDefault(true);
@ -79,6 +101,12 @@ namespace Bit.iOS.Core.Controllers
MinNumbersCell.Value = options.MinNumber.GetValueOrDefault(1); MinNumbersCell.Value = options.MinNumber.GetValueOrDefault(1);
MinSpecialCell.Value = options.MinSpecial.GetValueOrDefault(1); MinSpecialCell.Value = options.MinSpecial.GetValueOrDefault(1);
LengthCell.Value = options.Length.GetValueOrDefault(14); LengthCell.Value = options.Length.GetValueOrDefault(14);
AmbiguousCell.Switch.On = options.Ambiguous.GetValueOrDefault();
NumWordsCell.Value = options.NumWords.GetValueOrDefault(3);
WordSeparatorCell.TextField.Text = options.WordSeparator ?? "";
CapitalizeCell.Switch.On = options.Capitalize.GetValueOrDefault();
IncludeNumberCell.Switch.On = options.IncludeNumber.GetValueOrDefault();
UppercaseCell.ValueChanged += Options_ValueChanged; UppercaseCell.ValueChanged += Options_ValueChanged;
LowercaseCell.ValueChanged += Options_ValueChanged; LowercaseCell.ValueChanged += Options_ValueChanged;
@ -87,6 +115,12 @@ namespace Bit.iOS.Core.Controllers
MinNumbersCell.ValueChanged += Options_ValueChanged; MinNumbersCell.ValueChanged += Options_ValueChanged;
MinSpecialCell.ValueChanged += Options_ValueChanged; MinSpecialCell.ValueChanged += Options_ValueChanged;
LengthCell.ValueChanged += Options_ValueChanged; LengthCell.ValueChanged += Options_ValueChanged;
AmbiguousCell.ValueChanged += Options_ValueChanged;
NumWordsCell.ValueChanged += Options_ValueChanged;
WordSeparatorCell.ValueChanged += Options_ValueChanged;
CapitalizeCell.ValueChanged += Options_ValueChanged;
IncludeNumberCell.ValueChanged += Options_ValueChanged;
// Adjust based on context password options // Adjust based on context password options
if (PasswordOptions != null) if (PasswordOptions != null)
@ -154,6 +188,18 @@ namespace Bit.iOS.Core.Controllers
var task = GeneratePasswordAsync(); var task = GeneratePasswordAsync();
} }
private void Type_ValueChanged(object sender, EventArgs e)
{
SetPassType();
OptionsTableViewController.TableView.ReloadData();
var task = GeneratePasswordAsync();
}
private void SetPassType()
{
_passType = TypePickerCell.SelectedIndex == 1 ? "passphrase" : "password";
}
private bool InvalidState() private bool InvalidState()
{ {
return !LowercaseCell.Switch.On && !UppercaseCell.Switch.On && !NumbersCell.Switch.On && return !LowercaseCell.Switch.On && !UppercaseCell.Switch.On && !NumbersCell.Switch.On &&
@ -165,6 +211,7 @@ namespace Bit.iOS.Core.Controllers
BasePasswordLabel.Text = await _passwordGenerationService.GeneratePasswordAsync( BasePasswordLabel.Text = await _passwordGenerationService.GeneratePasswordAsync(
new Bit.Core.Models.Domain.PasswordGenerationOptions new Bit.Core.Models.Domain.PasswordGenerationOptions
{ {
Type = _passType,
Length = LengthCell.Value, Length = LengthCell.Value,
Uppercase = UppercaseCell.Switch.On, Uppercase = UppercaseCell.Switch.On,
Lowercase = LowercaseCell.Switch.On, Lowercase = LowercaseCell.Switch.On,
@ -172,6 +219,11 @@ namespace Bit.iOS.Core.Controllers
Special = SpecialCell.Switch.On, Special = SpecialCell.Switch.On,
MinSpecial = MinSpecialCell.Value, MinSpecial = MinSpecialCell.Value,
MinNumber = MinNumbersCell.Value, MinNumber = MinNumbersCell.Value,
Ambiguous = AmbiguousCell.Switch.On,
NumWords = NumWordsCell.Value,
WordSeparator = WordSeparatorCell.TextField.Text,
Capitalize = CapitalizeCell.Switch.On,
IncludeNumber = IncludeNumberCell.Switch.On
}); });
} }
@ -203,31 +255,62 @@ namespace Bit.iOS.Core.Controllers
if (indexPath.Row == 0) if (indexPath.Row == 0)
{ {
return _controller.LengthCell; return _controller.TypePickerCell;
} }
else if (indexPath.Row == 1)
if (_controller._passType == "password")
{ {
return _controller.UppercaseCell;
if (indexPath.Row == 1)
{
return _controller.LengthCell;
} }
else if (indexPath.Row == 2) else if (indexPath.Row == 2)
{ {
return _controller.LowercaseCell; return _controller.UppercaseCell;
} }
else if (indexPath.Row == 3) else if (indexPath.Row == 3)
{ {
return _controller.NumbersCell; return _controller.LowercaseCell;
} }
else if (indexPath.Row == 4) else if (indexPath.Row == 4)
{ {
return _controller.SpecialCell; return _controller.NumbersCell;
} }
else if (indexPath.Row == 5) else if (indexPath.Row == 5)
{ {
return _controller.MinNumbersCell; return _controller.SpecialCell;
} }
else if (indexPath.Row == 6) else if (indexPath.Row == 6)
{
return _controller.MinNumbersCell;
}
else if (indexPath.Row == 7)
{ {
return _controller.MinSpecialCell; return _controller.MinSpecialCell;
} else if (indexPath.Row == 8)
{
return _controller.AmbiguousCell;
}
}
else
{
if (indexPath.Row == 1)
{
return _controller.NumWordsCell;
}
else if (indexPath.Row == 2)
{
return _controller.WordSeparatorCell;
}
else if (indexPath.Row == 3)
{
return _controller.CapitalizeCell;
}
else if (indexPath.Row == 4)
{
return _controller.IncludeNumberCell;
}
} }
return new ExtendedUITableViewCell(); return new ExtendedUITableViewCell();
@ -250,7 +333,14 @@ namespace Bit.iOS.Core.Controllers
return 2; return 2;
} }
return 7; if (_controller._passType == "password")
{
return 9;
}
else
{
return 5;
}
} }
public override nfloat GetHeightForHeader(UITableView tableView, nint section) public override nfloat GetHeightForHeader(UITableView tableView, nint section)

View file

@ -11,7 +11,8 @@ namespace Bit.iOS.Core.Views
string labelName = null, string labelName = null,
bool useTextView = false, bool useTextView = false,
nfloat? height = null, nfloat? height = null,
bool useLabelAsPlaceholder = false) bool useLabelAsPlaceholder = false,
float leadingConstant = 15f)
: base(UITableViewCellStyle.Default, nameof(FormEntryTableViewCell)) : base(UITableViewCellStyle.Default, nameof(FormEntryTableViewCell))
{ {
var descriptor = UIFontDescriptor.PreferredBody; var descriptor = UIFontDescriptor.PreferredBody;
@ -48,7 +49,7 @@ namespace Bit.iOS.Core.Views
ContentView.Add(TextView); ContentView.Add(TextView);
ContentView.AddConstraints(new NSLayoutConstraint[] { ContentView.AddConstraints(new NSLayoutConstraint[] {
NSLayoutConstraint.Create(TextView, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1f, 15f), NSLayoutConstraint.Create(TextView, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1f, leadingConstant),
NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, TextView, NSLayoutAttribute.Trailing, 1f, 15f), NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, TextView, NSLayoutAttribute.Trailing, 1f, 15f),
NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextView, NSLayoutAttribute.Bottom, 1f, 10f) NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextView, NSLayoutAttribute.Bottom, 1f, 10f)
}); });
@ -69,7 +70,13 @@ namespace Bit.iOS.Core.Views
ContentView.AddConstraint( ContentView.AddConstraint(
NSLayoutConstraint.Create(TextView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1f, height.Value)); NSLayoutConstraint.Create(TextView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1f, height.Value));
} }
TextView.Changed += (object sender, EventArgs e) =>
{
ValueChanged?.Invoke(sender, e);
};
} }
else else
{ {
TextField = new UITextField TextField = new UITextField
@ -95,7 +102,7 @@ namespace Bit.iOS.Core.Views
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, leadingConstant),
NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, TextField, NSLayoutAttribute.Trailing, 1f, 15f), NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, TextField, NSLayoutAttribute.Trailing, 1f, 15f),
NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextField, NSLayoutAttribute.Bottom, 1f, 10f) NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, TextField, NSLayoutAttribute.Bottom, 1f, 10f)
}); });
@ -116,12 +123,17 @@ namespace Bit.iOS.Core.Views
ContentView.AddConstraint( ContentView.AddConstraint(
NSLayoutConstraint.Create(TextField, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1f, height.Value)); NSLayoutConstraint.Create(TextField, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1f, height.Value));
} }
TextField.AddTarget((object sender, EventArgs e) =>
{
ValueChanged?.Invoke(sender, e);
}, UIControlEvent.EditingChanged);
} }
if (labelName != null && !useLabelAsPlaceholder) 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, leadingConstant),
NSLayoutConstraint.Create(Label, NSLayoutAttribute.Top, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Top, 1f, 10f), NSLayoutConstraint.Create(Label, NSLayoutAttribute.Top, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Top, 1f, 10f),
NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, Label, NSLayoutAttribute.Trailing, 1f, 15f) NSLayoutConstraint.Create(ContentView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, Label, NSLayoutAttribute.Trailing, 1f, 15f)
}); });
@ -131,6 +143,7 @@ namespace Bit.iOS.Core.Views
public UILabel Label { get; set; } public UILabel Label { get; set; }
public UITextField TextField { get; set; } public UITextField TextField { get; set; }
public UITextView TextView { get; set; } public UITextView TextView { get; set; }
public event EventHandler ValueChanged;
public void Select() public void Select()
{ {

View file

@ -94,6 +94,8 @@ namespace Bit.iOS.Core.Views
public UILabel Label { get; set; } public UILabel Label { get; set; }
public UIPickerView Picker { get; set; } = new UIPickerView(); public UIPickerView Picker { get; set; } = new UIPickerView();
public event EventHandler ValueChanged;
public List<string> Items public List<string> Items
{ {
get { return _items; } get { return _items; }
@ -199,6 +201,7 @@ namespace Bit.iOS.Core.Views
} }
_cell.UpdatePickerFromModel(this); _cell.UpdatePickerFromModel(this);
_cell.ValueChanged?.Invoke(this, null);
} }
} }
} }