iOS: Show Home screen quick actions for recent tunnels
This commit is contained in:
parent
18119c6027
commit
abe5d3848d
|
@ -54,6 +54,7 @@
|
||||||
6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; };
|
6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; };
|
||||||
6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
|
6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
|
||||||
6F29A9432278518D00DC6A6B /* RecentTunnelsTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */; };
|
6F29A9432278518D00DC6A6B /* RecentTunnelsTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */; };
|
||||||
|
6F29A94722787B1600DC6A6B /* QuickActionItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29A94622787B1600DC6A6B /* QuickActionItem.swift */; };
|
||||||
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; };
|
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; };
|
||||||
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
|
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
|
||||||
6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
|
6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
|
||||||
|
@ -302,6 +303,7 @@
|
||||||
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = "<group>"; };
|
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = "<group>"; };
|
||||||
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
|
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
|
||||||
6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentTunnelsTracker.swift; sourceTree = "<group>"; };
|
6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentTunnelsTracker.swift; sourceTree = "<group>"; };
|
||||||
|
6F29A94622787B1600DC6A6B /* QuickActionItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickActionItem.swift; sourceTree = "<group>"; };
|
||||||
6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
|
6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
|
||||||
6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
|
6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
|
||||||
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
|
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -567,6 +569,7 @@
|
||||||
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */,
|
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */,
|
||||||
5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */,
|
5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */,
|
||||||
6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */,
|
6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */,
|
||||||
|
6F29A94622787B1600DC6A6B /* QuickActionItem.swift */,
|
||||||
6FF4AC23211EC472002C96EB /* Info.plist */,
|
6FF4AC23211EC472002C96EB /* Info.plist */,
|
||||||
6FF4AC482120B9E0002C96EB /* WireGuard.entitlements */,
|
6FF4AC482120B9E0002C96EB /* WireGuard.entitlements */,
|
||||||
);
|
);
|
||||||
|
@ -1350,6 +1353,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
6FE1765A21C90E87002690EA /* LocalizationHelper.swift in Sources */,
|
6FE1765A21C90E87002690EA /* LocalizationHelper.swift in Sources */,
|
||||||
|
6F29A94722787B1600DC6A6B /* QuickActionItem.swift in Sources */,
|
||||||
6FF3527221C2616C0008484E /* ringlogger.c in Sources */,
|
6FF3527221C2616C0008484E /* ringlogger.c in Sources */,
|
||||||
6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */,
|
6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */,
|
||||||
6FF3527321C2616C0008484E /* Logger.swift in Sources */,
|
6FF3527321C2616C0008484E /* Logger.swift in Sources */,
|
||||||
|
|
|
@ -311,6 +311,10 @@ class TunnelsManager {
|
||||||
return tunnels[index]
|
return tunnels[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mapTunnels<T>(transform: (TunnelContainer) throws -> T) rethrows -> [T] {
|
||||||
|
return try tunnels.map(transform)
|
||||||
|
}
|
||||||
|
|
||||||
func index(of tunnel: TunnelContainer) -> Int? {
|
func index(of tunnel: TunnelContainer) -> Int? {
|
||||||
return tunnels.firstIndex(of: tunnel)
|
return tunnels.firstIndex(of: tunnel)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
var mainVC: MainViewController?
|
var mainVC: MainViewController?
|
||||||
|
var isLaunchedForSpecificAction = false
|
||||||
|
|
||||||
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path)
|
Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path)
|
||||||
|
|
||||||
|
if let launchOptions = launchOptions {
|
||||||
|
if launchOptions[.url] != nil || launchOptions[.shortcutItem] != nil {
|
||||||
|
isLaunchedForSpecificAction = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let window = UIWindow(frame: UIScreen.main.bounds)
|
let window = UIWindow(frame: UIScreen.main.bounds)
|
||||||
window.backgroundColor = .white
|
window.backgroundColor = .white
|
||||||
self.window = window
|
self.window = window
|
||||||
|
@ -37,6 +44,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||||
mainVC?.refreshTunnelConnectionStatuses()
|
mainVC?.refreshTunnelConnectionStatuses()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applicationWillResignActive(_ application: UIApplication) {
|
||||||
|
guard let allTunnelNames = mainVC?.allTunnelNames() else { return }
|
||||||
|
application.shortcutItems = QuickActionItem.createItems(allTunnelNames: allTunnelNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||||
|
guard shortcutItem.type == QuickActionItem.type else {
|
||||||
|
completionHandler(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let tunnelName = shortcutItem.localizedTitle
|
||||||
|
mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false, shouldToggleStatus: true)
|
||||||
|
completionHandler(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
|
@ -45,7 +67,7 @@ extension AppDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
|
func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
|
||||||
return true
|
return !self.isLaunchedForSpecificAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? {
|
func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? {
|
||||||
|
@ -58,7 +80,7 @@ extension AppDelegate {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Show it when tunnelsManager is available
|
// Show it when tunnelsManager is available
|
||||||
mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false)
|
mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false, shouldToggleStatus: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class QuickActionItem: UIApplicationShortcutItem {
|
||||||
|
static let type = "WireGuardTunnelActivateAndShow"
|
||||||
|
|
||||||
|
init(tunnelName: String) {
|
||||||
|
super.init(type: QuickActionItem.type, localizedTitle: tunnelName, localizedSubtitle: nil, icon: nil, userInfo: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func createItems(allTunnelNames: [String]) -> [QuickActionItem] {
|
||||||
|
let numberOfItems = 10
|
||||||
|
// Currently, only 4 items shown by iOS, but that can increase in the future.
|
||||||
|
// iOS will discard additional items we give it.
|
||||||
|
var tunnelNames = RecentTunnelsTracker.recentlyActivatedTunnelNames(limit: numberOfItems)
|
||||||
|
let numberOfSlotsRemaining = numberOfItems - tunnelNames.count
|
||||||
|
if numberOfSlotsRemaining > 0 {
|
||||||
|
let moreTunnels = allTunnelNames.filter { !tunnelNames.contains($0) }.prefix(numberOfSlotsRemaining)
|
||||||
|
tunnelNames.append(contentsOf: moreTunnels)
|
||||||
|
}
|
||||||
|
return tunnelNames.map { QuickActionItem(tunnelName: $0) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,6 +57,10 @@ class MainViewController: UISplitViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allTunnelNames() -> [String]? {
|
||||||
|
guard let tunnelsManager = self.tunnelsManager else { return nil }
|
||||||
|
return tunnelsManager.mapTunnels { $0.name }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainViewController: TunnelsManagerActivationDelegate {
|
extension MainViewController: TunnelsManagerActivationDelegate {
|
||||||
|
@ -84,7 +88,7 @@ extension MainViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showTunnelDetailForTunnel(named tunnelName: String, animated: Bool) {
|
func showTunnelDetailForTunnel(named tunnelName: String, animated: Bool, shouldToggleStatus: Bool) {
|
||||||
let showTunnelDetailBlock: (TunnelsManager) -> Void = { [weak self] tunnelsManager in
|
let showTunnelDetailBlock: (TunnelsManager) -> Void = { [weak self] tunnelsManager in
|
||||||
if let tunnel = tunnelsManager.tunnel(named: tunnelName) {
|
if let tunnel = tunnelsManager.tunnel(named: tunnelName) {
|
||||||
let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
|
let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
|
||||||
|
@ -99,6 +103,13 @@ extension MainViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if shouldToggleStatus {
|
||||||
|
if tunnel.status == .inactive {
|
||||||
|
tunnelsManager.startActivation(of: tunnel)
|
||||||
|
} else if tunnel.status == .active {
|
||||||
|
tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let tunnelsManager = tunnelsManager {
|
if let tunnelsManager = tunnelsManager {
|
||||||
|
|
Loading…
Reference in New Issue