]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
iOS: Show Home screen quick actions for recent tunnels
authorRoopesh Chander <roop@roopc.net>
Tue, 30 Apr 2019 12:58:06 +0000 (18:28 +0530)
committerJason A. Donenfeld <Jason@zx2c4.com>
Sat, 25 May 2019 11:24:01 +0000 (13:24 +0200)
Signed-off-by: Roopesh Chander <roop@roopc.net>
WireGuard/WireGuard.xcodeproj/project.pbxproj
WireGuard/WireGuard/Tunnel/TunnelsManager.swift
WireGuard/WireGuard/UI/iOS/AppDelegate.swift
WireGuard/WireGuard/UI/iOS/QuickActionItem.swift [new file with mode: 0644]
WireGuard/WireGuard/UI/iOS/ViewController/MainViewController.swift

index d499929456d32f10d6dfcae19fdd46aeaaf61755..c107701586763a18f6067abc2690f4ad80adcae2 100644 (file)
@@ -55,6 +55,7 @@
                6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
                6F3E02E9228000F6001FE7E3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3E02E8228000F6001FE7E3 /* MainMenu.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 */; };
                6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
                6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
                6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
                6F3E02E8228000F6001FE7E3 /* MainMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainMenu.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>"; };
                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>"; };
                                6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */,
                                5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */,
                                6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */,
+                               6F29A94622787B1600DC6A6B /* QuickActionItem.swift */,
                                6FF4AC23211EC472002C96EB /* Info.plist */,
                                6FF4AC482120B9E0002C96EB /* WireGuard.entitlements */,
                        );
                        buildActionMask = 2147483647;
                        files = (
                                6FE1765A21C90E87002690EA /* LocalizationHelper.swift in Sources */,
+                               6F29A94722787B1600DC6A6B /* QuickActionItem.swift in Sources */,
                                6FF3527221C2616C0008484E /* ringlogger.c in Sources */,
                                6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */,
                                6FF3527321C2616C0008484E /* Logger.swift in Sources */,
index 460a9f6ccc9186d9234e11143de284cd684138ad..27811317152f8488cb51d530b9b75af92949757e 100644 (file)
@@ -311,6 +311,10 @@ class TunnelsManager {
         return tunnels[index]
     }
 
+    func mapTunnels<T>(transform: (TunnelContainer) throws -> T) rethrows -> [T] {
+        return try tunnels.map(transform)
+    }
+
     func index(of tunnel: TunnelContainer) -> Int? {
         return tunnels.firstIndex(of: tunnel)
     }
index d2742808e08f2592011c95f1513f092028b7f1ab..8884d44cfeb1e000597dc36e522314e911505427 100644 (file)
@@ -9,10 +9,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
     var window: UIWindow?
     var mainVC: MainViewController?
+    var isLaunchedForSpecificAction = false
 
     func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         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)
         window.backgroundColor = .white
         self.window = window
@@ -37,6 +44,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     func applicationDidBecomeActive(_ application: UIApplication) {
         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 {
@@ -45,7 +67,7 @@ extension AppDelegate {
     }
 
     func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
-        return true
+        return !self.isLaunchedForSpecificAction
     }
 
     func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? {
@@ -58,7 +80,7 @@ extension AppDelegate {
                 }
             } else {
                 // Show it when tunnelsManager is available
-                mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false)
+                mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false, shouldToggleStatus: false)
             }
         }
         return nil
diff --git a/WireGuard/WireGuard/UI/iOS/QuickActionItem.swift b/WireGuard/WireGuard/UI/iOS/QuickActionItem.swift
new file mode 100644 (file)
index 0000000..4367fa9
--- /dev/null
@@ -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) }
+    }
+}
index 380299c249682bcd3803022a901585f44b6d76c9..1929c793f47b1c06acb9f5a378ff16648365f957 100644 (file)
@@ -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 {
@@ -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
             if let tunnel = tunnelsManager.tunnel(named: tunnelName) {
                 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 {