diff --git a/src/iOS.Autofill/CredentialProviderViewController.cs b/src/iOS.Autofill/CredentialProviderViewController.cs index c532f9723..ccb9df3b3 100644 --- a/src/iOS.Autofill/CredentialProviderViewController.cs +++ b/src/iOS.Autofill/CredentialProviderViewController.cs @@ -26,7 +26,7 @@ namespace Bit.iOS.Autofill private bool _setupHockeyApp = false; private IGoogleAnalyticsService _googleAnalyticsService; - public CredentialProviderViewController (IntPtr handle) : base (handle) + public CredentialProviderViewController(IntPtr handle) : base(handle) { } @@ -38,7 +38,7 @@ namespace Bit.iOS.Autofill _context.ExtContext = ExtensionContext; _googleAnalyticsService = Resolver.Resolve(); - if (!_setupHockeyApp) + if(!_setupHockeyApp) { var appIdService = Resolver.Resolve(); var crashManagerDelegate = new HockeyAppCrashManagerDelegate(appIdService, Resolver.Resolve()); @@ -59,7 +59,7 @@ namespace Bit.iOS.Autofill base.PrepareCredentialList(serviceIdentifiers); var authService = Resolver.Resolve(); - if (!authService.IsAuthenticated) + if(!authService.IsAuthenticated) { var alert = Dialogs.CreateAlert(null, AppResources.MustLogInMainApp, AppResources.Ok, (a) => { @@ -69,8 +69,6 @@ namespace Bit.iOS.Autofill return; } - - PerformSegue("loginListSegue", this); } @@ -91,10 +89,11 @@ namespace Bit.iOS.Autofill public void CompleteRequest(string username = null, string password = null, string totp = null) { - if(string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(password)) { + if(string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(password)) + { _googleAnalyticsService.TrackAutofillExtensionEvent("Canceled"); - var err = new NSError(new NSString("ASExtensionErrorDomain"), - Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null); + var err = new NSError(new NSString("ASExtensionErrorDomain"), + Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null); _googleAnalyticsService.Dispatch(() => { NSRunLoop.Main.BeginInvokeOnMainThread(() => @@ -121,16 +120,14 @@ namespace Bit.iOS.Autofill }); } - - public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender) { var navController = segue.DestinationViewController as UINavigationController; - if (navController != null) + if(navController != null) { var listLoginController = navController.TopViewController as LoginListViewController; - if (listLoginController != null) + if(listLoginController != null) { listLoginController.Context = _context; listLoginController.CPViewController = this; diff --git a/src/iOS.Autofill/Models/Context.cs b/src/iOS.Autofill/Models/Context.cs index f389b83ac..30f3ab503 100644 --- a/src/iOS.Autofill/Models/Context.cs +++ b/src/iOS.Autofill/Models/Context.cs @@ -1,12 +1,12 @@ using AuthenticationServices; +using Bit.iOS.Core.Models; using Foundation; namespace Bit.iOS.Autofill.Models { - public class Context + public class Context : AppExtensionContext { public NSExtensionContext ExtContext { get; set; } - public ASCredentialServiceIdentifier[] ServiceIdentifiers { get; set; } - public string UrlString { get; set; } + public ASCredentialServiceIdentifier[] ServiceIdentifiers { get; set; } } } diff --git a/src/iOS.Core/Controllers/LockFingerprintViewController.cs b/src/iOS.Core/Controllers/LockFingerprintViewController.cs new file mode 100644 index 000000000..43447d96d --- /dev/null +++ b/src/iOS.Core/Controllers/LockFingerprintViewController.cs @@ -0,0 +1,86 @@ +using System; +using UIKit; +using XLabs.Ioc; +using Plugin.Fingerprint.Abstractions; +using System.Threading.Tasks; +using Bit.iOS.Core.Controllers; +using Bit.App.Resources; +using Bit.App.Abstractions; + +namespace Bit.iOS.Core.Controllers +{ + public abstract class LockFingerprintViewController : ExtendedUIViewController + { + private IAppSettingsService _appSettingsService; + private IFingerprint _fingerprint; + private IDeviceInfoService _deviceInfo; + + public LockFingerprintViewController(IntPtr handle) : base(handle) + { } + + public abstract UINavigationItem BaseNavItem { get; } + public abstract UIBarButtonItem BaseCancelButton { get; } + public abstract UIButton BaseUseButton { get; } + public abstract UIButton BaseFingerprintButton { get; } + public abstract Action Success { get; } + + public override void ViewWillAppear(bool animated) + { + UINavigationBar.Appearance.ShadowImage = new UIImage(); + UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); + base.ViewWillAppear(animated); + } + + public override void ViewDidLoad() + { + _appSettingsService = Resolver.Resolve(); + _fingerprint = Resolver.Resolve(); + _deviceInfo = Resolver.Resolve(); + + BaseNavItem.Title = _deviceInfo.HasFaceIdSupport ? AppResources.VerifyFaceID : AppResources.VerifyFingerprint; + BaseCancelButton.Title = AppResources.Cancel; + View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); + + BaseUseButton.SetTitle(_deviceInfo.HasFaceIdSupport ? AppResources.UseFaceIDToUnlock : + AppResources.UseFingerprintToUnlock, UIControlState.Normal); + var descriptor = UIFontDescriptor.PreferredBody; + BaseUseButton.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize); + BaseUseButton.BackgroundColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f); + BaseUseButton.TintColor = UIColor.White; + BaseUseButton.TouchUpInside += UseButton_TouchUpInside; + + BaseFingerprintButton.SetImage(new UIImage(_deviceInfo.HasFaceIdSupport ? "smile.png" : "fingerprint.png"), + UIControlState.Normal); + + base.ViewDidLoad(); + } + + private void UseButton_TouchUpInside(object sender, EventArgs e) + { + var task = CheckFingerprintAsync(); + } + + public override void ViewDidAppear(bool animated) + { + base.ViewDidAppear(animated); + var task = CheckFingerprintAsync(); + } + + public async Task CheckFingerprintAsync() + { + var fingerprintRequest = new AuthenticationRequestConfiguration( + _deviceInfo.HasFaceIdSupport ? AppResources.FaceIDDirection : AppResources.FingerprintDirection) + { + AllowAlternativeAuthentication = true, + CancelTitle = AppResources.Cancel, + FallbackTitle = AppResources.LogOut + }; + var result = await _fingerprint.AuthenticateAsync(fingerprintRequest); + if(result.Authenticated) + { + _appSettingsService.Locked = false; + Success(); + } + } + } +} diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs new file mode 100644 index 000000000..0b5ec45e1 --- /dev/null +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -0,0 +1,178 @@ +using System; +using UIKit; +using XLabs.Ioc; +using Foundation; +using Bit.iOS.Core.Views; +using Bit.App.Resources; +using Bit.iOS.Core.Utilities; +using Bit.App.Abstractions; +using System.Linq; +using Bit.iOS.Core.Controllers; + +namespace Bit.iOS.Core.Controllers +{ + public abstract class LockPasswordViewController : ExtendedUITableViewController + { + private IAppSettingsService _appSettingsService; + private IAuthService _authService; + private ICryptoService _cryptoService; + + public LockPasswordViewController(IntPtr handle) : base(handle) + { } + + public abstract UINavigationItem BaseNavItem { get; } + public abstract UIBarButtonItem BaseCancelButton { get; } + public abstract UIBarButtonItem BaseSubmitButton { get; } + public abstract Action Success { get; } + + public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell( + AppResources.MasterPassword, useLabelAsPlaceholder: true); + + public override void ViewWillAppear(bool animated) + { + UINavigationBar.Appearance.ShadowImage = new UIImage(); + UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); + base.ViewWillAppear(animated); + } + + public override void ViewDidLoad() + { + _appSettingsService = Resolver.Resolve(); + _authService = Resolver.Resolve(); + _cryptoService = Resolver.Resolve(); + + BaseNavItem.Title = AppResources.VerifyMasterPassword; + BaseCancelButton.Title = AppResources.Cancel; + BaseSubmitButton.Title = AppResources.Submit; + 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(); + } + + protected 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, + _authService.Kdf, _authService.KdfIterations); + if(key.Key.SequenceEqual(_cryptoService.Key.Key)) + { + _appSettingsService.Locked = false; + MasterPasswordCell.TextField.ResignFirstResponder(); + Success(); + } + else + { + // TODO: keep track of invalid attempts and logout? + + var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred, + string.Format(null, AppResources.InvalidMasterPassword), AppResources.Ok, (a) => + { + + MasterPasswordCell.TextField.Text = string.Empty; + MasterPasswordCell.TextField.BecomeFirstResponder(); + }); + + PresentViewController(alert, true, 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.Core/Controllers/LockPinViewController.cs b/src/iOS.Core/Controllers/LockPinViewController.cs new file mode 100644 index 000000000..2211c69e6 --- /dev/null +++ b/src/iOS.Core/Controllers/LockPinViewController.cs @@ -0,0 +1,102 @@ +using System; +using UIKit; +using XLabs.Ioc; +using Bit.App.Abstractions; +using Bit.iOS.Core.Utilities; +using Bit.App.Resources; +using System.Diagnostics; +using Bit.iOS.Core.Controllers; + +namespace Bit.iOS.Core.Controllers +{ + public abstract class LockPinViewController : ExtendedUIViewController + { + private IAppSettingsService _appSettingsService; + private IAuthService _authService; + + public LockPinViewController(IntPtr handle) : base(handle) + { } + + public abstract UINavigationItem BaseNavItem { get; } + public abstract UIBarButtonItem BaseCancelButton { get; } + public abstract UILabel BasePinLabel { get; } + public abstract UILabel BaseInstructionLabel { get; } + public abstract UITextField BasePinTextField { get; } + public abstract Action Success { get; } + + public override void ViewWillAppear(bool animated) + { + UINavigationBar.Appearance.ShadowImage = new UIImage(); + UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); + base.ViewWillAppear(animated); + } + + public override void ViewDidLoad() + { + _appSettingsService = Resolver.Resolve(); + _authService = Resolver.Resolve(); + + BaseNavItem.Title = AppResources.VerifyPIN; + BaseCancelButton.Title = AppResources.Cancel; + View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); + + var descriptor = UIFontDescriptor.PreferredBody; + BasePinLabel.Font = UIFont.FromName("Menlo-Regular", 35); + + BaseInstructionLabel.Text = AppResources.EnterPIN; + BaseInstructionLabel.LineBreakMode = UILineBreakMode.WordWrap; + BaseInstructionLabel.Lines = 0; + BaseInstructionLabel.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize * 0.8f); + BaseInstructionLabel.TextColor = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f); + + BasePinTextField.EditingChanged += PinTextField_EditingChanged; + + base.ViewDidLoad(); + } + + public override void ViewDidAppear(bool animated) + { + base.ViewDidAppear(animated); + BasePinTextField.BecomeFirstResponder(); + } + + private void PinTextField_EditingChanged(object sender, EventArgs e) + { + SetLabelText(); + + if(BasePinTextField.Text.Length >= 4) + { + if(BasePinTextField.Text == _authService.PIN) + { + Debug.WriteLine("BW Log, Start Dismiss PIN controller."); + _appSettingsService.Locked = false; + BasePinTextField.ResignFirstResponder(); + Success(); + } + else + { + // TODO: keep track of invalid attempts and logout? + + var alert = Dialogs.CreateAlert(null, AppResources.InvalidPIN, AppResources.Ok, (a) => + { + BasePinTextField.Text = string.Empty; + SetLabelText(); + BasePinTextField.BecomeFirstResponder(); + }); + PresentViewController(alert, true, null); + } + } + } + + private void SetLabelText() + { + var newText = string.Empty; + for(int i = 0; i < 4; i++) + { + newText += BasePinTextField.Text.Length <= i ? "- " : "• "; + } + + BasePinLabel.Text = newText.TrimEnd(); + } + } +} diff --git a/src/iOS.Core/Controllers/LoginAddViewController.cs b/src/iOS.Core/Controllers/LoginAddViewController.cs new file mode 100644 index 000000000..775a9c10a --- /dev/null +++ b/src/iOS.Core/Controllers/LoginAddViewController.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Bit.App.Abstractions; +using Bit.App.Models; +using Bit.App.Resources; +using Bit.iOS.Core.Views; +using Foundation; +using UIKit; +using XLabs.Ioc; +using Bit.App; +using Plugin.Connectivity.Abstractions; +using Bit.iOS.Core.Utilities; +using Bit.iOS.Core.Models; +using System.Threading.Tasks; + +namespace Bit.iOS.Core.Controllers +{ + public abstract class LoginAddViewController : ExtendedUITableViewController + { + private ICipherService _cipherService; + private IFolderService _folderService; + private IConnectivity _connectivity; + private IEnumerable _folders; + private IGoogleAnalyticsService _googleAnalyticsService; + + public LoginAddViewController(IntPtr handle) : base(handle) + { } + + public AppExtensionContext Context { get; set; } + public FormEntryTableViewCell NameCell { get; set; } = new FormEntryTableViewCell(AppResources.Name); + public FormEntryTableViewCell UsernameCell { get; set; } = new FormEntryTableViewCell(AppResources.Username); + public FormEntryTableViewCell PasswordCell { get; set; } = new FormEntryTableViewCell(AppResources.Password); + public UITableViewCell GeneratePasswordCell { get; set; } = new UITableViewCell(UITableViewCellStyle.Subtitle, "GeneratePasswordCell"); + public FormEntryTableViewCell UriCell { get; set; } = new FormEntryTableViewCell(AppResources.URI); + public SwitchTableViewCell FavoriteCell { get; set; } = new SwitchTableViewCell(AppResources.Favorite); + public FormEntryTableViewCell NotesCell { get; set; } = new FormEntryTableViewCell(useTextView: true, height: 180); + public PickerTableViewCell FolderCell { get; set; } = new PickerTableViewCell(AppResources.Folder); + + public abstract UINavigationItem BaseNavItem { get; } + public abstract UIBarButtonItem BaseCancelButton { get; } + public abstract UIBarButtonItem BaseSaveButton { get; } + public abstract Action Success { get; } + + public override void ViewWillAppear(bool animated) + { + UINavigationBar.Appearance.ShadowImage = new UIImage(); + UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); + base.ViewWillAppear(animated); + } + + public override void ViewDidLoad() + { + _cipherService = Resolver.Resolve(); + _connectivity = Resolver.Resolve(); + _folderService = Resolver.Resolve(); + _googleAnalyticsService = Resolver.Resolve(); + + BaseNavItem.Title = AppResources.AddItem; + BaseCancelButton.Title = AppResources.Cancel; + BaseSaveButton.Title = AppResources.Save; + View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); + + NameCell.TextField.Text = Context?.Uri?.Host ?? string.Empty; + NameCell.TextField.ReturnKeyType = UIReturnKeyType.Next; + NameCell.TextField.ShouldReturn += (UITextField tf) => + { + UsernameCell.TextField.BecomeFirstResponder(); + return true; + }; + + UsernameCell.TextField.AutocapitalizationType = UITextAutocapitalizationType.None; + UsernameCell.TextField.AutocorrectionType = UITextAutocorrectionType.No; + UsernameCell.TextField.SpellCheckingType = UITextSpellCheckingType.No; + UsernameCell.TextField.ReturnKeyType = UIReturnKeyType.Next; + UsernameCell.TextField.ShouldReturn += (UITextField tf) => + { + PasswordCell.TextField.BecomeFirstResponder(); + return true; + }; + + PasswordCell.TextField.SecureTextEntry = true; + PasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Next; + PasswordCell.TextField.ShouldReturn += (UITextField tf) => + { + UriCell.TextField.BecomeFirstResponder(); + return true; + }; + + GeneratePasswordCell.TextLabel.Text = AppResources.GeneratePassword; + GeneratePasswordCell.Accessory = UITableViewCellAccessory.DisclosureIndicator; + + UriCell.TextField.Text = Context?.UrlString ?? string.Empty; + UriCell.TextField.KeyboardType = UIKeyboardType.Url; + UriCell.TextField.ReturnKeyType = UIReturnKeyType.Next; + UriCell.TextField.ShouldReturn += (UITextField tf) => + { + NotesCell.TextView.BecomeFirstResponder(); + return true; + }; + + _folders = _folderService.GetAllAsync().GetAwaiter().GetResult(); + var folderNames = _folders.Select(s => s.Name.Decrypt()).OrderBy(s => s).ToList(); + folderNames.Insert(0, AppResources.FolderNone); + FolderCell.Items = folderNames; + + 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); + } + + protected async Task SaveAsync() + { + if(!_connectivity.IsConnected) + { + AlertNoConnection(); + return; + } + + if(string.IsNullOrWhiteSpace(PasswordCell.TextField.Text)) + { + DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Password), AppResources.Ok); + return; + } + + if(string.IsNullOrWhiteSpace(NameCell.TextField.Text)) + { + DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Name), AppResources.Ok); + return; + } + + var cipher = new Cipher + { + Name = string.IsNullOrWhiteSpace(NameCell.TextField.Text) ? null : NameCell.TextField.Text.Encrypt(), + Notes = string.IsNullOrWhiteSpace(NotesCell.TextView.Text) ? null : NotesCell.TextView.Text.Encrypt(), + Favorite = FavoriteCell.Switch.On, + FolderId = FolderCell.SelectedIndex == 0 ? null : _folders.ElementAtOrDefault(FolderCell.SelectedIndex - 1)?.Id, + Type = App.Enums.CipherType.Login, + Login = new Login + { + Uris = null, + Username = string.IsNullOrWhiteSpace(UsernameCell.TextField.Text) ? null : UsernameCell.TextField.Text.Encrypt(), + Password = string.IsNullOrWhiteSpace(PasswordCell.TextField.Text) ? null : PasswordCell.TextField.Text.Encrypt() + } + }; + + if(!string.IsNullOrWhiteSpace(UriCell.TextField.Text)) + { + cipher.Login.Uris = new List + { + new LoginUri + { + Uri = UriCell.TextField.Text.Encrypt() + } + }; + } + + var saveTask = _cipherService.SaveAsync(cipher); + var loadingAlert = Dialogs.CreateLoadingAlert(AppResources.Saving); + PresentViewController(loadingAlert, true, null); + await saveTask; + + await loadingAlert.DismissViewControllerAsync(true); + if(saveTask.Result.Succeeded) + { + _googleAnalyticsService.TrackExtensionEvent("CreatedLogin"); + Success(); + } + else if(saveTask.Result.Errors.Count() > 0) + { + DisplayAlert(AppResources.AnErrorHasOccurred, saveTask.Result.Errors.First().Message, AppResources.Ok); + } + else + { + DisplayAlert(null, AppResources.AnErrorHasOccurred, AppResources.Ok); + } + } + + public void DisplayAlert(string title, string message, string accept) + { + var alert = Dialogs.CreateAlert(title, message, accept); + PresentViewController(alert, true, null); + } + + private void AlertNoConnection() + { + DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, AppResources.Ok); + } + + public class TableSource : UITableViewSource + { + private LoginAddViewController _controller; + + public TableSource(LoginAddViewController controller) + { + _controller = controller; + } + + public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) + { + if(indexPath.Section == 0) + { + if(indexPath.Row == 0) + { + return _controller.NameCell; + } + else if(indexPath.Row == 1) + { + return _controller.UsernameCell; + } + else if(indexPath.Row == 2) + { + return _controller.PasswordCell; + } + else if(indexPath.Row == 3) + { + return _controller.GeneratePasswordCell; + } + } + else if(indexPath.Section == 1) + { + return _controller.UriCell; + } + else if(indexPath.Section == 2) + { + if(indexPath.Row == 0) + { + return _controller.FolderCell; + } + else if(indexPath.Row == 1) + { + return _controller.FavoriteCell; + } + } + else if(indexPath.Section == 3) + { + return _controller.NotesCell; + } + + return new UITableViewCell(); + } + + public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) + { + return UITableView.AutomaticDimension; + } + + public override nint NumberOfSections(UITableView tableView) + { + return 4; + } + + public override nint RowsInSection(UITableView tableview, nint section) + { + if(section == 0) + { + return 4; + } + else if(section == 1) + { + return 1; + } + else if(section == 2) + { + return 2; + } + else + { + return 1; + } + } + + public override nfloat GetHeightForHeader(UITableView tableView, nint section) + { + return section == 0 || section == 3 ? UITableView.AutomaticDimension : 0.00001f; + } + + public override string TitleForHeader(UITableView tableView, nint section) + { + if(section == 0) + { + return AppResources.ItemInformation; + } + else if(section == 3) + { + return AppResources.Notes; + } + + return string.Empty; + } + + public override void RowSelected(UITableView tableView, NSIndexPath indexPath) + { + tableView.DeselectRow(indexPath, true); + tableView.EndEditing(true); + + if(indexPath.Section == 0 && indexPath.Row == 3) + { + _controller.PerformSegue("passwordGeneratorSegue", this); + } + + var cell = tableView.CellAt(indexPath); + if(cell == null) + { + return; + } + + var selectableCell = cell as ISelectable; + if(selectableCell != null) + { + selectableCell.Select(); + } + } + } + } +} diff --git a/src/iOS.Core/Controllers/PasswordGeneratorViewController.cs b/src/iOS.Core/Controllers/PasswordGeneratorViewController.cs new file mode 100644 index 000000000..c8ea40754 --- /dev/null +++ b/src/iOS.Core/Controllers/PasswordGeneratorViewController.cs @@ -0,0 +1,336 @@ +using System; +using System.Linq; +using Bit.App.Abstractions; +using Bit.iOS.Core.Views; +using Bit.iOS.Core.Models; +using Foundation; +using UIKit; +using XLabs.Ioc; +using Plugin.Settings.Abstractions; +using CoreGraphics; +using Bit.iOS.Core.Utilities; +using Bit.App.Resources; + +namespace Bit.iOS.Core.Controllers +{ + public abstract class PasswordGeneratorViewController : ExtendedUIViewController + { + private IPasswordGenerationService _passwordGenerationService; + private ISettings _settings; + + public PasswordGeneratorViewController(IntPtr handle) : base(handle) + { } + + protected IGoogleAnalyticsService GoogleAnalyticsService { get; private set; } + public UITableViewController OptionsTableViewController { get; set; } + public SwitchTableViewCell UppercaseCell { 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 SpecialCell { get; set; } = new SwitchTableViewCell("!@#$%^&*"); + public StepperTableViewCell MinNumbersCell { get; set; } = new StepperTableViewCell(AppResources.MinNumbers, 1, 0, 5, 1); + public StepperTableViewCell MinSpecialCell { get; set; } = new StepperTableViewCell(AppResources.MinSpecial, 1, 0, 5, 1); + public SliderTableViewCell LengthCell { get; set; } = new SliderTableViewCell(AppResources.Length, 10, 5, 64); + + public PasswordGenerationOptions PasswordOptions { get; set; } + public abstract UINavigationItem BaseNavItem { get; } + public abstract UIBarButtonItem BaseCancelButton { get; } + public abstract UIBarButtonItem BaseSelectBarButton { get; } + public abstract UILabel BasePasswordLabel { get; } + + public override void ViewWillAppear(bool animated) + { + UINavigationBar.Appearance.ShadowImage = new UIImage(); + UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); + base.ViewWillAppear(animated); + } + + public override void ViewDidLoad() + { + _passwordGenerationService = Resolver.Resolve(); + _settings = Resolver.Resolve(); + GoogleAnalyticsService = Resolver.Resolve(); + + BaseNavItem.Title = AppResources.PasswordGenerator; + BaseCancelButton.Title = AppResources.Cancel; + BaseSelectBarButton.Title = AppResources.Select; + View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); + + var descriptor = UIFontDescriptor.PreferredBody; + BasePasswordLabel.Font = UIFont.FromName("Menlo-Regular", descriptor.PointSize * 1.3f); + BasePasswordLabel.LineBreakMode = UILineBreakMode.TailTruncation; + BasePasswordLabel.Lines = 1; + BasePasswordLabel.AdjustsFontSizeToFitWidth = false; + + var controller = ChildViewControllers.LastOrDefault(); + if(controller != null) + { + OptionsTableViewController = controller as UITableViewController; + } + + if(OptionsTableViewController != null) + { + OptionsTableViewController.TableView.RowHeight = UITableView.AutomaticDimension; + OptionsTableViewController.TableView.EstimatedRowHeight = 70; + OptionsTableViewController.TableView.Source = new TableSource(this); + OptionsTableViewController.TableView.AllowsSelection = true; + OptionsTableViewController.View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); + } + + UppercaseCell.Switch.On = _settings.GetValueOrDefault(App.Constants.PasswordGeneratorUppercase, true); + LowercaseCell.Switch.On = _settings.GetValueOrDefault(App.Constants.PasswordGeneratorLowercase, true); + SpecialCell.Switch.On = _settings.GetValueOrDefault(App.Constants.PasswordGeneratorSpecial, true); + NumbersCell.Switch.On = _settings.GetValueOrDefault(App.Constants.PasswordGeneratorNumbers, true); + MinNumbersCell.Value = _settings.GetValueOrDefault(App.Constants.PasswordGeneratorMinNumbers, 1); + MinSpecialCell.Value = _settings.GetValueOrDefault(App.Constants.PasswordGeneratorMinSpecial, 1); + LengthCell.Value = _settings.GetValueOrDefault(App.Constants.PasswordGeneratorLength, 10); + + UppercaseCell.ValueChanged += Options_ValueChanged; + LowercaseCell.ValueChanged += Options_ValueChanged; + NumbersCell.ValueChanged += Options_ValueChanged; + SpecialCell.ValueChanged += Options_ValueChanged; + MinNumbersCell.ValueChanged += Options_ValueChanged; + MinSpecialCell.ValueChanged += Options_ValueChanged; + LengthCell.ValueChanged += Options_ValueChanged; + + // Adjust based on context password options + if(PasswordOptions != null) + { + if(PasswordOptions.RequireDigits) + { + NumbersCell.Switch.On = true; + NumbersCell.Switch.Enabled = false; + + if(MinNumbersCell.Value < 1) + { + MinNumbersCell.Value = 1; + } + + MinNumbersCell.Stepper.MinimumValue = 1; + } + + if(PasswordOptions.RequireSymbols) + { + SpecialCell.Switch.On = true; + SpecialCell.Switch.Enabled = false; + + if(MinSpecialCell.Value < 1) + { + MinSpecialCell.Value = 1; + } + + MinSpecialCell.Stepper.MinimumValue = 1; + } + + if(PasswordOptions.MinLength < PasswordOptions.MaxLength) + { + if(PasswordOptions.MinLength > 0 && PasswordOptions.MinLength > LengthCell.Slider.MinValue) + { + if(LengthCell.Value < PasswordOptions.MinLength) + { + LengthCell.Slider.Value = PasswordOptions.MinLength; + } + + LengthCell.Slider.MinValue = PasswordOptions.MinLength; + } + + if(PasswordOptions.MaxLength > 5 && PasswordOptions.MaxLength < LengthCell.Slider.MaxValue) + { + if(LengthCell.Value > PasswordOptions.MaxLength) + { + LengthCell.Slider.Value = PasswordOptions.MaxLength; + } + + LengthCell.Slider.MaxValue = PasswordOptions.MaxLength; + } + } + } + + GeneratePassword(); + GoogleAnalyticsService.TrackExtensionEvent("GeneratedPassword"); + base.ViewDidLoad(); + } + + private void Options_ValueChanged(object sender, EventArgs e) + { + if(InvalidState()) + { + LowercaseCell.Switch.On = true; + } + + GeneratePassword(); + } + + private bool InvalidState() + { + return !LowercaseCell.Switch.On && !UppercaseCell.Switch.On && !NumbersCell.Switch.On && !SpecialCell.Switch.On; + } + + private void GeneratePassword() + { + BasePasswordLabel.Text = _passwordGenerationService.GeneratePassword( + length: LengthCell.Value, + uppercase: UppercaseCell.Switch.On, + lowercase: LowercaseCell.Switch.On, + numbers: NumbersCell.Switch.On, + special: SpecialCell.Switch.On, + minSpecial: MinSpecialCell.Value, + minNumbers: MinNumbersCell.Value); + } + + public class TableSource : UITableViewSource + { + private PasswordGeneratorViewController _controller; + + public TableSource(PasswordGeneratorViewController controller) + { + _controller = controller; + } + + public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) + { + if(indexPath.Section == 0) + { + var cell = new UITableViewCell(); + cell.TextLabel.TextColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f); + if(indexPath.Row == 0) + { + cell.TextLabel.Text = AppResources.RegeneratePassword; + } + else if(indexPath.Row == 1) + { + cell.TextLabel.Text = AppResources.CopyPassword; + } + return cell; + } + + if(indexPath.Row == 0) + { + return _controller.LengthCell; + } + else if(indexPath.Row == 1) + { + return _controller.UppercaseCell; + } + else if(indexPath.Row == 2) + { + return _controller.LowercaseCell; + } + else if(indexPath.Row == 3) + { + return _controller.NumbersCell; + } + else if(indexPath.Row == 4) + { + return _controller.SpecialCell; + } + else if(indexPath.Row == 5) + { + return _controller.MinNumbersCell; + } + else if(indexPath.Row == 6) + { + return _controller.MinSpecialCell; + } + + return new UITableViewCell(); + } + + public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) + { + return UITableView.AutomaticDimension; + } + + public override nint NumberOfSections(UITableView tableView) + { + return 2; + } + + public override nint RowsInSection(UITableView tableview, nint section) + { + if(section == 0) + { + return 2; + } + + return 7; + } + + public override nfloat GetHeightForHeader(UITableView tableView, nint section) + { + if(section == 0) + { + return 0.00001f; + } + + return UITableView.AutomaticDimension; + } + + public override UIView GetViewForHeader(UITableView tableView, nint section) + { + if(section == 0) + { + return new UIView(CGRect.Empty) + { + Hidden = true + }; + } + + return null; + } + + public override string TitleForHeader(UITableView tableView, nint section) + { + if(section == 1) + { + return AppResources.Options; + } + + return null; + } + + public override string TitleForFooter(UITableView tableView, nint section) + { + if(section == 1) + { + return AppResources.OptionDefaults; + } + + return null; + } + + public override void RowSelected(UITableView tableView, NSIndexPath indexPath) + { + if(indexPath.Section == 0) + { + if(indexPath.Row == 0) + { + _controller.GoogleAnalyticsService.TrackExtensionEvent("RegeneratedPassword"); + _controller.GeneratePassword(); + } + else if(indexPath.Row == 1) + { + _controller.GoogleAnalyticsService.TrackExtensionEvent("CopiedGeneratedPassword"); + UIPasteboard clipboard = UIPasteboard.General; + clipboard.String = _controller.BasePasswordLabel.Text; + var alert = Dialogs.CreateMessageAlert(AppResources.Copied); + _controller.PresentViewController(alert, true, () => + { + _controller.DismissViewController(true, null); + }); + } + } + + tableView.DeselectRow(indexPath, true); + tableView.EndEditing(true); + } + + public NSDate DateTimeToNSDate(DateTime date) + { + DateTime reference = TimeZone.CurrentTimeZone.ToLocalTime( + new DateTime(2001, 1, 1, 0, 0, 0)); + return NSDate.FromTimeIntervalSinceReferenceDate( + (date - reference).TotalSeconds); + } + } + } +} diff --git a/src/iOS.Core/Models/AppExtensionContext.cs b/src/iOS.Core/Models/AppExtensionContext.cs new file mode 100644 index 000000000..f68f1ef5e --- /dev/null +++ b/src/iOS.Core/Models/AppExtensionContext.cs @@ -0,0 +1,47 @@ +using System; + +namespace Bit.iOS.Core.Models +{ + public class AppExtensionContext + { + private string _uriString; + + public Uri Uri + { + get + { + Uri uri; + if(string.IsNullOrWhiteSpace(UrlString) || !Uri.TryCreate(UrlString, UriKind.Absolute, out uri)) + { + return null; + } + + return uri; + } + } + public string UrlString + { + get + { + return _uriString; + } + set + { + _uriString = value; + if(_uriString != null && !_uriString.StartsWith(App.Constants.iOSAppProtocol) && _uriString.Contains(".")) + { + if(!_uriString.Contains("://") && !_uriString.Contains(" ")) + { + _uriString = string.Concat("http://", _uriString); + } + } + + if(!_uriString.StartsWith("http") && !_uriString.StartsWith(App.Constants.iOSAppProtocol)) + { + _uriString = string.Concat(App.Constants.iOSAppProtocol, _uriString); + } + } + } + public PasswordGenerationOptions PasswordOptions { get; set; } + } +} \ No newline at end of file diff --git a/src/iOS.Extension/Models/PasswordGenerationOptions.cs b/src/iOS.Core/Models/PasswordGenerationOptions.cs similarity index 90% rename from src/iOS.Extension/Models/PasswordGenerationOptions.cs rename to src/iOS.Core/Models/PasswordGenerationOptions.cs index 0f897a78b..4c2aa0c8b 100644 --- a/src/iOS.Extension/Models/PasswordGenerationOptions.cs +++ b/src/iOS.Core/Models/PasswordGenerationOptions.cs @@ -1,6 +1,6 @@ using System; -namespace Bit.iOS.Extension.Models +namespace Bit.iOS.Core.Models { public class PasswordGenerationOptions { diff --git a/src/iOS.Core/iOS.Core.csproj b/src/iOS.Core/iOS.Core.csproj index 0aba39c58..2624a0daf 100644 --- a/src/iOS.Core/iOS.Core.csproj +++ b/src/iOS.Core/iOS.Core.csproj @@ -38,13 +38,19 @@ - + + + + + + + diff --git a/src/iOS.Extension/LoadingViewController.cs b/src/iOS.Extension/LoadingViewController.cs index 75e907195..7a2e0f59d 100644 --- a/src/iOS.Extension/LoadingViewController.cs +++ b/src/iOS.Extension/LoadingViewController.cs @@ -21,6 +21,7 @@ using Bit.iOS.Core.Controllers; using SimpleInjector; using XLabs.Ioc.SimpleInjectorContainer; using System.Collections.Generic; +using Bit.iOS.Core.Models; namespace Bit.iOS.Extension { @@ -147,17 +148,14 @@ namespace Bit.iOS.Extension } else if(fingerprintViewController != null) { - fingerprintViewController.Context = _context; fingerprintViewController.LoadingController = this; } else if(pinViewController != null) { - pinViewController.Context = _context; pinViewController.LoadingController = this; } else if(passwordViewController != null) { - passwordViewController.Context = _context; passwordViewController.LoadingController = this; } else if(setupViewController != null) diff --git a/src/iOS.Extension/LockFingerprintViewController.cs b/src/iOS.Extension/LockFingerprintViewController.cs index 19614d9a1..a5a81466a 100644 --- a/src/iOS.Extension/LockFingerprintViewController.cs +++ b/src/iOS.Extension/LockFingerprintViewController.cs @@ -1,68 +1,19 @@ using System; -using Bit.iOS.Extension.Models; using UIKit; -using XLabs.Ioc; -using Plugin.Fingerprint.Abstractions; -using System.Threading.Tasks; -using Bit.iOS.Core.Controllers; -using Bit.App.Resources; -using Bit.App.Abstractions; namespace Bit.iOS.Extension { - public partial class LockFingerprintViewController : ExtendedUIViewController + public partial class LockFingerprintViewController : Core.Controllers.LockFingerprintViewController { - private IAppSettingsService _appSettingsService; - private IFingerprint _fingerprint; - private IDeviceInfoService _deviceInfo; - public LockFingerprintViewController(IntPtr handle) : base(handle) { } - public Context Context { get; set; } public LoadingViewController LoadingController { get; set; } - - public override void ViewWillAppear(bool animated) - { - UINavigationBar.Appearance.ShadowImage = new UIImage(); - UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); - base.ViewWillAppear(animated); - } - - public override void ViewDidLoad() - { - _appSettingsService = Resolver.Resolve(); - _fingerprint = Resolver.Resolve(); - _deviceInfo = Resolver.Resolve(); - - NavItem.Title = _deviceInfo.HasFaceIdSupport ? AppResources.VerifyFaceID : AppResources.VerifyFingerprint; - CancelButton.Title = AppResources.Cancel; - View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); - - UseButton.SetTitle(_deviceInfo.HasFaceIdSupport ? AppResources.UseFaceIDToUnlock : - AppResources.UseFingerprintToUnlock, UIControlState.Normal); - var descriptor = UIFontDescriptor.PreferredBody; - UseButton.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize); - UseButton.BackgroundColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f); - UseButton.TintColor = UIColor.White; - UseButton.TouchUpInside += UseButton_TouchUpInside; - - FingerprintButton.SetImage(new UIImage(_deviceInfo.HasFaceIdSupport ? "smile.png" : "fingerprint.png"), - UIControlState.Normal); - - base.ViewDidLoad(); - } - - private void UseButton_TouchUpInside(object sender, EventArgs e) - { - var task = CheckFingerprintAsync(); - } - - public override void ViewDidAppear(bool animated) - { - base.ViewDidAppear(animated); - var task = CheckFingerprintAsync(); - } + public override UINavigationItem BaseNavItem => NavItem; + public override UIBarButtonItem BaseCancelButton => CancelButton; + public override UIButton BaseUseButton => UseButton; + public override UIButton BaseFingerprintButton => FingerprintButton; + public override Action Success => () => LoadingController.DismissLockAndContinue(); partial void CancelButton_Activated(UIBarButtonItem sender) { @@ -73,22 +24,5 @@ namespace Bit.iOS.Extension { var task = CheckFingerprintAsync(); } - - public async Task CheckFingerprintAsync() - { - var fingerprintRequest = new AuthenticationRequestConfiguration( - _deviceInfo.HasFaceIdSupport ? AppResources.FaceIDDirection : AppResources.FingerprintDirection) - { - AllowAlternativeAuthentication = true, - CancelTitle = AppResources.Cancel, - FallbackTitle = AppResources.LogOut - }; - var result = await _fingerprint.AuthenticateAsync(fingerprintRequest); - if(result.Authenticated) - { - _appSettingsService.Locked = false; - LoadingController.DismissLockAndContinue(); - } - } } } diff --git a/src/iOS.Extension/LockPasswordViewController.cs b/src/iOS.Extension/LockPasswordViewController.cs index 1a47a6e24..d51531591 100644 --- a/src/iOS.Extension/LockPasswordViewController.cs +++ b/src/iOS.Extension/LockPasswordViewController.cs @@ -1,186 +1,27 @@ using System; -using Bit.iOS.Extension.Models; using UIKit; -using XLabs.Ioc; -using Foundation; -using Bit.iOS.Core.Views; -using Bit.App.Resources; -using Bit.iOS.Core.Utilities; -using Bit.App.Abstractions; -using System.Linq; -using Bit.iOS.Core.Controllers; namespace Bit.iOS.Extension { - public partial class LockPasswordViewController : ExtendedUITableViewController + public partial class LockPasswordViewController : Core.Controllers.LockPasswordViewController { - private IAppSettingsService _appSettingsService; - private IAuthService _authService; - private ICryptoService _cryptoService; - public LockPasswordViewController(IntPtr handle) : base(handle) { } - public Context Context { get; set; } public LoadingViewController LoadingController { get; set; } - public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell( - AppResources.MasterPassword, useLabelAsPlaceholder: true); - - public override void ViewWillAppear(bool animated) - { - UINavigationBar.Appearance.ShadowImage = new UIImage(); - UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); - base.ViewWillAppear(animated); - } - - public override void ViewDidLoad() - { - _appSettingsService = Resolver.Resolve(); - _authService = Resolver.Resolve(); - _cryptoService = Resolver.Resolve(); - - NavItem.Title = AppResources.VerifyMasterPassword; - CancelButton.Title = AppResources.Cancel; - SubmitButton.Title = AppResources.Submit; - 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(); - } + public override UINavigationItem BaseNavItem => NavItem; + public override UIBarButtonItem BaseCancelButton => CancelButton; + public override UIBarButtonItem BaseSubmitButton => SubmitButton; + public override Action Success => () => LoadingController.DismissLockAndContinue(); 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, - _authService.Kdf, _authService.KdfIterations); - if(key.Key.SequenceEqual(_cryptoService.Key.Key)) - { - _appSettingsService.Locked = false; - MasterPasswordCell.TextField.ResignFirstResponder(); - LoadingController.DismissLockAndContinue(); - } - else - { - // TODO: keep track of invalid attempts and logout? - - var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred, - string.Format(null, AppResources.InvalidMasterPassword), AppResources.Ok, (a) => - { - - MasterPasswordCell.TextField.Text = string.Empty; - MasterPasswordCell.TextField.BecomeFirstResponder(); - }); - - PresentViewController(alert, true, null); - } - } - partial void CancelButton_Activated(UIBarButtonItem sender) { LoadingController.CompleteRequest(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/LockPinViewController.cs b/src/iOS.Extension/LockPinViewController.cs index 332b1f848..2737135be 100644 --- a/src/iOS.Extension/LockPinViewController.cs +++ b/src/iOS.Extension/LockPinViewController.cs @@ -1,102 +1,20 @@ using System; -using Bit.iOS.Extension.Models; using UIKit; -using XLabs.Ioc; -using Plugin.Settings.Abstractions; -using Bit.App.Abstractions; -using Bit.iOS.Core.Utilities; -using Bit.App.Resources; -using System.Diagnostics; -using Bit.App; -using Bit.iOS.Core.Controllers; namespace Bit.iOS.Extension { - public partial class LockPinViewController : ExtendedUIViewController + public partial class LockPinViewController : Core.Controllers.LockPinViewController { - private IAppSettingsService _appSettingsService; - private IAuthService _authService; - public LockPinViewController(IntPtr handle) : base(handle) { } - public Context Context { get; set; } public LoadingViewController LoadingController { get; set; } - - public override void ViewWillAppear(bool animated) - { - UINavigationBar.Appearance.ShadowImage = new UIImage(); - UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); - base.ViewWillAppear(animated); - } - - public override void ViewDidLoad() - { - _appSettingsService = Resolver.Resolve(); - _authService = Resolver.Resolve(); - - NavItem.Title = AppResources.VerifyPIN; - CancelButton.Title = AppResources.Cancel; - View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); - - var descriptor = UIFontDescriptor.PreferredBody; - PinLabel.Font = UIFont.FromName("Menlo-Regular", 35); - - InstructionLabel.Text = AppResources.EnterPIN; - InstructionLabel.LineBreakMode = UILineBreakMode.WordWrap; - InstructionLabel.Lines = 0; - InstructionLabel.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize * 0.8f); - InstructionLabel.TextColor = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f); - - PinTextField.EditingChanged += PinTextField_EditingChanged; - - base.ViewDidLoad(); - } - - public override void ViewDidAppear(bool animated) - { - base.ViewDidAppear(animated); - PinTextField.BecomeFirstResponder(); - } - - private void PinTextField_EditingChanged(object sender, EventArgs e) - { - SetLabelText(); - - if(PinTextField.Text.Length >= 4) - { - if(PinTextField.Text == _authService.PIN) - { - Debug.WriteLine("BW Log, Start Dismiss PIN controller."); - _appSettingsService.Locked = false; - PinTextField.ResignFirstResponder(); - LoadingController.DismissLockAndContinue(); - } - else - { - // TODO: keep track of invalid attempts and logout? - - var alert = Dialogs.CreateAlert(null, AppResources.InvalidPIN, AppResources.Ok, (a) => - { - PinTextField.Text = string.Empty; - SetLabelText(); - PinTextField.BecomeFirstResponder(); - }); - PresentViewController(alert, true, null); - } - } - } - - private void SetLabelText() - { - var newText = string.Empty; - for(int i = 0; i < 4; i++) - { - newText += PinTextField.Text.Length <= i ? "- " : "• "; - } - - PinLabel.Text = newText.TrimEnd(); - } + public override UINavigationItem BaseNavItem => NavItem; + public override UIBarButtonItem BaseCancelButton => CancelButton; + public override UILabel BasePinLabel => PinLabel; + public override UILabel BaseInstructionLabel => InstructionLabel; + public override UITextField BasePinTextField => PinTextField; + public override Action Success => () => LoadingController.DismissLockAndContinue(); partial void CancelButton_Activated(UIBarButtonItem sender) { diff --git a/src/iOS.Extension/LoginAddViewController.cs b/src/iOS.Extension/LoginAddViewController.cs index 34e463f70..acaa68a7a 100644 --- a/src/iOS.Extension/LoginAddViewController.cs +++ b/src/iOS.Extension/LoginAddViewController.cs @@ -1,118 +1,33 @@ using System; -using System.Collections.Generic; -using System.Linq; -using Bit.App.Abstractions; -using Bit.App.Models; -using Bit.App.Resources; -using Bit.iOS.Core.Views; -using Bit.iOS.Extension.Models; using Foundation; using UIKit; -using XLabs.Ioc; -using Bit.App; -using Plugin.Connectivity.Abstractions; -using Bit.iOS.Core.Utilities; -using Bit.iOS.Core.Controllers; namespace Bit.iOS.Extension { - public partial class LoginAddViewController : ExtendedUITableViewController + public partial class LoginAddViewController : Core.Controllers.LoginAddViewController { - private ICipherService _cipherService; - private IFolderService _folderService; - private IConnectivity _connectivity; - private IEnumerable _folders; - private IGoogleAnalyticsService _googleAnalyticsService; - public LoginAddViewController(IntPtr handle) : base(handle) { } - public Context Context { get; set; } public LoginListViewController LoginListController { get; set; } public LoadingViewController LoadingController { get; set; } - public FormEntryTableViewCell NameCell { get; set; } = new FormEntryTableViewCell(AppResources.Name); - public FormEntryTableViewCell UsernameCell { get; set; } = new FormEntryTableViewCell(AppResources.Username); - public FormEntryTableViewCell PasswordCell { get; set; } = new FormEntryTableViewCell(AppResources.Password); - public UITableViewCell GeneratePasswordCell { get; set; } = new UITableViewCell(UITableViewCellStyle.Subtitle, "GeneratePasswordCell"); - public FormEntryTableViewCell UriCell { get; set; } = new FormEntryTableViewCell(AppResources.URI); - public SwitchTableViewCell FavoriteCell { get; set; } = new SwitchTableViewCell(AppResources.Favorite); - public FormEntryTableViewCell NotesCell { get; set; } = new FormEntryTableViewCell(useTextView: true, height: 180); - public PickerTableViewCell FolderCell { get; set; } = new PickerTableViewCell(AppResources.Folder); - public override void ViewWillAppear(bool animated) + public override UINavigationItem BaseNavItem => NavItem; + public override UIBarButtonItem BaseCancelButton => CancelBarButton; + public override UIBarButtonItem BaseSaveButton => SaveBarButton; + + public override Action Success => () => { - UINavigationBar.Appearance.ShadowImage = new UIImage(); - UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); - base.ViewWillAppear(animated); - } - - public override void ViewDidLoad() - { - _cipherService = Resolver.Resolve(); - _connectivity = Resolver.Resolve(); - _folderService = Resolver.Resolve(); - _googleAnalyticsService = Resolver.Resolve(); - - NavItem.Title = AppResources.AddItem; - CancelBarButton.Title = AppResources.Cancel; - SaveBarButton.Title = AppResources.Save; - View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); - - NameCell.TextField.Text = Context?.Uri?.Host ?? string.Empty; - NameCell.TextField.ReturnKeyType = UIReturnKeyType.Next; - NameCell.TextField.ShouldReturn += (UITextField tf) => + if(LoginListController != null) { - UsernameCell.TextField.BecomeFirstResponder(); - return true; - }; - - UsernameCell.TextField.AutocapitalizationType = UITextAutocapitalizationType.None; - UsernameCell.TextField.AutocorrectionType = UITextAutocorrectionType.No; - UsernameCell.TextField.SpellCheckingType = UITextSpellCheckingType.No; - UsernameCell.TextField.ReturnKeyType = UIReturnKeyType.Next; - UsernameCell.TextField.ShouldReturn += (UITextField tf) => + LoginListController.DismissModal(); + } + else if(LoadingController != null) { - PasswordCell.TextField.BecomeFirstResponder(); - return true; - }; - - PasswordCell.TextField.SecureTextEntry = true; - PasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Next; - PasswordCell.TextField.ShouldReturn += (UITextField tf) => - { - UriCell.TextField.BecomeFirstResponder(); - return true; - }; - - GeneratePasswordCell.TextLabel.Text = AppResources.GeneratePassword; - GeneratePasswordCell.Accessory = UITableViewCellAccessory.DisclosureIndicator; - - UriCell.TextField.Text = Context?.UrlString ?? string.Empty; - UriCell.TextField.KeyboardType = UIKeyboardType.Url; - UriCell.TextField.ReturnKeyType = UIReturnKeyType.Next; - UriCell.TextField.ShouldReturn += (UITextField tf) => - { - NotesCell.TextView.BecomeFirstResponder(); - return true; - }; - - _folders = _folderService.GetAllAsync().GetAwaiter().GetResult(); - var folderNames = _folders.Select(s => s.Name.Decrypt()).OrderBy(s => s).ToList(); - folderNames.Insert(0, AppResources.FolderNone); - FolderCell.Items = folderNames; - - 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); - } + LoadingController.CompleteUsernamePasswordRequest(UsernameCell.TextField.Text, + PasswordCell.TextField.Text, null, null); + } + }; partial void CancelBarButton_Activated(UIBarButtonItem sender) { @@ -128,77 +43,7 @@ namespace Bit.iOS.Extension async partial void SaveBarButton_Activated(UIBarButtonItem sender) { - if(!_connectivity.IsConnected) - { - AlertNoConnection(); - return; - } - - if(string.IsNullOrWhiteSpace(PasswordCell.TextField.Text)) - { - DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Password), AppResources.Ok); - return; - } - - if(string.IsNullOrWhiteSpace(NameCell.TextField.Text)) - { - DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Name), AppResources.Ok); - return; - } - - var cipher = new Cipher - { - Name = string.IsNullOrWhiteSpace(NameCell.TextField.Text) ? null : NameCell.TextField.Text.Encrypt(), - Notes = string.IsNullOrWhiteSpace(NotesCell.TextView.Text) ? null : NotesCell.TextView.Text.Encrypt(), - Favorite = FavoriteCell.Switch.On, - FolderId = FolderCell.SelectedIndex == 0 ? null : _folders.ElementAtOrDefault(FolderCell.SelectedIndex - 1)?.Id, - Type = App.Enums.CipherType.Login, - Login = new Login - { - Uris = null, - Username = string.IsNullOrWhiteSpace(UsernameCell.TextField.Text) ? null : UsernameCell.TextField.Text.Encrypt(), - Password = string.IsNullOrWhiteSpace(PasswordCell.TextField.Text) ? null : PasswordCell.TextField.Text.Encrypt() - } - }; - - if(!string.IsNullOrWhiteSpace(UriCell.TextField.Text)) - { - cipher.Login.Uris = new List - { - new LoginUri - { - Uri = UriCell.TextField.Text.Encrypt() - } - }; - } - - var saveTask = _cipherService.SaveAsync(cipher); - var loadingAlert = Dialogs.CreateLoadingAlert(AppResources.Saving); - PresentViewController(loadingAlert, true, null); - await saveTask; - - await loadingAlert.DismissViewControllerAsync(true); - if(saveTask.Result.Succeeded) - { - _googleAnalyticsService.TrackExtensionEvent("CreatedLogin"); - if(LoginListController != null) - { - LoginListController.DismissModal(); - } - else if(LoadingController != null) - { - LoadingController.CompleteUsernamePasswordRequest(UsernameCell.TextField.Text, PasswordCell.TextField.Text, - null, null); - } - } - else if(saveTask.Result.Errors.Count() > 0) - { - DisplayAlert(AppResources.AnErrorHasOccurred, saveTask.Result.Errors.First().Message, AppResources.Ok); - } - else - { - DisplayAlert(null, AppResources.AnErrorHasOccurred, AppResources.Ok); - } + await this.SaveAsync(); } public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender) @@ -209,147 +54,10 @@ namespace Bit.iOS.Extension var passwordGeneratorController = navController.TopViewController as PasswordGeneratorViewController; if(passwordGeneratorController != null) { - passwordGeneratorController.Context = Context; + passwordGeneratorController.PasswordOptions = Context.PasswordOptions; passwordGeneratorController.Parent = this; } } } - - public void DisplayAlert(string title, string message, string accept) - { - var alert = Dialogs.CreateAlert(title, message, accept); - PresentViewController(alert, true, null); - } - - private void AlertNoConnection() - { - DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, AppResources.Ok); - } - - public class TableSource : UITableViewSource - { - private LoginAddViewController _controller; - - public TableSource(LoginAddViewController controller) - { - _controller = controller; - } - - public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) - { - if(indexPath.Section == 0) - { - if(indexPath.Row == 0) - { - return _controller.NameCell; - } - else if(indexPath.Row == 1) - { - return _controller.UsernameCell; - } - else if(indexPath.Row == 2) - { - return _controller.PasswordCell; - } - else if(indexPath.Row == 3) - { - return _controller.GeneratePasswordCell; - } - } - else if(indexPath.Section == 1) - { - return _controller.UriCell; - } - else if(indexPath.Section == 2) - { - if(indexPath.Row == 0) - { - return _controller.FolderCell; - } - else if(indexPath.Row == 1) - { - return _controller.FavoriteCell; - } - } - else if(indexPath.Section == 3) - { - return _controller.NotesCell; - } - - return new UITableViewCell(); - } - - public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) - { - return UITableView.AutomaticDimension; - } - - public override nint NumberOfSections(UITableView tableView) - { - return 4; - } - - public override nint RowsInSection(UITableView tableview, nint section) - { - if(section == 0) - { - return 4; - } - else if(section == 1) - { - return 1; - } - else if(section == 2) - { - return 2; - } - else - { - return 1; - } - } - - public override nfloat GetHeightForHeader(UITableView tableView, nint section) - { - return section == 0 || section == 3 ? UITableView.AutomaticDimension : 0.00001f; - } - - public override string TitleForHeader(UITableView tableView, nint section) - { - if(section == 0) - { - return AppResources.ItemInformation; - } - else if(section == 3) - { - return AppResources.Notes; - } - - return string.Empty; - } - - public override void RowSelected(UITableView tableView, NSIndexPath indexPath) - { - tableView.DeselectRow(indexPath, true); - tableView.EndEditing(true); - - if(indexPath.Section == 0 && indexPath.Row == 3) - { - _controller.PerformSegue("passwordGeneratorSegue", this); - } - - 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/Models/Context.cs b/src/iOS.Extension/Models/Context.cs index 992388630..94015819e 100644 --- a/src/iOS.Extension/Models/Context.cs +++ b/src/iOS.Extension/Models/Context.cs @@ -1,57 +1,19 @@ -using System; -using Foundation; -using Bit.App; +using Foundation; +using Bit.iOS.Core.Models; namespace Bit.iOS.Extension.Models { - public class Context + public class Context : AppExtensionContext { private string _uriString; public NSExtensionContext ExtContext { get; set; } public string ProviderType { get; set; } - public Uri Uri - { - get - { - Uri uri; - if(string.IsNullOrWhiteSpace(UrlString) || !Uri.TryCreate(UrlString, UriKind.Absolute, out uri)) - { - return null; - } - - return uri; - } - } - public string UrlString - { - get - { - return _uriString; - } - set - { - _uriString = value; - if(_uriString != null && !_uriString.StartsWith(Constants.iOSAppProtocol) && _uriString.Contains(".")) - { - if(!_uriString.Contains("://") && !_uriString.Contains(" ")) - { - _uriString = string.Concat("http://", _uriString); - } - } - - if(!_uriString.StartsWith("http") && !_uriString.StartsWith(Constants.iOSAppProtocol)) - { - _uriString = string.Concat(Constants.iOSAppProtocol, _uriString); - } - } - } public string LoginTitle { get; set; } public string Username { get; set; } public string Password { get; set; } public string OldPassword { get; set; } public string Notes { get; set; } - public PasswordGenerationOptions PasswordOptions { get; set; } public PageDetails Details { get; set; } } } diff --git a/src/iOS.Extension/PasswordGeneratorViewController.cs b/src/iOS.Extension/PasswordGeneratorViewController.cs index 5cc1e1609..36f3b99aa 100644 --- a/src/iOS.Extension/PasswordGeneratorViewController.cs +++ b/src/iOS.Extension/PasswordGeneratorViewController.cs @@ -1,171 +1,22 @@ using System; -using System.Linq; -using Bit.App.Abstractions; -using Bit.iOS.Core.Views; -using Bit.iOS.Extension.Models; -using Foundation; using UIKit; -using XLabs.Ioc; -using Plugin.Settings.Abstractions; -using CoreGraphics; -using Bit.App; -using Bit.iOS.Core.Utilities; -using Bit.iOS.Core.Controllers; -using Bit.App.Resources; namespace Bit.iOS.Extension { - public partial class PasswordGeneratorViewController : ExtendedUIViewController + public partial class PasswordGeneratorViewController : Core.Controllers.PasswordGeneratorViewController { - private IPasswordGenerationService _passwordGenerationService; - private ISettings _settings; - private IGoogleAnalyticsService _googleAnalyticsService; - public PasswordGeneratorViewController(IntPtr handle) : base(handle) { } - - public Context Context { get; set; } + public LoginAddViewController Parent { get; set; } - public UITableViewController OptionsTableViewController { get; set; } - public SwitchTableViewCell UppercaseCell { 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 SpecialCell { get; set; } = new SwitchTableViewCell("!@#$%^&*"); - public StepperTableViewCell MinNumbersCell { get; set; } = new StepperTableViewCell(AppResources.MinNumbers, 1, 0, 5, 1); - public StepperTableViewCell MinSpecialCell { get; set; } = new StepperTableViewCell(AppResources.MinSpecial, 1, 0, 5, 1); - public SliderTableViewCell LengthCell { get; set; } = new SliderTableViewCell(AppResources.Length, 10, 5, 64); - - public override void ViewWillAppear(bool animated) - { - UINavigationBar.Appearance.ShadowImage = new UIImage(); - UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); - base.ViewWillAppear(animated); - } - - public override void ViewDidLoad() - { - _passwordGenerationService = Resolver.Resolve(); - _settings = Resolver.Resolve(); - _googleAnalyticsService = Resolver.Resolve(); - - NavItem.Title = AppResources.PasswordGenerator; - CancelBarButton.Title = AppResources.Cancel; - SelectBarButton.Title = AppResources.Select; - View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); - - var descriptor = UIFontDescriptor.PreferredBody; - PasswordLabel.Font = UIFont.FromName("Menlo-Regular", descriptor.PointSize * 1.3f); - PasswordLabel.LineBreakMode = UILineBreakMode.TailTruncation; - PasswordLabel.Lines = 1; - PasswordLabel.AdjustsFontSizeToFitWidth = false; - - var controller = ChildViewControllers.LastOrDefault(); - if(controller != null) - { - OptionsTableViewController = controller as UITableViewController; - } - - if(OptionsTableViewController != null) - { - OptionsTableViewController.TableView.RowHeight = UITableView.AutomaticDimension; - OptionsTableViewController.TableView.EstimatedRowHeight = 70; - OptionsTableViewController.TableView.Source = new TableSource(this); - OptionsTableViewController.TableView.AllowsSelection = true; - OptionsTableViewController.View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); - } - - UppercaseCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, true); - LowercaseCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, true); - SpecialCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, true); - NumbersCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, true); - MinNumbersCell.Value = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, 1); - MinSpecialCell.Value = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, 1); - LengthCell.Value = _settings.GetValueOrDefault(Constants.PasswordGeneratorLength, 10); - - UppercaseCell.ValueChanged += Options_ValueChanged; - LowercaseCell.ValueChanged += Options_ValueChanged; - NumbersCell.ValueChanged += Options_ValueChanged; - SpecialCell.ValueChanged += Options_ValueChanged; - MinNumbersCell.ValueChanged += Options_ValueChanged; - MinSpecialCell.ValueChanged += Options_ValueChanged; - LengthCell.ValueChanged += Options_ValueChanged; - - // Adjust based on context password options - if(Context.PasswordOptions != null) - { - if(Context.PasswordOptions.RequireDigits) - { - NumbersCell.Switch.On = true; - NumbersCell.Switch.Enabled = false; - - if(MinNumbersCell.Value < 1) - { - MinNumbersCell.Value = 1; - } - - MinNumbersCell.Stepper.MinimumValue = 1; - } - - if(Context.PasswordOptions.RequireSymbols) - { - SpecialCell.Switch.On = true; - SpecialCell.Switch.Enabled = false; - - if(MinSpecialCell.Value < 1) - { - MinSpecialCell.Value = 1; - } - - MinSpecialCell.Stepper.MinimumValue = 1; - } - - if(Context.PasswordOptions.MinLength < Context.PasswordOptions.MaxLength) - { - if(Context.PasswordOptions.MinLength > 0 && Context.PasswordOptions.MinLength > LengthCell.Slider.MinValue) - { - if(LengthCell.Value < Context.PasswordOptions.MinLength) - { - LengthCell.Slider.Value = Context.PasswordOptions.MinLength; - } - - LengthCell.Slider.MinValue = Context.PasswordOptions.MinLength; - } - - if(Context.PasswordOptions.MaxLength > 5 && Context.PasswordOptions.MaxLength < LengthCell.Slider.MaxValue) - { - if(LengthCell.Value > Context.PasswordOptions.MaxLength) - { - LengthCell.Slider.Value = Context.PasswordOptions.MaxLength; - } - - LengthCell.Slider.MaxValue = Context.PasswordOptions.MaxLength; - } - } - } - - GeneratePassword(); - _googleAnalyticsService.TrackExtensionEvent("GeneratedPassword"); - base.ViewDidLoad(); - } - - private void Options_ValueChanged(object sender, EventArgs e) - { - if(InvalidState()) - { - LowercaseCell.Switch.On = true; - } - - GeneratePassword(); - } - - private bool InvalidState() - { - return !LowercaseCell.Switch.On && !UppercaseCell.Switch.On && !NumbersCell.Switch.On && !SpecialCell.Switch.On; - } + public override UINavigationItem BaseNavItem => NavItem; + public override UIBarButtonItem BaseCancelButton => CancelBarButton; + public override UIBarButtonItem BaseSelectBarButton => SelectBarButton; + public override UILabel BasePasswordLabel => PasswordLabel; partial void SelectBarButton_Activated(UIBarButtonItem sender) { - _googleAnalyticsService.TrackExtensionEvent("SelectedGeneratedPassword"); + GoogleAnalyticsService.TrackExtensionEvent("SelectedGeneratedPassword"); DismissViewController(true, () => { Parent.PasswordCell.TextField.Text = PasswordLabel.Text; @@ -176,173 +27,5 @@ namespace Bit.iOS.Extension { DismissViewController(true, null); } - - private void GeneratePassword() - { - PasswordLabel.Text = _passwordGenerationService.GeneratePassword( - length: LengthCell.Value, - uppercase: UppercaseCell.Switch.On, - lowercase: LowercaseCell.Switch.On, - numbers: NumbersCell.Switch.On, - special: SpecialCell.Switch.On, - minSpecial: MinSpecialCell.Value, - minNumbers: MinNumbersCell.Value); - } - - public class TableSource : UITableViewSource - { - private PasswordGeneratorViewController _controller; - - public TableSource(PasswordGeneratorViewController controller) - { - _controller = controller; - } - - public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) - { - if(indexPath.Section == 0) - { - var cell = new UITableViewCell(); - cell.TextLabel.TextColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f); - if(indexPath.Row == 0) - { - cell.TextLabel.Text = AppResources.RegeneratePassword; - } - else if(indexPath.Row == 1) - { - cell.TextLabel.Text = AppResources.CopyPassword; - } - return cell; - } - - if(indexPath.Row == 0) - { - return _controller.LengthCell; - } - else if(indexPath.Row == 1) - { - return _controller.UppercaseCell; - } - else if(indexPath.Row == 2) - { - return _controller.LowercaseCell; - } - else if(indexPath.Row == 3) - { - return _controller.NumbersCell; - } - else if(indexPath.Row == 4) - { - return _controller.SpecialCell; - } - else if(indexPath.Row == 5) - { - return _controller.MinNumbersCell; - } - else if(indexPath.Row == 6) - { - return _controller.MinSpecialCell; - } - - return new UITableViewCell(); - } - - public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) - { - return UITableView.AutomaticDimension; - } - - public override nint NumberOfSections(UITableView tableView) - { - return 2; - } - - public override nint RowsInSection(UITableView tableview, nint section) - { - if(section == 0) - { - return 2; - } - - return 7; - } - - public override nfloat GetHeightForHeader(UITableView tableView, nint section) - { - if(section == 0) - { - return 0.00001f; - } - - return UITableView.AutomaticDimension; - } - - public override UIView GetViewForHeader(UITableView tableView, nint section) - { - if(section == 0) - { - return new UIView(CGRect.Empty) - { - Hidden = true - }; - } - - return null; - } - - public override string TitleForHeader(UITableView tableView, nint section) - { - if(section == 1) - { - return AppResources.Options; - } - - return null; - } - - public override string TitleForFooter(UITableView tableView, nint section) - { - if(section == 1) - { - return AppResources.OptionDefaults; - } - - return null; - } - - public override void RowSelected(UITableView tableView, NSIndexPath indexPath) - { - if(indexPath.Section == 0) - { - if(indexPath.Row == 0) - { - _controller._googleAnalyticsService.TrackExtensionEvent("RegeneratedPassword"); - _controller.GeneratePassword(); - } - else if(indexPath.Row == 1) - { - _controller._googleAnalyticsService.TrackExtensionEvent("CopiedGeneratedPassword"); - UIPasteboard clipboard = UIPasteboard.General; - clipboard.String = _controller.PasswordLabel.Text; - var alert = Dialogs.CreateMessageAlert(AppResources.Copied); - _controller.PresentViewController(alert, true, () => - { - _controller.DismissViewController(true, null); - }); - } - } - - tableView.DeselectRow(indexPath, true); - tableView.EndEditing(true); - } - - public NSDate DateTimeToNSDate(DateTime date) - { - DateTime reference = TimeZone.CurrentTimeZone.ToLocalTime( - new DateTime(2001, 1, 1, 0, 0, 0)); - return NSDate.FromTimeIntervalSinceReferenceDate( - (date - reference).TotalSeconds); - } - } } } diff --git a/src/iOS.Extension/iOS.Extension.csproj b/src/iOS.Extension/iOS.Extension.csproj index 05cf5de9c..352a99818 100644 --- a/src/iOS.Extension/iOS.Extension.csproj +++ b/src/iOS.Extension/iOS.Extension.csproj @@ -232,7 +232,6 @@ - LoadingViewController.cs