1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-14 23:33:34 +00:00

ios autofill extension implemented

This commit is contained in:
Kyle Spearrin
2019-06-28 08:21:44 -04:00
parent be4ae605a9
commit f237fa98d2
22 changed files with 1865 additions and 206 deletions

View File

@@ -0,0 +1,260 @@
using AuthenticationServices;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.iOS.Autofill.Models;
using Bit.iOS.Core.Utilities;
using Foundation;
using System;
using System.Threading.Tasks;
using UIKit;
namespace Bit.iOS.Autofill
{
public partial class CredentialProviderViewController : ASCredentialProviderViewController
{
private Context _context;
public CredentialProviderViewController(IntPtr handle)
: base(handle)
{ }
public override void ViewDidLoad()
{
InitApp();
base.ViewDidLoad();
_context = new Context
{
ExtContext = ExtensionContext
};
}
public override void PrepareCredentialList(ASCredentialServiceIdentifier[] serviceIdentifiers)
{
_context.ServiceIdentifiers = serviceIdentifiers;
if(serviceIdentifiers.Length > 0)
{
var uri = serviceIdentifiers[0].Identifier;
if(serviceIdentifiers[0].Type == ASCredentialServiceIdentifierType.Domain)
{
uri = string.Concat("https://", uri);
}
_context.UrlString = uri;
}
if(!CheckAuthed())
{
return;
}
if(IsLocked())
{
PerformSegue("lockPasswordSegue", this);
}
else
{
if(_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
{
PerformSegue("loginSearchSegue", this);
}
else
{
PerformSegue("loginListSegue", this);
}
}
}
public override void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
{
if(!IsAuthed() || IsLocked())
{
var err = new NSError(new NSString("ASExtensionErrorDomain"),
Convert.ToInt32(ASExtensionErrorCode.UserInteractionRequired), null);
ExtensionContext.CancelRequest(err);
return;
}
_context.CredentialIdentity = credentialIdentity;
ProvideCredentialAsync().GetAwaiter().GetResult();
}
public override void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
{
if(!CheckAuthed())
{
return;
}
_context.CredentialIdentity = credentialIdentity;
CheckLock(async () => await ProvideCredentialAsync());
}
public override void PrepareInterfaceForExtensionConfiguration()
{
_context.Configuring = true;
if(!CheckAuthed())
{
return;
}
CheckLock(() => PerformSegue("setupSegue", this));
}
public void CompleteRequest(string username = null, string password = null, string totp = null)
{
if((_context?.Configuring ?? true) && string.IsNullOrWhiteSpace(password))
{
ExtensionContext?.CompleteExtensionConfigurationRequest();
return;
}
if(_context == null || string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{
var err = new NSError(new NSString("ASExtensionErrorDomain"),
Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null);
NSRunLoop.Main.BeginInvokeOnMainThread(() => ExtensionContext?.CancelRequest(err));
return;
}
if(!string.IsNullOrWhiteSpace(totp))
{
UIPasteboard.General.String = totp;
}
var cred = new ASPasswordCredential(username, password);
NSRunLoop.Main.BeginInvokeOnMainThread(() => ExtensionContext?.CompleteRequest(cred, null));
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
var navController = segue.DestinationViewController as UINavigationController;
if(navController != null)
{
var listLoginController = navController.TopViewController as LoginListViewController;
var listSearchController = navController.TopViewController as LoginSearchViewController;
var passwordViewController = navController.TopViewController as LockPasswordViewController;
var setupViewController = navController.TopViewController as SetupViewController;
if(listLoginController != null)
{
listLoginController.Context = _context;
listLoginController.CPViewController = this;
}
else if(listSearchController != null)
{
listSearchController.Context = _context;
listSearchController.CPViewController = this;
}
else if(passwordViewController != null)
{
passwordViewController.CPViewController = this;
}
else if(setupViewController != null)
{
setupViewController.CPViewController = this;
}
}
}
public void DismissLockAndContinue()
{
DismissViewController(false, async () =>
{
if(_context.CredentialIdentity != null)
{
await ProvideCredentialAsync();
return;
}
if(_context.Configuring)
{
PerformSegue("setupSegue", this);
return;
}
if(_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
{
PerformSegue("loginSearchSegue", this);
}
else
{
PerformSegue("loginListSegue", this);
}
});
}
private async Task ProvideCredentialAsync()
{
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
var cipher = await cipherService.GetAsync(_context.CredentialIdentity.RecordIdentifier);
if(cipher == null || cipher.Type != Bit.Core.Enums.CipherType.Login)
{
var err = new NSError(new NSString("ASExtensionErrorDomain"),
Convert.ToInt32(ASExtensionErrorCode.CredentialIdentityNotFound), null);
ExtensionContext.CancelRequest(err);
return;
}
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
var decCipher = await cipher.DecryptAsync();
string totpCode = null;
var disableTotpCopy = await storageService.GetAsync<bool?>(Bit.Core.Constants.DisableAutoTotpCopyKey);
if(!disableTotpCopy.GetValueOrDefault(false))
{
var userService = ServiceContainer.Resolve<IUserService>("userService");
var canAccessPremiumAsync = await userService.CanAccessPremiumAsync();
if(!string.IsNullOrWhiteSpace(decCipher.Login.Totp) &&
(canAccessPremiumAsync || cipher.OrganizationUseTotp))
{
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
totpCode = await totpService.GetCodeAsync(decCipher.Login.Totp);
}
}
CompleteRequest(decCipher.Login.Username, decCipher.Login.Password, totpCode);
}
private void CheckLock(Action notLockedAction)
{
if(IsLocked())
{
PerformSegue("lockPasswordSegue", this);
}
else
{
notLockedAction();
}
}
private bool CheckAuthed()
{
if(!IsAuthed())
{
var alert = Dialogs.CreateAlert(null, AppResources.MustLogInMainAppAutofill, AppResources.Ok, (a) =>
{
CompleteRequest();
});
PresentViewController(alert, true, null);
return false;
}
return true;
}
private bool IsLocked()
{
var lockService = ServiceContainer.Resolve<ILockService>("lockService");
return lockService.IsLockedAsync().GetAwaiter().GetResult();
}
private bool IsAuthed()
{
var userService = ServiceContainer.Resolve<IUserService>("userService");
return userService.IsAuthenticatedAsync().GetAwaiter().GetResult();
}
private void InitApp()
{
if(ServiceContainer.RegisteredServices.Count > 0)
{
return;
}
iOSCoreHelpers.RegisterLocalServices();
ServiceContainer.Init();
iOSCoreHelpers.RegisterHockeyApp();
iOSCoreHelpers.Bootstrap();
}
}
}

View File

@@ -0,0 +1,21 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("CredentialProviderViewController")]
partial class CredentialProviderViewController
{
void ReleaseDesignerOutlets ()
{
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using UIKit;
namespace Bit.iOS.Autofill
{
public partial class LockPasswordViewController : Core.Controllers.LockPasswordViewController
{
public LockPasswordViewController(IntPtr handle)
: base(handle)
{ }
public CredentialProviderViewController CPViewController { get; set; }
public override UINavigationItem BaseNavItem => NavItem;
public override UIBarButtonItem BaseCancelButton => CancelButton;
public override UIBarButtonItem BaseSubmitButton => SubmitButton;
public override Action Success => () => CPViewController.DismissLockAndContinue();
public override Action Cancel => () => CPViewController.CompleteRequest();
partial void SubmitButton_Activated(UIBarButtonItem sender)
{
var task = CheckPasswordAsync();
}
partial void CancelButton_Activated(UIBarButtonItem sender)
{
Cancel();
}
}
}

View File

@@ -0,0 +1,64 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("LockPasswordViewController")]
partial class LockPasswordViewController
{
[Outlet]
[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.UINavigationItem NavItem { 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 (NavItem != null) {
NavItem.Dispose ();
NavItem = null;
}
if (SubmitButton != null) {
SubmitButton.Dispose ();
SubmitButton = null;
}
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using Foundation;
using UIKit;
namespace Bit.iOS.Autofill
{
public partial class LoginAddViewController : Core.Controllers.LoginAddViewController
{
public LoginAddViewController(IntPtr handle)
: base(handle)
{ }
public LoginListViewController LoginListController { get; set; }
public LoginSearchViewController LoginSearchController { get; set; }
public override UINavigationItem BaseNavItem => NavItem;
public override UIBarButtonItem BaseCancelButton => CancelBarButton;
public override UIBarButtonItem BaseSaveButton => SaveBarButton;
public override Action Success => () =>
{
LoginListController?.DismissModal();
LoginSearchController?.DismissModal();
};
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
DismissViewController(true, null);
}
async partial void SaveBarButton_Activated(UIBarButtonItem sender)
{
await SaveAsync();
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
if(segue.DestinationViewController is UINavigationController navController)
{
if(navController.TopViewController is PasswordGeneratorViewController passwordGeneratorController)
{
passwordGeneratorController.PasswordOptions = Context.PasswordOptions;
passwordGeneratorController.Parent = this;
}
}
}
}
}

View File

@@ -0,0 +1,55 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("LoginAddViewController")]
partial class LoginAddViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem CancelBarButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UINavigationItem NavItem { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem SaveBarButton { get; set; }
[Action ("CancelBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("SaveBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void SaveBarButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets ()
{
if (CancelBarButton != null) {
CancelBarButton.Dispose ();
CancelBarButton = null;
}
if (NavItem != null) {
NavItem.Dispose ();
NavItem = null;
}
if (SaveBarButton != null) {
SaveBarButton.Dispose ();
SaveBarButton = null;
}
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using Bit.iOS.Autofill.Models;
using Foundation;
using UIKit;
using Bit.iOS.Core.Controllers;
using Bit.App.Resources;
using Bit.iOS.Core.Views;
using Bit.iOS.Autofill.Utilities;
namespace Bit.iOS.Autofill
{
public partial class LoginListViewController : ExtendedUITableViewController
{
public LoginListViewController(IntPtr handle)
: base(handle)
{ }
public Context Context { get; set; }
public CredentialProviderViewController CPViewController { get; set; }
public override void ViewWillAppear(bool animated)
{
UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
base.ViewWillAppear(animated);
}
public async override void ViewDidLoad()
{
base.ViewDidLoad();
NavItem.Title = AppResources.Items;
CancelBarButton.Title = AppResources.Cancel;
TableView.RowHeight = UITableView.AutomaticDimension;
TableView.EstimatedRowHeight = 44;
TableView.Source = new TableSource(this);
await ((TableSource)TableView.Source).LoadItemsAsync();
}
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
CPViewController.CompleteRequest();
}
partial void AddBarButton_Activated(UIBarButtonItem sender)
{
PerformSegue("loginAddSegue", this);
}
partial void SearchBarButton_Activated(UIBarButtonItem sender)
{
PerformSegue("loginSearchFromListSegue", this);
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
if(segue.DestinationViewController is UINavigationController navController)
{
if(navController.TopViewController is LoginAddViewController addLoginController)
{
addLoginController.Context = Context;
addLoginController.LoginListController = this;
}
if(navController.TopViewController is LoginSearchViewController searchLoginController)
{
searchLoginController.Context = Context;
searchLoginController.CPViewController = CPViewController;
}
}
}
public void DismissModal()
{
DismissViewController(true, async () =>
{
await ((TableSource)TableView.Source).LoadItemsAsync();
TableView.ReloadData();
});
}
public class TableSource : ExtensionTableSource
{
private Context _context;
private LoginListViewController _controller;
public TableSource(LoginListViewController controller)
: base(controller.Context, controller)
{
_context = controller.Context;
_controller = controller;
}
public async override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
await AutofillHelpers.TableRowSelectedAsync(tableView, indexPath, this,
_controller.CPViewController, _controller, "loginAddSegue");
}
}
}
}

View File

@@ -0,0 +1,59 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("LoginListViewController")]
partial class LoginListViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem AddBarButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem CancelBarButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UINavigationItem NavItem { get; set; }
[Action ("AddBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void AddBarButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("CancelBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("SearchBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void SearchBarButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets ()
{
if (AddBarButton != null) {
AddBarButton.Dispose ();
AddBarButton = null;
}
if (CancelBarButton != null) {
CancelBarButton.Dispose ();
CancelBarButton = null;
}
if (NavItem != null) {
NavItem.Dispose ();
NavItem = null;
}
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using Bit.iOS.Autofill.Models;
using Foundation;
using UIKit;
using Bit.iOS.Core.Controllers;
using Bit.App.Resources;
using Bit.iOS.Core.Views;
using Bit.iOS.Autofill.Utilities;
namespace Bit.iOS.Autofill
{
public partial class LoginSearchViewController : ExtendedUITableViewController
{
public LoginSearchViewController(IntPtr handle)
: base(handle)
{ }
public Context Context { get; set; }
public CredentialProviderViewController CPViewController { get; set; }
public override void ViewWillAppear(bool animated)
{
UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
base.ViewWillAppear(animated);
}
public async override void ViewDidLoad()
{
base.ViewDidLoad();
NavItem.Title = AppResources.SearchVault;
CancelBarButton.Title = AppResources.Cancel;
SearchBar.Placeholder = AppResources.Search;
TableView.RowHeight = UITableView.AutomaticDimension;
TableView.EstimatedRowHeight = 44;
TableView.Source = new TableSource(this);
SearchBar.Delegate = new ExtensionSearchDelegate(TableView);
await ((TableSource)TableView.Source).LoadItemsAsync(false, SearchBar.Text);
}
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
CPViewController.CompleteRequest();
}
partial void AddBarButton_Activated(UIBarButtonItem sender)
{
PerformSegue("loginAddFromSearchSegue", this);
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
if(segue.DestinationViewController is UINavigationController navController)
{
if(navController.TopViewController is LoginAddViewController addLoginController)
{
addLoginController.Context = Context;
addLoginController.LoginSearchController = this;
}
}
}
public void DismissModal()
{
DismissViewController(true, async () =>
{
await ((TableSource)TableView.Source).LoadItemsAsync(false, SearchBar.Text);
TableView.ReloadData();
});
}
public class TableSource : ExtensionTableSource
{
private Context _context;
private LoginSearchViewController _controller;
public TableSource(LoginSearchViewController controller)
: base(controller.Context, controller)
{
_context = controller.Context;
_controller = controller;
}
public async override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
await AutofillHelpers.TableRowSelectedAsync(tableView, indexPath, this,
_controller.CPViewController, _controller, "loginAddFromSearchSegue");
}
}
}
}

View File

@@ -0,0 +1,55 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("LoginSearchViewController")]
partial class LoginSearchViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem CancelBarButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UINavigationItem NavItem { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UISearchBar SearchBar { get; set; }
[Action ("AddBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void AddBarButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("CancelBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets ()
{
if (CancelBarButton != null) {
CancelBarButton.Dispose ();
CancelBarButton = null;
}
if (NavItem != null) {
NavItem.Dispose ();
NavItem = null;
}
if (SearchBar != null) {
SearchBar.Dispose ();
SearchBar = null;
}
}
}
}

View File

@@ -1,63 +1,528 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6154.17" systemVersion="13D65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="ObA-dk-sSI"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="43">
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6153.11"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Action View Controller - Image--> <!--Credential Provider View Controller-->
<scene sceneID="7MM-of-jgj"> <scene sceneID="42">
<objects> <objects>
<viewController title="Image" id="ObA-dk-sSI" customClass="ActionViewController" sceneMemberID="viewController"> <viewController id="43" customClass="CredentialProviderViewController" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="qkL-Od-lgU"/> <viewControllerLayoutGuide type="top" id="40"/>
<viewControllerLayoutGuide type="bottom" id="n38-gi-rB5"/> <viewControllerLayoutGuide type="bottom" id="41"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="zMn-AG-sqS"> <view key="view" contentMode="scaleToFill" id="44">
<rect key="frame" x="0.0" y="0.0" width="320" height="528"/> <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="9ga-4F-77Z"> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="logo.png" translatesAutoresizingMaskIntoConstraints="NO" id="1713">
<rect key="frame" x="0.0" y="64" width="320" height="464"/> <rect key="frame" x="66" y="316" width="282" height="44"/>
</imageView> </imageView>
<navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NOA-Dm-cuz">
<rect key="frame" x="0.0" y="20" width="320" height="44"/>
<items>
<navigationItem id="3HJ-uW-3hn">
<barButtonItem key="leftBarButtonItem" title="Done" style="done" id="WYi-yp-eM6">
<connections>
<action selector="DoneClicked" destination="ObA-dk-sSI" id="Qdu-qn-U6V"/>
</connections>
</barButtonItem>
</navigationItem>
</items>
</navigationBar>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="trailing" secondItem="NOA-Dm-cuz" secondAttribute="trailing" id="A05-Pj-hrr"/> <constraint firstItem="1713" firstAttribute="centerY" secondItem="44" secondAttribute="centerY" constant="-30" id="1763"/>
<constraint firstItem="9ga-4F-77Z" firstAttribute="top" secondItem="NOA-Dm-cuz" secondAttribute="bottom" id="Fps-3D-QQW"/> <constraint firstItem="1713" firstAttribute="centerX" secondItem="44" secondAttribute="centerX" id="1764"/>
<constraint firstItem="NOA-Dm-cuz" firstAttribute="leading" secondItem="zMn-AG-sqS" secondAttribute="leading" id="HxO-8t-aoh"/>
<constraint firstAttribute="trailing" secondItem="9ga-4F-77Z" secondAttribute="trailing" id="Ozw-Hg-0yh"/>
<constraint firstItem="9ga-4F-77Z" firstAttribute="leading" secondItem="zMn-AG-sqS" secondAttribute="leading" id="XH5-ld-ONA"/>
<constraint firstItem="n38-gi-rB5" firstAttribute="top" secondItem="9ga-4F-77Z" secondAttribute="bottom" id="eQg-nn-Zy4"/>
<constraint firstItem="NOA-Dm-cuz" firstAttribute="top" secondItem="qkL-Od-lgU" secondAttribute="bottom" id="we0-1t-bgp"/>
</constraints> </constraints>
</view> </view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="528"/>
<connections> <connections>
<outlet property="imageView" destination="9ga-4F-77Z" id="5y6-5w-9QO"/> <segue destination="oCZ-GQ-aOK" kind="show" identifier="loginListSegue" id="1679"/>
<outlet property="view" destination="zMn-AG-sqS" id="Qma-de-2ek"/> <segue destination="6855" kind="presentation" identifier="lockPasswordSegue" id="9874"/>
<segue destination="10580" kind="presentation" identifier="setupSegue" modalTransitionStyle="coverVertical" id="11089"/>
<segue destination="11552" kind="show" identifier="loginSearchSegue" id="12959"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="X47-rx-isc" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="45" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="252" y="-124"/> <point key="canvasLocation" x="-374" y="560"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="RvZ-Bc-vCe">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="oCZ-GQ-aOK" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="8A5-AR-QHS">
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<color key="tintColor" red="0.0" green="0.52549019607843139" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="barTintColor" red="0.23529411764705882" green="0.55294117647058827" blue="0.73725490196078436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textAttributes>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="2304" kind="relationship" relationship="rootViewController" id="4562"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Kkn-u3-rq1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="399" y="561"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="1844">
<objects>
<navigationController definesPresentationContext="YES" id="1845" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="1848">
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="barTintColor" red="0.23529411764705882" green="0.55294117647058827" blue="0.73725490196078436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textAttributes>
</navigationBar>
<connections>
<segue destination="2087" kind="relationship" relationship="rootViewController" id="2253"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1849" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1932" y="-270"/>
</scene>
<!--Add Login-->
<scene sceneID="2086">
<objects>
<tableViewController id="2087" customClass="LoginAddViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" allowsSelection="NO" rowHeight="50" sectionHeaderHeight="22" sectionFooterHeight="22" id="2088">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="sectionIndexBackgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<sections/>
<connections>
<outlet property="dataSource" destination="2087" id="2089"/>
<outlet property="delegate" destination="2087" id="2090"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Add Login" id="2252">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="3747">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="CancelBarButton_Activated:" destination="2087" id="3751"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="Save" id="3748">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="SaveBarButton_Activated:" destination="2087" id="3752"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="CancelBarButton" destination="3747" id="name-outlet-3747"/>
<outlet property="NavItem" destination="2252" id="name-outlet-2252"/>
<outlet property="SaveBarButton" destination="3748" id="name-outlet-3748"/>
<segue destination="4574" kind="show" identifier="passwordGeneratorSegue" id="4805"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="2093" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2632" y="-276"/>
</scene>
<!--Logins-->
<scene sceneID="2303">
<objects>
<tableViewController id="2304" customClass="LoginListViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="2305">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="3763" detailTextLabel="3764" rowHeight="44" style="IBUITableViewCellStyleSubtitle" id="3761">
<rect key="frame" x="0.0" y="22" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3761" id="3762">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="3763">
<rect key="frame" x="20" y="4" width="35" height="21.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="3764">
<rect key="frame" x="20" y="25.5" width="44" height="14.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="2304" id="2306"/>
<outlet property="delegate" destination="2304" id="2307"/>
</connections>
</tableView>
<toolbarItems/>
<navigationItem key="navigationItem" title="Logins" id="3734">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="3735">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="CancelBarButton_Activated:" destination="2304" id="3750"/>
</connections>
</barButtonItem>
<rightBarButtonItems>
<barButtonItem systemItem="add" id="3736">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="AddBarButton_Activated:" destination="2304" id="3749"/>
</connections>
</barButtonItem>
<barButtonItem systemItem="search" id="13195">
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<action selector="SearchBarButton_Activated:" destination="2304" id="13400"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="AddBarButton" destination="3736" id="name-outlet-3736"/>
<outlet property="CancelBarButton" destination="3735" id="name-outlet-3735"/>
<outlet property="NavItem" destination="3734" id="name-outlet-3734"/>
<segue destination="1845" kind="presentation" identifier="loginAddSegue" modalPresentationStyle="fullScreen" modalTransitionStyle="coverVertical" id="3731"/>
<segue destination="11552" kind="show" identifier="loginSearchFromListSegue" id="12574"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="2310" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1157" y="566"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="4573">
<objects>
<navigationController definesPresentationContext="YES" id="4574" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="4577">
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.0" green="0.52549019607843139" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="barTintColor" red="0.23529411764705882" green="0.55294117647058827" blue="0.73725490196078436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textAttributes>
</navigationBar>
<connections>
<segue destination="4576" kind="relationship" relationship="rootViewController" id="4575"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="4578" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3369" y="-276"/>
</scene>
<!--Generate Password-->
<scene sceneID="4579">
<objects>
<viewController id="4576" customClass="PasswordGeneratorViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="4571"/>
<viewControllerLayoutGuide type="bottom" id="4572"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="4930">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<containerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4933">
<rect key="frame" x="0.0" y="160.5" width="414" height="575.5"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<segue destination="4912" kind="embed" id="6480"/>
</connections>
</containerView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Label" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="4940">
<rect key="frame" x="15" y="105" width="384" height="20.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="4933" secondAttribute="trailing" id="6484"/>
<constraint firstItem="4933" firstAttribute="top" secondItem="4940" secondAttribute="bottom" constant="35" id="6485"/>
<constraint firstItem="4933" firstAttribute="leading" secondItem="4930" secondAttribute="leading" id="6486"/>
<constraint firstItem="4940" firstAttribute="leading" secondItem="4930" secondAttribute="leading" constant="15" id="6487"/>
<constraint firstItem="4940" firstAttribute="top" secondItem="4571" secondAttribute="bottom" constant="35" id="6488"/>
<constraint firstAttribute="trailing" secondItem="4940" secondAttribute="trailing" constant="15" id="6489"/>
<constraint firstItem="4572" firstAttribute="top" secondItem="4933" secondAttribute="bottom" id="6490"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Generate Password" id="4580">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="4807">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="CancelBarButton_Activated:" destination="4576" id="4887"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="Select" id="4808">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="SelectBarButton_Activated:" destination="4576" id="4810"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="BaseView" destination="4930" id="name-outlet-4930"/>
<outlet property="CancelBarButton" destination="4807" id="name-outlet-4807"/>
<outlet property="NavItem" destination="4580" id="name-outlet-4580"/>
<outlet property="OptionsContainer" destination="4933" id="name-outlet-4933"/>
<outlet property="PasswordLabel" destination="4940" id="name-outlet-4940"/>
<outlet property="SelectBarButton" destination="4808" id="name-outlet-4808"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="4582" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4045" y="-272"/>
</scene>
<!--Table View Controller-->
<scene sceneID="4911">
<objects>
<tableViewController id="4912" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="4913">
<rect key="frame" x="0.0" y="0.0" width="414" height="575.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="dataSource" destination="4912" id="4914"/>
<outlet property="delegate" destination="4912" id="4915"/>
</connections>
</tableView>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="4918" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4708" y="-194"/>
</scene>
<!--Navigation Controller-->
<!--Verify Fingerprint-->
<!--Verify PIN-->
<!--Navigation Controller-->
<!--Navigation Controller-->
<scene sceneID="6854">
<objects>
<navigationController definesPresentationContext="YES" id="6855" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="6857">
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="barTintColor" red="0.23529411764705882" green="0.55294117647058827" blue="0.73725490196078436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textAttributes>
</navigationBar>
<connections>
<segue destination="7413" kind="relationship" relationship="rootViewController" id="8266"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6858" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="390" y="1407"/>
</scene>
<!--Verify Master Password-->
<scene sceneID="7412">
<objects>
<tableViewController id="7413" customClass="LockPasswordViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="7414">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="dataSource" destination="7413" id="7415"/>
<outlet property="delegate" destination="7413" id="7416"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Verify Master Password" id="8265">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="8268">
<connections>
<action selector="CancelButton_Activated:" destination="7413" id="8287"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="Submit" id="8269">
<connections>
<action selector="SubmitButton_Activated:" destination="7413" id="8288"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="CancelButton" destination="8268" id="name-outlet-8268"/>
<outlet property="MainTableView" destination="7414" id="name-outlet-7414"/>
<outlet property="NavItem" destination="8265" id="name-outlet-8265"/>
<outlet property="SubmitButton" destination="8269" id="name-outlet-8269"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7419" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="955" y="1407"/>
</scene>
<!--Setup View Controller-->
<scene sceneID="10573">
<objects>
<viewController id="10570" customClass="SetupViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="10565"/>
<viewControllerLayoutGuide type="bottom" id="10566"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="10575">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Extension Activated!" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="11092">
<rect key="frame" x="15" y="100" width="384" height="20.5"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="11093">
<rect key="frame" x="15" y="134.5" width="570" height="41"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="check.png" translatesAutoresizingMaskIntoConstraints="NO" id="11094">
<rect key="frame" x="255" y="205.5" width="90" height="90"/>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="11092" firstAttribute="leading" secondItem="10575" secondAttribute="leading" constant="15" id="11114"/>
<constraint firstAttribute="trailing" secondItem="11092" secondAttribute="trailing" constant="15" id="11115"/>
<constraint firstItem="11092" firstAttribute="top" secondItem="10565" secondAttribute="bottom" constant="30" id="11116"/>
<constraint firstItem="11093" firstAttribute="leading" secondItem="10575" secondAttribute="leading" constant="15" id="11119"/>
<constraint firstAttribute="trailing" secondItem="11093" secondAttribute="trailing" constant="15" id="11120"/>
<constraint firstItem="11093" firstAttribute="top" secondItem="11092" secondAttribute="bottom" constant="20" id="11121"/>
<constraint firstItem="11094" firstAttribute="centerX" secondItem="10575" secondAttribute="centerX" id="11122"/>
<constraint firstItem="11094" firstAttribute="top" secondItem="11093" secondAttribute="bottom" constant="30" id="11123"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="10574">
<barButtonItem key="leftBarButtonItem" title="Back" id="11091">
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<action selector="BackButton_Activated:" destination="10570" id="11124"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="ActivatedLabel" destination="11092" id="name-outlet-11092"/>
<outlet property="BackButton" destination="11091" id="name-outlet-11091"/>
<outlet property="DescriptionLabel" destination="11093" id="name-outlet-11093"/>
<outlet property="IconImage" destination="11094" id="name-outlet-11094"/>
<outlet property="NavItem" destination="10574" id="name-outlet-10574"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="10576" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1129" y="-264"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="10579">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="10580" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="10583">
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="barTintColor" red="0.23529411764705882" green="0.55294117647058827" blue="0.73725490196078436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textAttributes>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="10570" kind="relationship" relationship="rootViewController" id="10939"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="10584" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="362" y="-267"/>
</scene>
<!--Search Logins-->
<scene sceneID="11542">
<objects>
<tableViewController id="11543" customClass="LoginSearchViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="11545">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<searchBar key="tableHeaderView" contentMode="redraw" id="13084">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="11543" id="13085"/>
</connections>
</searchBar>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="11548">
<rect key="frame" x="0.0" y="72" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="11548" id="11549">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="11543" id="11546"/>
<outlet property="delegate" destination="11543" id="11547"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Search Logins" id="11544">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="11950">
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<action selector="CancelBarButton_Activated:" destination="11543" id="12044"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" systemItem="add" id="11951">
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<action selector="AddBarButton_Activated:" destination="11543" id="12045"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="CancelBarButton" destination="11950" id="name-outlet-11950"/>
<outlet property="NavItem" destination="11544" id="name-outlet-11544"/>
<outlet property="SearchBar" destination="13084" id="name-outlet-13084"/>
<segue destination="1845" kind="show" identifier="loginAddFromSearchSegue" id="12738"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="11550" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2513" y="907"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="11551">
<objects>
<navigationController id="11552" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="11554">
<rect key="frame" x="0.0" y="20" width="414" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="barTintColor" red="0.23529411764705882" green="0.55294117647058827" blue="0.73725490196078436" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textAttributes>
</navigationBar>
<connections>
<segue destination="11543" kind="relationship" relationship="rootViewController" id="11553"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="11555" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1920" y="908"/>
</scene> </scene>
</scenes> </scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics"> <resources>
<simulatedStatusBarMetrics key="statusBar"/> <image name="check.png" width="90" height="90"/>
<simulatedOrientationMetrics key="orientation"/> <image name="logo.png" width="282" height="44"/>
<simulatedScreenMetrics key="destination" type="retina4"/> </resources>
</simulatedMetricsContainer> </document>
</document>

View File

@@ -0,0 +1,28 @@
using System;
using UIKit;
namespace Bit.iOS.Autofill
{
public partial class PasswordGeneratorViewController : Core.Controllers.PasswordGeneratorViewController
{
public PasswordGeneratorViewController(IntPtr handle)
: base(handle)
{ }
public LoginAddViewController Parent { get; set; }
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)
{
DismissViewController(true, () => Parent.PasswordCell.TextField.Text = PasswordLabel.Text);
}
partial void CancelBarButton_Activated(UIBarButtonItem sender)
{
DismissViewController(true, null);
}
}
}

View File

@@ -0,0 +1,82 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("PasswordGeneratorViewController")]
partial class PasswordGeneratorViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIView BaseView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem CancelBarButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UINavigationItem NavItem { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIView OptionsContainer { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel PasswordLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem SelectBarButton { get; set; }
[Action ("CancelBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
[Action ("SelectBarButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void SelectBarButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets ()
{
if (BaseView != null) {
BaseView.Dispose ();
BaseView = null;
}
if (CancelBarButton != null) {
CancelBarButton.Dispose ();
CancelBarButton = null;
}
if (NavItem != null) {
NavItem.Dispose ();
NavItem = null;
}
if (OptionsContainer != null) {
OptionsContainer.Dispose ();
OptionsContainer = null;
}
if (PasswordLabel != null) {
PasswordLabel.Dispose ();
PasswordLabel = null;
}
if (SelectBarButton != null) {
SelectBarButton.Dispose ();
SelectBarButton = null;
}
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using UIKit;
using Bit.iOS.Core.Controllers;
using Bit.App.Resources;
using Bit.iOS.Core.Utilities;
namespace Bit.iOS.Autofill
{
public partial class SetupViewController : ExtendedUIViewController
{
public SetupViewController(IntPtr handle) : base(handle)
{ }
public CredentialProviderViewController CPViewController { get; set; }
public override void ViewWillAppear(bool animated)
{
UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
base.ViewWillAppear(animated);
}
public override void ViewDidLoad()
{
View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f);
var descriptor = UIFontDescriptor.PreferredBody;
DescriptionLabel.Text = $@"{AppResources.AutofillSetup}
{AppResources.AutofillSetup2}";
DescriptionLabel.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize);
DescriptionLabel.TextColor = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f);
ActivatedLabel.Text = AppResources.AutofillActivated;
ActivatedLabel.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize * 1.3f);
BackButton.Title = AppResources.Back;
base.ViewDidLoad();
var task = ASHelpers.ReplaceAllIdentities();
}
partial void BackButton_Activated(UIBarButtonItem sender)
{
CPViewController.CompleteRequest();
}
}
}

View File

@@ -0,0 +1,69 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace Bit.iOS.Autofill
{
[Register ("SetupViewController")]
partial class SetupViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel ActivatedLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIBarButtonItem BackButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel DescriptionLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIImageView IconImage { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UINavigationItem NavItem { get; set; }
[Action ("BackButton_Activated:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void BackButton_Activated (UIKit.UIBarButtonItem sender);
void ReleaseDesignerOutlets ()
{
if (ActivatedLabel != null) {
ActivatedLabel.Dispose ();
ActivatedLabel = null;
}
if (BackButton != null) {
BackButton.Dispose ();
BackButton = null;
}
if (DescriptionLabel != null) {
DescriptionLabel.Dispose ();
DescriptionLabel = null;
}
if (IconImage != null) {
IconImage.Dispose ();
IconImage = null;
}
if (NavItem != null) {
NavItem.Dispose ();
NavItem = null;
}
}
}
}

View File

@@ -1,6 +1,8 @@
using System; using System.Linq;
using System.Linq; using System.Threading.Tasks;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.iOS.Core.Utilities; using Bit.iOS.Core.Utilities;
using Bit.iOS.Core.Views; using Bit.iOS.Core.Views;
using Foundation; using Foundation;
@@ -10,10 +12,9 @@ namespace Bit.iOS.Autofill.Utilities
{ {
public static class AutofillHelpers public static class AutofillHelpers
{ {
/* public async static Task TableRowSelectedAsync(UITableView tableView, NSIndexPath indexPath,
public static void TableRowSelected(UITableView tableView, NSIndexPath indexPath,
ExtensionTableSource tableSource, CredentialProviderViewController cpViewController, ExtensionTableSource tableSource, CredentialProviderViewController cpViewController,
UITableViewController controller, ISettings settings, string loginAddSegue) UITableViewController controller, string loginAddSegue)
{ {
tableView.DeselectRow(indexPath, true); tableView.DeselectRow(indexPath, true);
tableView.EndEditing(true); tableView.EndEditing(true);
@@ -23,7 +24,6 @@ namespace Bit.iOS.Autofill.Utilities
controller.PerformSegue(loginAddSegue, tableSource); controller.PerformSegue(loginAddSegue, tableSource);
return; return;
} }
var item = tableSource.Items.ElementAt(indexPath.Row); var item = tableSource.Items.ElementAt(indexPath.Row);
if(item == null) if(item == null)
{ {
@@ -34,15 +34,23 @@ namespace Bit.iOS.Autofill.Utilities
if(!string.IsNullOrWhiteSpace(item.Username) && !string.IsNullOrWhiteSpace(item.Password)) if(!string.IsNullOrWhiteSpace(item.Username) && !string.IsNullOrWhiteSpace(item.Password))
{ {
string totp = null; string totp = null;
if(!settings.GetValueOrDefault(App.Constants.SettingDisableTotpCopy, false)) var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
var disableTotpCopy = await storageService.GetAsync<bool?>(Bit.Core.Constants.DisableAutoTotpCopyKey);
if(!disableTotpCopy.GetValueOrDefault(false))
{ {
totp = tableSource.GetTotp(item); var userService = ServiceContainer.Resolve<IUserService>("userService");
var canAccessPremiumAsync = await userService.CanAccessPremiumAsync();
if(!string.IsNullOrWhiteSpace(item.Totp) &&
(canAccessPremiumAsync || item.CipherView.OrganizationUseTotp))
{
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
totp = await totpService.GetCodeAsync(item.Totp);
}
} }
cpViewController.CompleteRequest(item.Username, item.Password, totp); cpViewController.CompleteRequest(item.Username, item.Password, totp);
} }
else if(!string.IsNullOrWhiteSpace(item.Username) || !string.IsNullOrWhiteSpace(item.Password) || else if(!string.IsNullOrWhiteSpace(item.Username) || !string.IsNullOrWhiteSpace(item.Password) ||
!string.IsNullOrWhiteSpace(item.Totp.Value)) !string.IsNullOrWhiteSpace(item.Totp))
{ {
var sheet = Dialogs.CreateActionSheet(item.Name, controller); var sheet = Dialogs.CreateActionSheet(item.Name, controller);
if(!string.IsNullOrWhiteSpace(item.Username)) if(!string.IsNullOrWhiteSpace(item.Username))
@@ -65,7 +73,8 @@ namespace Bit.iOS.Autofill.Utilities
{ {
UIPasteboard clipboard = UIPasteboard.General; UIPasteboard clipboard = UIPasteboard.General;
clipboard.String = item.Password; clipboard.String = item.Password;
var alert = Dialogs.CreateMessageAlert(AppResources.CopiedPassword); var alert = Dialogs.CreateMessageAlert(
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
controller.PresentViewController(alert, true, () => controller.PresentViewController(alert, true, () =>
{ {
controller.DismissViewController(true, null); controller.DismissViewController(true, null);
@@ -73,26 +82,25 @@ namespace Bit.iOS.Autofill.Utilities
})); }));
} }
if(!string.IsNullOrWhiteSpace(item.Totp.Value)) if(!string.IsNullOrWhiteSpace(item.Totp))
{ {
sheet.AddAction(UIAlertAction.Create(AppResources.CopyTotp, UIAlertActionStyle.Default, a => sheet.AddAction(UIAlertAction.Create(AppResources.CopyTotp, UIAlertActionStyle.Default, async a =>
{ {
var totp = tableSource.GetTotp(item); var totp = await tableSource.GetTotpAsync(item);
if(string.IsNullOrWhiteSpace(totp)) if(string.IsNullOrWhiteSpace(totp))
{ {
return; return;
} }
UIPasteboard clipboard = UIPasteboard.General; UIPasteboard clipboard = UIPasteboard.General;
clipboard.String = totp; clipboard.String = totp;
var alert = Dialogs.CreateMessageAlert(AppResources.CopiedTotp); var alert = Dialogs.CreateMessageAlert(
string.Format(AppResources.ValueHasBeenCopied, AppResources.VerificationCodeTotp));
controller.PresentViewController(alert, true, () => controller.PresentViewController(alert, true, () =>
{ {
controller.DismissViewController(true, null); controller.DismissViewController(true, null);
}); });
})); }));
} }
sheet.AddAction(UIAlertAction.Create(AppResources.Cancel, UIAlertActionStyle.Cancel, null)); sheet.AddAction(UIAlertAction.Create(AppResources.Cancel, UIAlertActionStyle.Cancel, null));
controller.PresentViewController(sheet, true, null); controller.PresentViewController(sheet, true, null);
} }
@@ -102,6 +110,5 @@ namespace Bit.iOS.Autofill.Utilities
controller.PresentViewController(alert, true, null); controller.PresentViewController(alert, true, null);
} }
} }
*/
} }
} }

View File

@@ -64,8 +64,36 @@
<AppExtensionDebugBundleId /> <AppExtensionDebugBundleId />
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CredentialProviderViewController.cs" />
<Compile Include="CredentialProviderViewController.designer.cs">
<DependentUpon>CredentialProviderViewController.cs</DependentUpon>
</Compile>
<Compile Include="LockPasswordViewController.cs" />
<Compile Include="LockPasswordViewController.designer.cs">
<DependentUpon>LockPasswordViewController.cs</DependentUpon>
</Compile>
<Compile Include="LoginAddViewController.cs" />
<Compile Include="LoginAddViewController.designer.cs">
<DependentUpon>LoginAddViewController.cs</DependentUpon>
</Compile>
<Compile Include="LoginListViewController.cs" />
<Compile Include="LoginListViewController.designer.cs">
<DependentUpon>LoginListViewController.cs</DependentUpon>
</Compile>
<Compile Include="LoginSearchViewController.cs" />
<Compile Include="LoginSearchViewController.designer.cs">
<DependentUpon>LoginSearchViewController.cs</DependentUpon>
</Compile>
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" /> <Compile Include="AppDelegate.cs" />
<Compile Include="PasswordGeneratorViewController.cs" />
<Compile Include="PasswordGeneratorViewController.designer.cs">
<DependentUpon>PasswordGeneratorViewController.cs</DependentUpon>
</Compile>
<Compile Include="SetupViewController.cs" />
<Compile Include="SetupViewController.designer.cs">
<DependentUpon>SetupViewController.cs</DependentUpon>
</Compile>
<Compile Include="Utilities\AutofillHelpers.cs" /> <Compile Include="Utilities\AutofillHelpers.cs" />
<None Include="Info.plist" /> <None Include="Info.plist" />
<None Include="Entitlements.plist" /> <None Include="Entitlements.plist" />
@@ -90,6 +118,10 @@
<Project>{ee44c6a1-2a85-45fe-8d9b-bf1d5f88809c}</Project> <Project>{ee44c6a1-2a85-45fe-8d9b-bf1d5f88809c}</Project>
<Name>App</Name> <Name>App</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\Core\Core.csproj">
<Project>{4b8a8c41-9820-4341-974c-41e65b7f4366}</Project>
<Name>Core</Name>
</ProjectReference>
<ProjectReference Include="..\iOS.Core\iOS.Core.csproj"> <ProjectReference Include="..\iOS.Core\iOS.Core.csproj">
<Project>{e71f3053-056c-4381-9638-048ed73bdff6}</Project> <Project>{e71f3053-056c-4381-9638-048ed73bdff6}</Project>
<Name>iOS.Core</Name> <Name>iOS.Core</Name>

View File

@@ -5,23 +5,38 @@ using Bit.iOS.Core.Views;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.iOS.Core.Utilities; using Bit.iOS.Core.Utilities;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using System.Linq; using Bit.Core.Abstractions;
using Bit.iOS.Core.Controllers; using Bit.Core.Utilities;
using System.Threading.Tasks;
using Bit.Core.Models.Domain;
using Bit.Core.Enums;
namespace Bit.iOS.Core.Controllers namespace Bit.iOS.Core.Controllers
{ {
public abstract class LockPasswordViewController : ExtendedUITableViewController public abstract class LockPasswordViewController : ExtendedUITableViewController
{ {
//private IAuthService _authService; private ILockService _lockService;
//private ICryptoService _cryptoService; private ICryptoService _cryptoService;
private IDeviceActionService _deviceActionService;
private IUserService _userService;
private IStorageService _storageService;
private IStorageService _secureStorageService;
private IPlatformUtilsService _platformUtilsService;
private Tuple<bool, bool> _pinSet;
private bool _hasKey;
private bool _pinLock;
private bool _fingerprintLock;
private int _invalidPinAttempts;
public LockPasswordViewController(IntPtr handle) : base(handle) public LockPasswordViewController(IntPtr handle)
: base(handle)
{ } { }
public abstract UINavigationItem BaseNavItem { get; } public abstract UINavigationItem BaseNavItem { get; }
public abstract UIBarButtonItem BaseCancelButton { get; } public abstract UIBarButtonItem BaseCancelButton { get; }
public abstract UIBarButtonItem BaseSubmitButton { get; } public abstract UIBarButtonItem BaseSubmitButton { get; }
public abstract Action Success { get; } public abstract Action Success { get; }
public abstract Action Cancel { get; }
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell( public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
AppResources.MasterPassword, useLabelAsPlaceholder: true); AppResources.MasterPassword, useLabelAsPlaceholder: true);
@@ -35,21 +50,32 @@ namespace Bit.iOS.Core.Controllers
public override void ViewDidLoad() public override void ViewDidLoad()
{ {
// _authService = Resolver.Resolve<IAuthService>(); _lockService = ServiceContainer.Resolve<ILockService>("lockService");
// _cryptoService = Resolver.Resolve<ICryptoService>(); _cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
BaseNavItem.Title = AppResources.VerifyMasterPassword; _pinSet = _lockService.IsPinLockSetAsync().GetAwaiter().GetResult();
_hasKey = _cryptoService.HasKeyAsync().GetAwaiter().GetResult();
_pinLock = (_pinSet.Item1 && _hasKey) || _pinSet.Item2;
_fingerprintLock = _lockService.IsFingerprintLockSetAsync().GetAwaiter().GetResult();
BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
BaseCancelButton.Title = AppResources.Cancel; BaseCancelButton.Title = AppResources.Cancel;
BaseSubmitButton.Title = AppResources.Submit; BaseSubmitButton.Title = AppResources.Submit;
View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f);
var descriptor = UIFontDescriptor.PreferredBody; var descriptor = UIFontDescriptor.PreferredBody;
MasterPasswordCell.TextField.Placeholder = _pinLock ? AppResources.PIN : AppResources.MasterPassword;
MasterPasswordCell.TextField.SecureTextEntry = true; MasterPasswordCell.TextField.SecureTextEntry = true;
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go; MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) => MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
{ {
// CheckPassword(); CheckPasswordAsync().GetAwaiter().GetResult();
return true; return true;
}; };
@@ -59,49 +85,156 @@ namespace Bit.iOS.Core.Controllers
TableView.AllowsSelection = true; TableView.AllowsSelection = true;
base.ViewDidLoad(); base.ViewDidLoad();
if(_fingerprintLock)
{
var fingerprintButtonText = _deviceActionService.SupportsFaceId() ? AppResources.UseFaceIDToUnlock :
AppResources.UseFingerprintToUnlock;
// TODO: set button text
var tasks = Task.Run(async () =>
{
await Task.Delay(500);
PromptFingerprintAsync().GetAwaiter().GetResult();
});
}
} }
public override void ViewDidAppear(bool animated) public override void ViewDidAppear(bool animated)
{ {
base.ViewDidAppear(animated); base.ViewDidAppear(animated);
MasterPasswordCell.TextField.BecomeFirstResponder(); if(!_fingerprintLock)
{
MasterPasswordCell.TextField.BecomeFirstResponder();
}
} }
/* // TODO: Try fingerprint again button action
protected void CheckPassword()
protected async Task CheckPasswordAsync()
{ {
if(string.IsNullOrWhiteSpace(MasterPasswordCell.TextField.Text)) if(string.IsNullOrWhiteSpace(MasterPasswordCell.TextField.Text))
{ {
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred, var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword), AppResources.Ok); string.Format(AppResources.ValidationFieldRequired,
_pinLock ? AppResources.PIN : AppResources.MasterPassword),
AppResources.Ok);
PresentViewController(alert, true, null); PresentViewController(alert, true, null);
return; return;
} }
var key = _cryptoService.MakeKeyFromPassword(MasterPasswordCell.TextField.Text, _authService.Email, var email = await _userService.GetEmailAsync();
_authService.Kdf, _authService.KdfIterations); var kdf = await _userService.GetKdfAsync();
if(key.Key.SequenceEqual(_cryptoService.Key.Key)) var kdfIterations = await _userService.GetKdfIterationsAsync();
var inputtedValue = MasterPasswordCell.TextField.Text;
if(_pinLock)
{ {
_appSettingsService.Locked = false; var failed = true;
MasterPasswordCell.TextField.ResignFirstResponder(); try
Success(); {
if(_pinSet.Item1)
{
var protectedPin = await _storageService.GetAsync<string>(Bit.Core.Constants.ProtectedPin);
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin));
failed = decPin != inputtedValue;
_lockService.PinLocked = failed;
if(!failed)
{
DoContinue();
}
}
else
{
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
failed = false;
await SetKeyAndContinueAsync(key2);
}
}
catch
{
failed = true;
}
if(failed)
{
_invalidPinAttempts++;
if(_invalidPinAttempts >= 5)
{
Cancel?.Invoke();
return;
}
InvalidValue();
}
} }
else else
{ {
// TODO: keep track of invalid attempts and logout? var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdf, kdfIterations);
var keyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2);
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
if(storedKeyHash == null)
{
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
if(key2.KeyB64 == oldKey)
{
await _secureStorageService.RemoveAsync("oldKey");
await _cryptoService.SetKeyHashAsync(keyHash);
storedKeyHash = keyHash;
}
}
if(storedKeyHash != null && keyHash != null && storedKeyHash == keyHash)
{
await SetKeyAndContinueAsync(key2);
}
else
{
InvalidValue();
}
}
}
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred, private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key)
string.Format(null, AppResources.InvalidMasterPassword), AppResources.Ok, (a) => {
if(!_hasKey)
{
await _cryptoService.SetKeyAsync(key);
}
DoContinue();
}
private void DoContinue()
{
MasterPasswordCell.TextField.ResignFirstResponder();
Success();
}
public async Task PromptFingerprintAsync()
{
if(!_fingerprintLock)
{
return;
}
var success = await _platformUtilsService.AuthenticateFingerprintAsync(null,
_pinLock ? AppResources.PIN : AppResources.MasterPassword,
() => MasterPasswordCell.TextField.BecomeFirstResponder());
_lockService.FingerprintLocked = !success;
if(success)
{
DoContinue();
}
}
private void InvalidValue()
{
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
string.Format(null, _pinLock ? AppResources.PIN : AppResources.InvalidMasterPassword),
AppResources.Ok, (a) =>
{ {
MasterPasswordCell.TextField.Text = string.Empty; MasterPasswordCell.TextField.Text = string.Empty;
MasterPasswordCell.TextField.BecomeFirstResponder(); MasterPasswordCell.TextField.BecomeFirstResponder();
}); });
PresentViewController(alert, true, null);
PresentViewController(alert, true, null);
}
} }
*/
public class TableSource : UITableViewSource public class TableSource : UITableViewSource
{ {
@@ -121,7 +254,6 @@ namespace Bit.iOS.Core.Controllers
return _controller.MasterPasswordCell; return _controller.MasterPasswordCell;
} }
} }
return new UITableViewCell(); return new UITableViewCell();
} }
@@ -141,7 +273,6 @@ namespace Bit.iOS.Core.Controllers
{ {
return 1; return 1;
} }
return 0; return 0;
} }
@@ -159,15 +290,12 @@ namespace Bit.iOS.Core.Controllers
{ {
tableView.DeselectRow(indexPath, true); tableView.DeselectRow(indexPath, true);
tableView.EndEditing(true); tableView.EndEditing(true);
var cell = tableView.CellAt(indexPath); var cell = tableView.CellAt(indexPath);
if(cell == null) if(cell == null)
{ {
return; return;
} }
if(cell is ISelectable selectableCell)
var selectableCell = cell as ISelectable;
if(selectableCell != null)
{ {
selectableCell.Select(); selectableCell.Select();
} }

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Services;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.iOS.Core.Services;
using Foundation;
using HockeyApp.iOS;
using UIKit;
namespace Bit.iOS.Core.Utilities
{
public static class iOSCoreHelpers
{
public static string AppId = "com.8bit.bitwarden";
public static string AppGroupId = "group.com.8bit.bitwarden";
public static string AccessGroup = "LTZ2PFU5D6.com.8bit.bitwarden";
public static void RegisterHockeyApp()
{
var crashManagerDelegate = new HockeyAppCrashManagerDelegate(
ServiceContainer.Resolve<IAppIdService>("appIdService"),
ServiceContainer.Resolve<IUserService>("userService"));
var manager = BITHockeyManager.SharedHockeyManager;
manager.Configure("51f96ae568ba45f699a18ad9f63046c3", crashManagerDelegate);
manager.CrashManager.CrashManagerStatus = BITCrashManagerStatus.AutoSend;
manager.StartManager();
manager.Authenticator.AuthenticateInstallation();
manager.DisableMetricsManager = manager.DisableFeedbackManager = manager.DisableUpdateManager = true;
var task = crashManagerDelegate.InitAsync(manager);
}
public static void RegisterLocalServices()
{
if(ServiceContainer.Resolve<ILogService>("logService", true) == null)
{
ServiceContainer.Register<ILogService>("logService", new ConsoleLogService());
}
// Note: This might cause a race condition. Investigate more.
Task.Run(() =>
{
FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
FFImageLoading.ImageService.Instance.Initialize(new FFImageLoading.Config.Configuration
{
FadeAnimationEnabled = false,
FadeAnimationForCachedImages = false
});
});
var preferencesStorage = new PreferencesStorageService(AppGroupId);
var appGroupContainer = new NSFileManager().GetContainerUrl(AppGroupId);
var liteDbStorage = new LiteDbStorageService(
Path.Combine(appGroupContainer.Path, "Library", "bitwarden.db"));
liteDbStorage.InitAsync();
var localizeService = new LocalizeService();
var broadcasterService = new BroadcasterService();
var messagingService = new MobileBroadcasterMessagingService(broadcasterService);
var i18nService = new MobileI18nService(localizeService.GetCurrentCultureInfo());
var secureStorageService = new KeyChainStorageService(AppId, AccessGroup);
var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService);
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
broadcasterService);
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
ServiceContainer.Register<IMessagingService>("messagingService", messagingService);
ServiceContainer.Register<ILocalizeService>("localizeService", localizeService);
ServiceContainer.Register<II18nService>("i18nService", i18nService);
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
}
public static void Bootstrap()
{
(ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService).Init();
ServiceContainer.Resolve<IAuthService>("authService").Init();
// Note: This is not awaited
var bootstrapTask = BootstrapAsync();
}
public static void AppearanceAdjustments()
{
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(false));
UIApplication.SharedApplication.StatusBarHidden = false;
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
}
private static async Task BootstrapAsync()
{
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService").GetAsync<bool?>(
Bit.Core.Constants.DisableFaviconKey);
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
Bit.Core.Constants.DisableFaviconKey, disableFavicon);
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
}
}
}

View File

@@ -22,6 +22,7 @@ namespace Bit.iOS.Core.Views
protected ICipherService _cipherService; protected ICipherService _cipherService;
protected ITotpService _totpService; protected ITotpService _totpService;
protected IUserService _userService; protected IUserService _userService;
protected ISearchService _searchService;
private AppExtensionContext _context; private AppExtensionContext _context;
private UIViewController _controller; private UIViewController _controller;
@@ -30,6 +31,7 @@ namespace Bit.iOS.Core.Views
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService"); _cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_totpService = ServiceContainer.Resolve<ITotpService>("totpService"); _totpService = ServiceContainer.Resolve<ITotpService>("totpService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _userService = ServiceContainer.Resolve<IUserService>("userService");
_searchService = ServiceContainer.Resolve<ISearchService>("searchService");
_context = context; _context = context;
_controller = controller; _controller = controller;
} }
@@ -61,8 +63,6 @@ namespace Bit.iOS.Core.Views
_allItems = combinedLogins _allItems = combinedLogins
.Where(c => c.Type == Bit.Core.Enums.CipherType.Login) .Where(c => c.Type == Bit.Core.Enums.CipherType.Login)
.Select(s => new CipherViewModel(s)) .Select(s => new CipherViewModel(s))
.OrderBy(s => s.Name)
.ThenBy(s => s.Username)
.ToList() ?? new List<CipherViewModel>(); .ToList() ?? new List<CipherViewModel>();
FilterResults(searchFilter, new CancellationToken()); FilterResults(searchFilter, new CancellationToken());
} }
@@ -78,12 +78,9 @@ namespace Bit.iOS.Core.Views
else else
{ {
searchFilter = searchFilter.ToLower(); searchFilter = searchFilter.ToLower();
Items = _allItems var results = _searchService.SearchCiphersAsync(searchFilter,
.Where(s => s.Name?.ToLower().Contains(searchFilter) ?? false || c => c.Type == Bit.Core.Enums.CipherType.Login, null, ct).GetAwaiter().GetResult();
(s.Username?.ToLower().Contains(searchFilter) ?? false) || Items = results.Select(s => new CipherViewModel(s)).ToArray();
(s.Uris?.FirstOrDefault()?.Uri?.ToLower().Contains(searchFilter) ?? false))
.TakeWhile(s => !ct.IsCancellationRequested)
.ToArray();
} }
} }

View File

@@ -70,6 +70,7 @@
<Compile Include="Services\CryptoPrimitiveService.cs" /> <Compile Include="Services\CryptoPrimitiveService.cs" />
<Compile Include="Services\KeyChainStorageService.cs" /> <Compile Include="Services\KeyChainStorageService.cs" />
<Compile Include="Services\LocalizeService.cs" /> <Compile Include="Services\LocalizeService.cs" />
<Compile Include="Utilities\iOSCoreHelpers.cs" />
<Compile Include="Utilities\ThemeHelpers.cs" /> <Compile Include="Utilities\ThemeHelpers.cs" />
<Compile Include="Views\ExtensionSearchDelegate.cs" /> <Compile Include="Views\ExtensionSearchDelegate.cs" />
<Compile Include="Views\ExtensionTableSource.cs" /> <Compile Include="Views\ExtensionTableSource.cs" />

View File

@@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using AuthenticationServices; using AuthenticationServices;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
@@ -10,12 +8,10 @@ using Bit.App.Utilities;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.iOS.Core.Services;
using Bit.iOS.Core.Utilities; using Bit.iOS.Core.Utilities;
using Bit.iOS.Services; using Bit.iOS.Services;
using CoreNFC; using CoreNFC;
using Foundation; using Foundation;
using HockeyApp.iOS;
using UIKit; using UIKit;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS; using Xamarin.Forms.Platform.iOS;
@@ -23,12 +19,8 @@ using Xamarin.Forms.Platform.iOS;
namespace Bit.iOS namespace Bit.iOS
{ {
[Register("AppDelegate")] [Register("AppDelegate")]
public partial class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate public partial class AppDelegate : FormsApplicationDelegate
{ {
private const string AppId = "com.8bit.bitwarden";
private const string AppGroupId = "group.com.8bit.bitwarden";
private const string AccessGroup = "LTZ2PFU5D6.com.8bit.bitwarden";
private NFCNdefReaderSession _nfcSession = null; private NFCNdefReaderSession _nfcSession = null;
private iOSPushNotificationHandler _pushHandler = null; private iOSPushNotificationHandler _pushHandler = null;
private NFCReaderDelegate _nfcDelegate = null; private NFCReaderDelegate _nfcDelegate = null;
@@ -42,14 +34,13 @@ namespace Bit.iOS
{ {
Forms.Init(); Forms.Init();
InitApp(); InitApp();
Bootstrap();
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _storageService = ServiceContainer.Resolve<IStorageService>("storageService");
LoadApplication(new App.App(null)); LoadApplication(new App.App(null));
AppearanceAdjustments(); iOSCoreHelpers.AppearanceAdjustments();
ZXing.Net.Mobile.Forms.iOS.Platform.Init(); ZXing.Net.Mobile.Forms.iOS.Platform.Init();
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) => _broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
@@ -226,33 +217,14 @@ namespace Bit.iOS
_pushHandler?.OnMessageReceived(userInfo); _pushHandler?.OnMessageReceived(userInfo);
} }
private void InitApp() public void InitApp()
{ {
if(ServiceContainer.RegisteredServices.Count > 0) if(ServiceContainer.RegisteredServices.Count > 0)
{ {
return; return;
} }
RegisterLocalServices();
ServiceContainer.Init();
_pushHandler = new iOSPushNotificationHandler(
ServiceContainer.Resolve<IPushNotificationListenerService>("pushNotificationListenerService"));
_nfcDelegate = new NFCReaderDelegate((success, message) =>
_messagingService.Send("gotYubiKeyOTP", message));
var crashManagerDelegate = new HockeyAppCrashManagerDelegate( // Migration services
ServiceContainer.Resolve<IAppIdService>("appIdService"),
ServiceContainer.Resolve<IUserService>("userService"));
var manager = BITHockeyManager.SharedHockeyManager;
manager.Configure("51f96ae568ba45f699a18ad9f63046c3", crashManagerDelegate);
manager.CrashManager.CrashManagerStatus = BITCrashManagerStatus.AutoSend;
manager.StartManager();
manager.Authenticator.AuthenticateInstallation();
manager.DisableMetricsManager = manager.DisableFeedbackManager = manager.DisableUpdateManager = true;
var task = crashManagerDelegate.InitAsync(manager);
}
private void RegisterLocalServices()
{
ServiceContainer.Register<ILogService>("logService", new ConsoleLogService()); ServiceContainer.Register<ILogService>("logService", new ConsoleLogService());
ServiceContainer.Register("settingsShim", new App.Migration.SettingsShim()); ServiceContainer.Register("settingsShim", new App.Migration.SettingsShim());
if(false && App.Migration.MigrationHelpers.NeedsMigration()) if(false && App.Migration.MigrationHelpers.NeedsMigration())
@@ -261,44 +233,20 @@ namespace Bit.iOS
"oldSecureStorageService", new Migration.KeyChainStorageService()); "oldSecureStorageService", new Migration.KeyChainStorageService());
} }
// Note: This might cause a race condition. Investigate more. iOSCoreHelpers.RegisterLocalServices();
Task.Run(() => RegisterPush();
{ ServiceContainer.Init();
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(); iOSCoreHelpers.RegisterHockeyApp();
FFImageLoading.ImageService.Instance.Initialize(new FFImageLoading.Config.Configuration _pushHandler = new iOSPushNotificationHandler(
{ ServiceContainer.Resolve<IPushNotificationListenerService>("pushNotificationListenerService"));
FadeAnimationEnabled = false, _nfcDelegate = new NFCReaderDelegate((success, message) =>
FadeAnimationForCachedImages = false _messagingService.Send("gotYubiKeyOTP", message));
});
});
var preferencesStorage = new PreferencesStorageService(AppGroupId); iOSCoreHelpers.Bootstrap();
var appGroupContainer = new NSFileManager().GetContainerUrl(AppGroupId); }
var liteDbStorage = new LiteDbStorageService(
Path.Combine(appGroupContainer.Path, "Library", "bitwarden.db"));
liteDbStorage.InitAsync();
var localizeService = new LocalizeService();
var broadcasterService = new BroadcasterService();
var messagingService = new MobileBroadcasterMessagingService(broadcasterService);
var i18nService = new MobileI18nService(localizeService.GetCurrentCultureInfo());
var secureStorageService = new KeyChainStorageService(AppId, AccessGroup);
var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService);
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
broadcasterService);
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService); private void RegisterPush()
ServiceContainer.Register<IMessagingService>("messagingService", messagingService); {
ServiceContainer.Register<ILocalizeService>("localizeService", localizeService);
ServiceContainer.Register<II18nService>("i18nService", i18nService);
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
// Push
var notificationListenerService = new PushNotificationListenerService(); var notificationListenerService = new PushNotificationListenerService();
ServiceContainer.Register<IPushNotificationListenerService>( ServiceContainer.Register<IPushNotificationListenerService>(
"pushNotificationListenerService", notificationListenerService); "pushNotificationListenerService", notificationListenerService);
@@ -307,42 +255,6 @@ namespace Bit.iOS
"pushNotificationService", iosPushNotificationService); "pushNotificationService", iosPushNotificationService);
} }
private void Bootstrap()
{
(ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService).Init();
ServiceContainer.Resolve<IAuthService>("authService").Init();
// Note: This is not awaited
var bootstrapTask = BootstrapAsync();
}
private async Task BootstrapAsync()
{
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService").GetAsync<bool?>(
Bit.Core.Constants.DisableFaviconKey);
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
Bit.Core.Constants.DisableFaviconKey, disableFavicon);
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
}
private void AppearanceAdjustments()
{
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(false));
/*
var primaryColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f);
var grayLight = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f);
UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
UIBarButtonItem.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).TintColor = primaryColor;
UIButton.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).SetTitleColor(primaryColor,
UIControlState.Normal);
UIButton.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).TintColor = primaryColor;
UIStepper.Appearance.TintColor = grayLight;
UISlider.Appearance.TintColor = primaryColor;
*/
UIApplication.SharedApplication.StatusBarHidden = false;
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
}
private void ListenYubiKey(bool listen) private void ListenYubiKey(bool listen)
{ {
if(_deviceActionService.SupportsNfc()) if(_deviceActionService.SupportsNfc())