Add notification action to copy OTP or just inform about the copied OTP (#513)
* Add notification action to copy OTP or just inform about the copied OTP The notification either shows the current OTP which can be copied by a notification action or it shows just a hint to inform about the copied OTP. This depends on the new option "autoCopyOTP". * Extract method * Set type and style one-time
This commit is contained in:
parent
63e7235978
commit
e1cbcb5d7a
11 changed files with 155 additions and 76 deletions
|
|
@ -78,6 +78,7 @@
|
|||
30B00F5526D59562004DAC61 /* PasscodeLockViewControllerForExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B00F5426D59562004DAC61 /* PasscodeLockViewControllerForExtension.swift */; };
|
||||
30B00F5626D597A8004DAC61 /* PasscodeLockViewControllerForExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B00F5426D59562004DAC61 /* PasscodeLockViewControllerForExtension.swift */; };
|
||||
30B04860209A5141001013CA /* PasswordTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B0485F209A5141001013CA /* PasswordTest.swift */; };
|
||||
30B331772704DBEE00D64A99 /* NotificationCenterDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B331762704DBEE00D64A99 /* NotificationCenterDispatcher.swift */; };
|
||||
30B4C7BA24084AAA008B86F7 /* PasswordGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B4C7B924084AAA008B86F7 /* PasswordGenerator.swift */; };
|
||||
30BAC8C622E3BAAF00438475 /* TestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAC8C422E3BAAF00438475 /* TestBase.swift */; };
|
||||
30BAC8C722E3BAAF00438475 /* TestPGPKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAC8C522E3BAAF00438475 /* TestPGPKeys.swift */; };
|
||||
|
|
@ -372,6 +373,7 @@
|
|||
30A86F94230F237000F821A4 /* CryptoFrameworkTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoFrameworkTest.swift; sourceTree = "<group>"; };
|
||||
30B00F5426D59562004DAC61 /* PasscodeLockViewControllerForExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasscodeLockViewControllerForExtension.swift; sourceTree = "<group>"; };
|
||||
30B0485F209A5141001013CA /* PasswordTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordTest.swift; sourceTree = "<group>"; };
|
||||
30B331762704DBEE00D64A99 /* NotificationCenterDispatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterDispatcher.swift; sourceTree = "<group>"; };
|
||||
30B4C7B924084AAA008B86F7 /* PasswordGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordGenerator.swift; sourceTree = "<group>"; };
|
||||
30BAC8C422E3BAAF00438475 /* TestBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestBase.swift; sourceTree = "<group>"; };
|
||||
30BAC8C522E3BAAF00438475 /* TestPGPKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestPGPKeys.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -742,10 +744,10 @@
|
|||
9AFC87E025B3B556008D6060 /* Services */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9AFC87E125B3B5C6008D6060 /* PasswordNavigationDataSource.swift */,
|
||||
9AFC87EF25B514AD008D6060 /* PasswordDecryptor.swift */,
|
||||
9AFC87FF25B51EC3008D6060 /* PasswordEncryptor.swift */,
|
||||
9AFC87F725B51742008D6060 /* PasswordManager.swift */,
|
||||
9AFC87E125B3B5C6008D6060 /* PasswordNavigationDataSource.swift */,
|
||||
);
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -875,6 +877,7 @@
|
|||
30697C2421F63C590064FCAC /* Globals.swift */,
|
||||
3032327322C7F710009EBD9C /* KeyFileManager.swift */,
|
||||
30BAC8CC22E3BB9700438475 /* KeyStore.swift */,
|
||||
30B331762704DBEE00D64A99 /* NotificationCenterDispatcher.swift */,
|
||||
30697C2321F63C580064FCAC /* NotificationNames.swift */,
|
||||
302202EE222F14E400555236 /* SearchBarScope.swift */,
|
||||
30697C2721F63C590064FCAC /* Utils.swift */,
|
||||
|
|
@ -1515,6 +1518,7 @@
|
|||
A2AA934422DE30DD00D79A00 /* PGPAgent.swift in Sources */,
|
||||
30697C3B21F63C990064FCAC /* String+Localization.swift in Sources */,
|
||||
302E85612125ECC70031BA64 /* Parser.swift in Sources */,
|
||||
30B331772704DBEE00D64A99 /* NotificationCenterDispatcher.swift in Sources */,
|
||||
30CCA91A232591320048CA51 /* ObjectivePGPInterface.swift in Sources */,
|
||||
30697C4621F63CAB0064FCAC /* GitCredential.swift in Sources */,
|
||||
30B4C7BA24084AAA008B86F7 /* PasswordGenerator.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
return uiSwitch
|
||||
}()
|
||||
|
||||
let autoCopyOTPSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(autoCopyOTPSwitchAction(_:)), for: UIControl.Event.valueChanged)
|
||||
return uiSwitch
|
||||
}()
|
||||
|
||||
let rememberPGPPassphraseSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
|
|
@ -91,6 +99,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
[.title: "HidePasswordImages".localize(), .action: "none"],
|
||||
[.title: "HideUnknownFields".localize(), .action: "none"],
|
||||
[.title: "HideOtpFields".localize(), .action: "none"],
|
||||
[.title: "AutoCopyOTP".localize(), .action: "none"],
|
||||
],
|
||||
]
|
||||
super.viewDidLoad()
|
||||
|
|
@ -98,71 +107,64 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
cell.accessoryType = .none
|
||||
cell.selectionStyle = .none
|
||||
switch cell.textLabel!.text! {
|
||||
case "HideUnknownFields".localize():
|
||||
cell.accessoryType = .none
|
||||
let detailButton = UIButton(type: .detailDisclosure)
|
||||
hideUnknownSwitch.frame = CGRect(x: detailButton.bounds.width + 10, y: 0, width: hideUnknownSwitch.bounds.width, height: hideUnknownSwitch.bounds.height)
|
||||
detailButton.frame = CGRect(x: 0, y: 5, width: detailButton.bounds.width, height: detailButton.bounds.height)
|
||||
detailButton.addTarget(self, action: #selector(GeneralSettingsTableViewController.tapHideUnknownSwitchDetailButton(_:)), for: UIControl.Event.touchDown)
|
||||
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hideUnknownSwitch.bounds.width + 10, height: cell.contentView.bounds.height))
|
||||
accessoryView.addSubview(detailButton)
|
||||
accessoryView.addSubview(hideUnknownSwitch)
|
||||
hideUnknownSwitch.center.y = accessoryView.center.y
|
||||
detailButton.center.y = accessoryView.center.y
|
||||
cell.accessoryView = accessoryView
|
||||
cell.selectionStyle = .none
|
||||
hideUnknownSwitch.isOn = Defaults.isHideUnknownOn
|
||||
addDetailButton(to: cell, for: hideUnknownSwitch, with: #selector(tapHideUnknownSwitchDetailButton))
|
||||
case "HideOtpFields".localize():
|
||||
cell.accessoryType = .none
|
||||
let detailButton = UIButton(type: .detailDisclosure)
|
||||
hideOTPSwitch.frame = CGRect(x: detailButton.bounds.width + 10, y: 0, width: hideOTPSwitch.bounds.width, height: hideOTPSwitch.bounds.height)
|
||||
detailButton.frame = CGRect(x: 0, y: 5, width: detailButton.bounds.width, height: detailButton.bounds.height)
|
||||
detailButton.addTarget(self, action: #selector(GeneralSettingsTableViewController.tapHideOTPSwitchDetailButton(_:)), for: UIControl.Event.touchDown)
|
||||
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hideOTPSwitch.bounds.width + 10, height: cell.contentView.bounds.height))
|
||||
accessoryView.addSubview(detailButton)
|
||||
accessoryView.addSubview(hideOTPSwitch)
|
||||
hideOTPSwitch.center.y = accessoryView.center.y
|
||||
detailButton.center.y = accessoryView.center.y
|
||||
cell.accessoryView = accessoryView
|
||||
cell.selectionStyle = .none
|
||||
hideOTPSwitch.isOn = Defaults.isHideOTPOn
|
||||
addDetailButton(to: cell, for: hideOTPSwitch, with: #selector(tapHideOTPSwitchDetailButton))
|
||||
case "RememberPgpKeyPassphrase".localize():
|
||||
cell.accessoryType = .none
|
||||
cell.selectionStyle = .none
|
||||
cell.accessoryView = rememberPGPPassphraseSwitch
|
||||
case "RememberGitCredentialPassphrase".localize():
|
||||
cell.accessoryType = .none
|
||||
cell.selectionStyle = .none
|
||||
cell.accessoryView = rememberGitCredentialPassphraseSwitch
|
||||
case "ShowFolders".localize():
|
||||
cell.accessoryType = .none
|
||||
cell.selectionStyle = .none
|
||||
cell.accessoryView = showFolderSwitch
|
||||
case "EnableGPGID".localize():
|
||||
cell.accessoryType = .none
|
||||
cell.selectionStyle = .none
|
||||
cell.accessoryView = enableGPGIDSwitch
|
||||
case "HidePasswordImages".localize():
|
||||
cell.accessoryType = .none
|
||||
let detailButton = UIButton(type: .detailDisclosure)
|
||||
hidePasswordImagesSwitch.frame = CGRect(x: detailButton.bounds.width + 10, y: 0, width: hidePasswordImagesSwitch.bounds.width, height: hidePasswordImagesSwitch.bounds.height)
|
||||
detailButton.frame = CGRect(x: 0, y: 5, width: detailButton.bounds.width, height: detailButton.bounds.height)
|
||||
detailButton.addTarget(self, action: #selector(GeneralSettingsTableViewController.tapHidePasswordImagesSwitchDetailButton(_:)), for: UIControl.Event.touchDown)
|
||||
let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: detailButton.bounds.width + hidePasswordImagesSwitch.bounds.width + 10, height: cell.contentView.bounds.height))
|
||||
accessoryView.addSubview(detailButton)
|
||||
accessoryView.addSubview(hidePasswordImagesSwitch)
|
||||
hidePasswordImagesSwitch.center.y = accessoryView.center.y
|
||||
detailButton.center.y = accessoryView.center.y
|
||||
cell.accessoryView = accessoryView
|
||||
cell.selectionStyle = .none
|
||||
hidePasswordImagesSwitch.isOn = Defaults.isHidePasswordImagesOn
|
||||
addDetailButton(to: cell, for: hidePasswordImagesSwitch, with: #selector(tapHidePasswordImagesSwitchDetailButton))
|
||||
case "AutoCopyOTP".localize():
|
||||
autoCopyOTPSwitch.isOn = Defaults.autoCopyOTP
|
||||
addDetailButton(to: cell, for: autoCopyOTPSwitch, with: #selector(tapAutoCopyOTPSwitchDetailButton))
|
||||
default:
|
||||
break
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
private func addDetailButton(to cell: UITableViewCell, for uiSwitch: UISwitch, with action: Selector) {
|
||||
let detailButton = UIButton(type: .detailDisclosure)
|
||||
uiSwitch.frame = CGRect(
|
||||
x: detailButton.bounds.width + 10,
|
||||
y: 0,
|
||||
width: uiSwitch.bounds.width,
|
||||
height: uiSwitch.bounds.height
|
||||
)
|
||||
detailButton.frame = CGRect(
|
||||
x: 0,
|
||||
y: 5,
|
||||
width: detailButton.bounds.width,
|
||||
height: detailButton.bounds.height
|
||||
)
|
||||
detailButton.addTarget(self, action: action, for: UIControl.Event.touchDown)
|
||||
let accessoryViewFrame = CGRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: detailButton.bounds.width + uiSwitch.bounds.width + 10,
|
||||
height: cell.contentView.bounds.height
|
||||
)
|
||||
let accessoryView = UIView(frame: accessoryViewFrame)
|
||||
accessoryView.addSubview(detailButton)
|
||||
accessoryView.addSubview(uiSwitch)
|
||||
uiSwitch.center.y = accessoryView.center.y
|
||||
detailButton.center.y = accessoryView.center.y
|
||||
cell.accessoryView = accessoryView
|
||||
}
|
||||
|
||||
@objc
|
||||
func tapHideUnknownSwitchDetailButton(_: Any?) {
|
||||
let alertMessage = "HideUnknownFieldsExplanation.".localize()
|
||||
|
|
@ -178,6 +180,13 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
func tapAutoCopyOTPSwitchDetailButton(_: Any?) {
|
||||
let alertMessage = "AutoCopyOTPExplanation.".localize()
|
||||
let alertTitle = "AutoCopyOTP".localize()
|
||||
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
func tapHidePasswordImagesSwitchDetailButton(_: Any?) {
|
||||
let alertMessage = "HidePasswordImagesExplanation.".localize()
|
||||
|
|
@ -197,6 +206,11 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
func autoCopyOTPSwitchAction(_: Any?) {
|
||||
Defaults.autoCopyOTP = autoCopyOTPSwitch.isOn
|
||||
}
|
||||
|
||||
@objc
|
||||
func rememberPGPPassphraseSwitchAction(_: Any?) {
|
||||
Defaults.isRememberPGPPassphraseOn = rememberPGPPassphraseSwitch.isOn
|
||||
|
|
|
|||
|
|
@ -80,8 +80,26 @@ class PasswordNavigationViewController: UIViewController {
|
|||
}
|
||||
|
||||
private func requestNotificationPermission() {
|
||||
// Ask for permission to receive notifications
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
let permissionOptions = UNAuthorizationOptions(arrayLiteral: .alert)
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: permissionOptions) { _, _ in }
|
||||
notificationCenter.requestAuthorization(options: permissionOptions) { _, _ in }
|
||||
|
||||
// Register notification action
|
||||
let copyAction = UNNotificationAction(
|
||||
identifier: Globals.otpNotificationCopyAction,
|
||||
title: "CopyToPasteboard".localize(),
|
||||
options: UNNotificationActionOptions(rawValue: 0)
|
||||
)
|
||||
let otpCategory = UNNotificationCategory(
|
||||
identifier: Globals.otpNotificationCategory,
|
||||
actions: [copyAction],
|
||||
intentIdentifiers: [],
|
||||
hiddenPreviewsBodyPlaceholder: "",
|
||||
options: []
|
||||
)
|
||||
notificationCenter.setNotificationCategories([otpCategory])
|
||||
notificationCenter.delegate = NotificationCenterDispatcher.shared
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@
|
|||
"HmacBased" = "HMAC-basiert";
|
||||
"None" = "Kein valides Token";
|
||||
"ExpiresIn" = "(läuft in %ds ab)";
|
||||
"OTPFor" = "Einmalpasswort für %@";
|
||||
"OTPForPassword" = "Einmalpasswort für %@";
|
||||
"OTPForPasswordCopied" = "Einmalpasswort für %@ kopiert";
|
||||
"CopyToPasteboard" = "In Zwischenablage kopieren";
|
||||
"AutoCopyOTP" = "OTPs automatisch kopieren";
|
||||
"AutoCopyOTPExplanation." = "Nachdem Login und Passwort automatisch in die vorgesehenen Felder gefüllt wurden, wird eine Benachrichtigung mit dem aktuellen Einmalpasswort angezeigt. Dieses kann in die Zwischenablage kopiert werden. Ist diese Option aktiviert, geschieht das Kopieren automatisch.";
|
||||
|
||||
// General (error) messages
|
||||
"Error" = "Fehler";
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@
|
|||
"HmacBased" = "HMAC-based";
|
||||
"None" = "None";
|
||||
"ExpiresIn" = "(expires in %ds)";
|
||||
"OTPFor" = "One-time Password for %@";
|
||||
"OTPForPassword" = "One-time password for %@";
|
||||
"OTPForPasswordCopied" = "One-time password for %@ copied";
|
||||
"CopyToPasteboard" = "Copy to pasteboard";
|
||||
"AutoCopyOTP" = "Automatically Copy OTPs";
|
||||
"AutoCopyOTPExplanation." = "After username and password have been auto-filled into a form by Pass, a notification is shown with the current one-time password which can be copied to the pasteboard. Enabling this option automatically copies the current one-time password.";
|
||||
|
||||
// General (error) messages
|
||||
"Error" = "Error";
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
|
|||
}()
|
||||
|
||||
private lazy var credentialProvider: CredentialProvider = { [unowned self] in
|
||||
CredentialProvider(viewController: self, extensionContext: extensionContext, afterDecryption: Utils.showNotificationWithOTP)
|
||||
CredentialProvider(viewController: self, extensionContext: extensionContext, afterDecryption: NotificationCenterDispatcher.showOTPNotification)
|
||||
}()
|
||||
|
||||
private lazy var passwordsTableEntries = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class ExtensionViewController: UIViewController {
|
|||
}()
|
||||
|
||||
private lazy var credentialProvider: CredentialProvider = { [unowned self] in
|
||||
CredentialProvider(viewController: self, extensionContext: extensionContext!, afterDecryption: Utils.showNotificationWithOTP)
|
||||
CredentialProvider(viewController: self, extensionContext: extensionContext!, afterDecryption: NotificationCenterDispatcher.showOTPNotification)
|
||||
}()
|
||||
|
||||
private lazy var passwordsTableEntries = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false)
|
||||
|
|
|
|||
|
|
@ -57,4 +57,6 @@ public extension DefaultsKeys {
|
|||
var passwordGenerator: DefaultsKey<PasswordGenerator> { .init("passwordGenerator", defaultValue: PasswordGenerator()) }
|
||||
|
||||
var encryptInArmored: DefaultsKey<Bool> { .init("encryptInArmored", defaultValue: false) }
|
||||
|
||||
var autoCopyOTP: DefaultsKey<Bool> { .init("autoCopyOTP", defaultValue: false) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ public final class Globals {
|
|||
public static let oneTimePasswordDots = "••••••"
|
||||
public static let passwordFont = UIFont(name: "Courier-Bold", size: UIFont.labelFontSize - 1)
|
||||
|
||||
public static let otpNotification = bundleIdentifier + ".notification.otp"
|
||||
public static let otpNotificationCategory = bundleIdentifier + ".notification.otp.category"
|
||||
public static let otpNotificationCopyAction = bundleIdentifier + ".notification.otp.action.copy"
|
||||
|
||||
// UI related
|
||||
public static let tableCellButtonSize = CGFloat(20.0)
|
||||
|
||||
|
|
|
|||
47
passKit/Helpers/NotificationCenterDispatcher.swift
Normal file
47
passKit/Helpers/NotificationCenterDispatcher.swift
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// NotificationCenterDispatcher.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Danny Moesch on 29.09.21.
|
||||
// Copyright © 2021 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
public class NotificationCenterDispatcher: NSObject, UNUserNotificationCenterDelegate {
|
||||
public static let shared = NotificationCenterDispatcher()
|
||||
|
||||
public func userNotificationCenter(_: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
if response.actionIdentifier == Globals.otpNotificationCopyAction {
|
||||
if let otp = response.notification.request.content.userInfo["otp"] as? String {
|
||||
UIPasteboard.general.string = otp
|
||||
}
|
||||
}
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
public static func showOTPNotification(password: Password) {
|
||||
guard let otp = password.currentOtp else {
|
||||
return
|
||||
}
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
notificationCenter.getNotificationSettings { state in
|
||||
guard state.authorizationStatus == .authorized else {
|
||||
return
|
||||
}
|
||||
let content = UNMutableNotificationContent()
|
||||
if Defaults.autoCopyOTP {
|
||||
content.title = "OTPForPasswordCopied".localize(password.name)
|
||||
} else {
|
||||
content.title = "OTPForPassword".localize(password.name)
|
||||
content.body = otp
|
||||
content.categoryIdentifier = Globals.otpNotificationCategory
|
||||
content.userInfo = [
|
||||
"path": password.namePath,
|
||||
"otp": otp,
|
||||
]
|
||||
}
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: Globals.otpNotification, content: content, trigger: trigger)
|
||||
notificationCenter.add(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,22 +70,4 @@ public enum Utils {
|
|||
return passphrase
|
||||
}
|
||||
}
|
||||
|
||||
public static func showNotificationWithOTP(password: Password) {
|
||||
guard let otp = password.currentOtp else {
|
||||
return
|
||||
}
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
notificationCenter.getNotificationSettings { state in
|
||||
guard state.authorizationStatus == .authorized else {
|
||||
return
|
||||
}
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = "OTPFor".localize(password.name)
|
||||
content.body = otp
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: "otpNotification", content: content, trigger: trigger)
|
||||
notificationCenter.add(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue