Support auto fill (no quicktype bar support)
This commit is contained in:
parent
5c43fc07dd
commit
a849b667dc
5 changed files with 309 additions and 33 deletions
|
|
@ -19,13 +19,14 @@
|
|||
A2367BA01EF0387000C8FE8B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A2367B9F1EF0387000C8FE8B /* Assets.xcassets */; };
|
||||
A239F51F2157B72700576CBF /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A239F51E2157B72700576CBF /* StringExtension.swift */; };
|
||||
A239F5212157B75E00576CBF /* FileManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A239F5202157B75E00576CBF /* FileManagerExtension.swift */; };
|
||||
A239F5612157EEB000576CBF /* PasscodeExtensionDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */; };
|
||||
A239F5902158C07D00576CBF /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A239F58F2158C07D00576CBF /* AuthenticationServices.framework */; };
|
||||
A239F5962158C08C00576CBF /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A239F58F2158C07D00576CBF /* AuthenticationServices.framework */; };
|
||||
A239F5992158C08C00576CBF /* CredentialProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A239F5982158C08C00576CBF /* CredentialProviderViewController.swift */; };
|
||||
A239F59C2158C08C00576CBF /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A239F59A2158C08C00576CBF /* MainInterface.storyboard */; };
|
||||
A239F5A12158C08C00576CBF /* passAutoFillExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
A239F5A52158C3F400576CBF /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; };
|
||||
A239F5A621591C3200576CBF /* PasscodeExtensionDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */; };
|
||||
A239F5A821591C5C00576CBF /* PasscodeExtensionDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A239F5A721591C5C00576CBF /* PasscodeExtensionDisplay.swift */; };
|
||||
A26075811EEC6F34005DB03E /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; };
|
||||
A26075881EEC6F34005DB03E /* passKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A26075871EEC6F34005DB03E /* passKitTests.swift */; };
|
||||
A260758A1EEC6F34005DB03E /* passKit.h in Headers */ = {isa = PBXBuildFile; fileRef = A260757A1EEC6F34005DB03E /* passKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
|
@ -200,6 +201,7 @@
|
|||
A239F59B2158C08C00576CBF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
A239F59D2158C08C00576CBF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
A239F59E2158C08C00576CBF /* passAutoFillExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passAutoFillExtension.entitlements; sourceTree = "<group>"; };
|
||||
A239F5A721591C5C00576CBF /* PasscodeExtensionDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasscodeExtensionDisplay.swift; sourceTree = "<group>"; };
|
||||
A26075781EEC6F34005DB03E /* passKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = passKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A260757A1EEC6F34005DB03E /* passKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = passKit.h; sourceTree = "<group>"; };
|
||||
A260757B1EEC6F34005DB03E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
|
@ -216,7 +218,7 @@
|
|||
A26700351EEC475600176B8A /* passProcessor.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = passProcessor.js; sourceTree = "<group>"; };
|
||||
A2802BF71E70813A00879216 /* SliderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderTableViewCell.swift; sourceTree = "<group>"; };
|
||||
A2802BF81E70813A00879216 /* SliderTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SliderTableViewCell.xib; sourceTree = "<group>"; };
|
||||
A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasscodeExtensionDisplay.swift; path = ../passExtension/PasscodeExtensionDisplay.swift; sourceTree = "<group>"; };
|
||||
A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeExtensionDisplay.swift; sourceTree = "<group>"; };
|
||||
A2A61C0C1EEF8DFE00CFE063 /* libPods-passExtension.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-passExtension.a"; path = "../../Library/Developer/Xcode/DerivedData/pass-fwlmfsjroyvbfhdyqmglrwfhvjli/Build/Products/Debug-iphonesimulator/libPods-passExtension.a"; sourceTree = "<group>"; };
|
||||
A2A61C101EEF8E3500CFE063 /* libPods-passKit.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-passKit.a"; path = "Pods/../build/Debug-iphoneos/libPods-passKit.a"; sourceTree = "<group>"; };
|
||||
A2A61C1F1EEFABAD00CFE063 /* UtilsExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilsExtension.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -363,6 +365,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
A2A61C2B1EEFDF3300CFE063 /* ExtensionViewController.swift */,
|
||||
A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */,
|
||||
);
|
||||
name = Controllers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -378,7 +381,8 @@
|
|||
A239F5972158C08C00576CBF /* passAutoFillExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A239F5982158C08C00576CBF /* CredentialProviderViewController.swift */,
|
||||
A239F5A921591E3700576CBF /* Controllers */,
|
||||
A239F5AA21591E3D00576CBF /* Helpers */,
|
||||
A239F59A2158C08C00576CBF /* MainInterface.storyboard */,
|
||||
A239F59D2158C08C00576CBF /* Info.plist */,
|
||||
A239F59E2158C08C00576CBF /* passAutoFillExtension.entitlements */,
|
||||
|
|
@ -386,6 +390,22 @@
|
|||
path = passAutoFillExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A239F5A921591E3700576CBF /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A239F5982158C08C00576CBF /* CredentialProviderViewController.swift */,
|
||||
A239F5A721591C5C00576CBF /* PasscodeExtensionDisplay.swift */,
|
||||
);
|
||||
name = Controllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A239F5AA21591E3D00576CBF /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A26075791EEC6F34005DB03E /* passKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -428,7 +448,6 @@
|
|||
children = (
|
||||
A2C532BD201E5AA100DB9F53 /* PasscodeLockPresenter.swift */,
|
||||
A2C532BC201E5AA000DB9F53 /* PasscodeLockViewController.swift */,
|
||||
A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */,
|
||||
);
|
||||
name = Controllers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -559,11 +578,11 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
DC917BD51E2E8231000FDF54 /* pass */,
|
||||
DC13B14F1E8640810097803F /* passTests */,
|
||||
A26700251EEC466A00176B8A /* passExtension */,
|
||||
A26075791EEC6F34005DB03E /* passKit */,
|
||||
A26075861EEC6F34005DB03E /* passKitTests */,
|
||||
A26700251EEC466A00176B8A /* passExtension */,
|
||||
A239F5972158C08C00576CBF /* passAutoFillExtension */,
|
||||
DC13B14F1E8640810097803F /* passTests */,
|
||||
A26075861EEC6F34005DB03E /* passKitTests */,
|
||||
DC917BD41E2E8231000FDF54 /* Products */,
|
||||
DC917BED1E2F38C4000FDF54 /* Frameworks */,
|
||||
A51B01737D08DB47BB58F85A /* Pods */,
|
||||
|
|
@ -1059,6 +1078,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A239F5A821591C5C00576CBF /* PasscodeExtensionDisplay.swift in Sources */,
|
||||
A239F5992158C08C00576CBF /* CredentialProviderViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -1067,7 +1087,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A239F5612157EEB000576CBF /* PasscodeExtensionDisplay.swift in Sources */,
|
||||
A2BEC1BB207D2EFE00F3051C /* UIViewExtension.swift in Sources */,
|
||||
A2C532BB201E5A9600DB9F53 /* PasscodeLock.swift in Sources */,
|
||||
A2F4E2151EED800F0011986E /* Password.swift in Sources */,
|
||||
|
|
@ -1101,6 +1120,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A239F5A621591C3200576CBF /* PasscodeExtensionDisplay.swift in Sources */,
|
||||
A2A61C2C1EEFDF3300CFE063 /* ExtensionViewController.swift in Sources */,
|
||||
A2168A7F1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift in Sources */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14092" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Xki-Si-B7m">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Xki-Si-B7m">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14081.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
|
@ -13,7 +12,7 @@
|
|||
<!--Credential Provider View Controller-->
|
||||
<scene sceneID="Uma-9u-xWV">
|
||||
<objects>
|
||||
<viewController id="Xki-Si-B7m" customClass="CredentialProviderViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="Xki-Si-B7m" customClass="CredentialProviderViewController" customModule="passAutoFillExtension" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="BuU-Ak-iZz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
|
|
@ -30,28 +29,52 @@
|
|||
</navigationItem>
|
||||
</items>
|
||||
</navigationBar>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a7v-ug-QzG">
|
||||
<rect key="frame" x="87.5" y="327" width="199" height="33"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Return Example Password"/>
|
||||
<connections>
|
||||
<action selector="passwordSelected:" destination="Xki-Si-B7m" eventType="touchUpInside" id="ODd-lr-mud"/>
|
||||
</connections>
|
||||
</button>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="6fX-Sk-6qN">
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||
<subviews>
|
||||
<searchBar contentMode="redraw" text="" translatesAutoresizingMaskIntoConstraints="NO" id="MmN-WX-sur">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="56"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</searchBar>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="XmI-l4-SgT">
|
||||
<rect key="frame" x="0.0" y="56" width="375" height="547"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="passwordTableViewCell" id="fXA-SG-IOe" customClass="PasswordDetailTitleTableViewCell" customModule="pass">
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fXA-SG-IOe" id="KPa-Az-i6V">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Ky8-vK-JVj" firstAttribute="trailing" secondItem="6fX-Sk-6qN" secondAttribute="trailing" id="17t-mv-eRl"/>
|
||||
<constraint firstItem="Ky8-vK-JVj" firstAttribute="top" secondItem="3wq-kG-lGu" secondAttribute="top" id="BIN-jb-uNd"/>
|
||||
<constraint firstItem="6fX-Sk-6qN" firstAttribute="leading" secondItem="Ky8-vK-JVj" secondAttribute="leading" id="Cd5-jJ-hSk"/>
|
||||
<constraint firstItem="6fX-Sk-6qN" firstAttribute="top" secondItem="3wq-kG-lGu" secondAttribute="bottom" id="Clz-JE-ME7"/>
|
||||
<constraint firstItem="6fX-Sk-6qN" firstAttribute="trailing" secondItem="Ky8-vK-JVj" secondAttribute="trailing" id="T5d-Qv-N42"/>
|
||||
<constraint firstItem="3wq-kG-lGu" firstAttribute="width" secondItem="BuU-Ak-iZz" secondAttribute="width" id="UkD-v4-BcH"/>
|
||||
<constraint firstItem="a7v-ug-QzG" firstAttribute="centerY" secondItem="Ky8-vK-JVj" secondAttribute="centerY" id="fAC-0v-NFE"/>
|
||||
<constraint firstItem="a7v-ug-QzG" firstAttribute="centerX" secondItem="3wq-kG-lGu" secondAttribute="centerX" id="io1-ZS-gwn"/>
|
||||
<constraint firstItem="6fX-Sk-6qN" firstAttribute="bottom" secondItem="Ky8-vK-JVj" secondAttribute="bottom" id="Zrc-7l-kYX"/>
|
||||
<constraint firstItem="Ky8-vK-JVj" firstAttribute="bottom" secondItem="6fX-Sk-6qN" secondAttribute="bottom" id="kH5-f1-kuv"/>
|
||||
<constraint firstItem="3wq-kG-lGu" firstAttribute="centerX" secondItem="BuU-Ak-iZz" secondAttribute="centerX" id="rtV-5c-0bl"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="Ky8-vK-JVj"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="searchBar" destination="MmN-WX-sur" id="KZk-g3-9Ox"/>
|
||||
<outlet property="tableView" destination="XmI-l4-SgT" id="2yd-Uj-96H"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="RwB-HB-TSk" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53.600000000000001" y="26.53673163418291"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -9,24 +9,61 @@
|
|||
import AuthenticationServices
|
||||
import passKit
|
||||
|
||||
class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||
fileprivate class PasswordsTableEntry : NSObject {
|
||||
var title: String
|
||||
var categoryText: String
|
||||
var categoryArray: [String]
|
||||
var passwordEntity: PasswordEntity?
|
||||
init(_ entity: PasswordEntity) {
|
||||
self.title = entity.name!
|
||||
self.categoryText = entity.getCategoryText()
|
||||
self.categoryArray = entity.getCategoryArray()
|
||||
self.passwordEntity = entity
|
||||
}
|
||||
}
|
||||
|
||||
class CredentialProviderViewController: ASCredentialProviderViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate {
|
||||
@IBOutlet weak var searchBar: UISearchBar!
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
private let passwordStore = PasswordStore.shared
|
||||
|
||||
private var searchActive = false
|
||||
private var passwordsTableEntries: [PasswordsTableEntry] = []
|
||||
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
|
||||
|
||||
private lazy var passcodelock: PasscodeExtensionDisplay = {
|
||||
let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext)
|
||||
return passcodelock
|
||||
}()
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
passcodelock.presentPasscodeLockIfNeeded(self)
|
||||
}
|
||||
|
||||
/*
|
||||
Prepare your UI to list available credentials for the user to choose from. The items in
|
||||
'serviceIdentifiers' describe the service the user is logging in to, so your extension can
|
||||
prioritize the most relevant credentials in the list.
|
||||
*/
|
||||
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
||||
print("prepareCredentialList")
|
||||
|
||||
// clean up the search bar
|
||||
guard serviceIdentifiers.count > 0 else {
|
||||
searchBar.text = ""
|
||||
searchBar.becomeFirstResponder()
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
return
|
||||
}
|
||||
|
||||
// get the domain
|
||||
var identifier = serviceIdentifiers[0].identifier
|
||||
if !identifier.hasPrefix("http://") && !identifier.hasPrefix("https://") {
|
||||
identifier = "http://" + identifier
|
||||
}
|
||||
let url = URL(string: identifier)?.host ?? ""
|
||||
|
||||
// "click" search
|
||||
searchBar.text = url
|
||||
searchBar.becomeFirstResponder()
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -63,9 +100,148 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
|
|||
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
||||
}
|
||||
|
||||
@IBAction func passwordSelected(_ sender: AnyObject?) {
|
||||
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
|
||||
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
passcodelock.presentPasscodeLockIfNeeded(self)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// prepare
|
||||
searchBar.delegate = self
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
|
||||
|
||||
// initialize table entries
|
||||
initPasswordsTableEntries()
|
||||
}
|
||||
|
||||
private func initPasswordsTableEntries() {
|
||||
passwordsTableEntries.removeAll()
|
||||
filteredPasswordsTableEntries.removeAll()
|
||||
var passwordEntities = [PasswordEntity]()
|
||||
passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false)
|
||||
passwordsTableEntries = passwordEntities.map {
|
||||
PasswordsTableEntry($0)
|
||||
}
|
||||
}
|
||||
|
||||
// define cell contents, and set long press action
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
if entry.passwordEntity!.synced {
|
||||
cell.textLabel?.text = entry.title
|
||||
} else {
|
||||
cell.textLabel?.text = "↻ \(entry.title)"
|
||||
}
|
||||
cell.accessoryType = .none
|
||||
cell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
cell.detailTextLabel?.text = entry.categoryText
|
||||
return cell
|
||||
}
|
||||
|
||||
// select row -> extension returns (with username and password)
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
|
||||
guard self.passwordStore.privateKey != nil else {
|
||||
Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
let passwordEntity = entry.passwordEntity!
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
var decryptedPassword: Password?
|
||||
do {
|
||||
decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase)
|
||||
let username = decryptedPassword?.username ?? decryptedPassword?.login ?? ""
|
||||
let password = decryptedPassword?.password ?? ""
|
||||
DispatchQueue.main.async {// prepare a dictionary to return
|
||||
let passwordCredential = ASPasswordCredential(user: username, password: password)
|
||||
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
DispatchQueue.main.async {
|
||||
// remove the wrong passphrase so that users could enter it next time
|
||||
self.passwordStore.pgpKeyPassphrase = nil
|
||||
Utils.alert(title: "Cannot Copy Password", message: error.localizedDescription, controller: self, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if searchActive {
|
||||
return filteredPasswordsTableEntries.count
|
||||
}
|
||||
return passwordsTableEntries.count;
|
||||
}
|
||||
|
||||
private func requestPGPKeyPassphrase() -> String {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
var passphrase = ""
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertController.Style.alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: {_ in
|
||||
passphrase = alert.textFields!.first!.text!
|
||||
sem.signal()
|
||||
}))
|
||||
alert.addTextField(configurationHandler: {(textField: UITextField!) in
|
||||
textField.text = ""
|
||||
textField.isSecureTextEntry = true
|
||||
})
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
let _ = sem.wait(timeout: DispatchTime.distantFuture)
|
||||
if SharedDefaults[.isRememberPGPPassphraseOn] {
|
||||
self.passwordStore.pgpKeyPassphrase = passphrase
|
||||
}
|
||||
return passphrase
|
||||
}
|
||||
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.text = ""
|
||||
searchActive = false
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||
if let searchText = searchBar.text, searchText.isEmpty == false {
|
||||
filteredPasswordsTableEntries = passwordsTableEntries.filter { entry in
|
||||
var matched = false
|
||||
matched = matched || entry.title.range(of: searchText, options: .caseInsensitive) != nil
|
||||
matched = matched || searchText.range(of: entry.title, options: .caseInsensitive) != nil
|
||||
entry.categoryArray.forEach({ (category) in
|
||||
matched = matched || category.range(of: searchText, options: .caseInsensitive) != nil
|
||||
matched = matched || searchText.range(of: category, options: .caseInsensitive) != nil
|
||||
})
|
||||
return matched
|
||||
}
|
||||
searchActive = true
|
||||
} else {
|
||||
searchActive = false
|
||||
}
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
}
|
||||
|
||||
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordsTableEntry {
|
||||
if searchActive {
|
||||
return filteredPasswordsTableEntries[indexPath.row]
|
||||
} else {
|
||||
return passwordsTableEntries[indexPath.row]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
56
passAutoFillExtension/PasscodeExtensionDisplay.swift
Normal file
56
passAutoFillExtension/PasscodeExtensionDisplay.swift
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// PasscodeLockDisplay.swift
|
||||
// pass
|
||||
//
|
||||
// Created by Yishi Lin on 14/6/17.
|
||||
// Copyright © 2017 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import passKit
|
||||
import AuthenticationServices
|
||||
|
||||
// cancel means cancel the extension
|
||||
class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
|
||||
var originalExtensionContest: ASCredentialProviderExtensionContext?
|
||||
public convenience init(extensionContext: ASCredentialProviderExtensionContext?) {
|
||||
self.init()
|
||||
originalExtensionContest = extensionContext
|
||||
}
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
cancelButton?.removeTarget(nil, action: nil, for: .allEvents)
|
||||
cancelButton?.addTarget(self, action: #selector(cancelExtension), for: .touchUpInside)
|
||||
}
|
||||
@objc func cancelExtension() {
|
||||
originalExtensionContest?.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
class PasscodeExtensionDisplay {
|
||||
private var isPasscodePresented = false
|
||||
private let passcodeLockVC: PasscodeLockViewControllerForExtension
|
||||
private let extensionContext: ASCredentialProviderExtensionContext?
|
||||
|
||||
public init(extensionContext: ASCredentialProviderExtensionContext?) {
|
||||
self.extensionContext = extensionContext
|
||||
passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
|
||||
passcodeLockVC.dismissCompletionCallback = { [weak self] in
|
||||
self?.dismiss()
|
||||
}
|
||||
passcodeLockVC.setCancellable(true)
|
||||
}
|
||||
|
||||
// present the passcode lock view if passcode is set and the view controller is not presented
|
||||
public func presentPasscodeLockIfNeeded(_ extensionVC: UIViewController) {
|
||||
guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else {
|
||||
return
|
||||
}
|
||||
isPasscodePresented = true
|
||||
extensionVC.present(passcodeLockVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
public func dismiss(animated: Bool = true) {
|
||||
isPasscodePresented = false
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import passKit
|
||||
|
||||
// cancel means cancel the extension
|
||||
class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
|
||||
|
|
@ -25,7 +26,7 @@ class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
|
|||
}
|
||||
}
|
||||
|
||||
open class PasscodeExtensionDisplay {
|
||||
class PasscodeExtensionDisplay {
|
||||
private var isPasscodePresented = false
|
||||
private let passcodeLockVC: PasscodeLockViewControllerForExtension
|
||||
private let extensionContext: NSExtensionContext?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue