From e4d8c4a1a18ec1a0a418e9af9c7f9e02b868e34c Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 5 Dec 2024 23:30:00 -0500 Subject: [PATCH] Handoff prototype --- DuckDuckGo/Application/AppDelegate.swift | 13 ++++++ DuckDuckGo/Info.plist | 4 ++ .../Tab/View/BrowserTabViewController.swift | 46 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index 3d79b94ed3..ebcc07561e 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -555,6 +555,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { Task { @MainActor in await subscriptionCookieManager.refreshSubscriptionCookie() } + + WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController.browserTabViewController.becomeCurrentActivity() } private func initializeSync() { @@ -627,6 +629,17 @@ final class AppDelegate: NSObject, NSApplicationDelegate { urlEventHandler.handleFiles(files) } + func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void) -> Bool { + guard userActivity.activityType == "com.duckduckgo.mobile.ios.web-browsing", + let mainWindowController = WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController else { + return false + } + + restorationHandler([mainWindowController.browserTabViewController]) + + return true + } + // MARK: - PixelKit static func configurePixelKit() { diff --git a/DuckDuckGo/Info.plist b/DuckDuckGo/Info.plist index 68f7172023..f9834aeaa4 100644 --- a/DuckDuckGo/Info.plist +++ b/DuckDuckGo/Info.plist @@ -540,6 +540,10 @@ NSSupportsSuddenTermination + NSUserActivityTypes + + com.duckduckgo.mobile.ios.web-browsing + SUBSCRIPTION_APP_GROUP $(SUBSCRIPTION_APP_GROUP) SUEnableAutomaticChecks diff --git a/DuckDuckGo/Tab/View/BrowserTabViewController.swift b/DuckDuckGo/Tab/View/BrowserTabViewController.swift index 4fbb30cf21..0d91da877e 100644 --- a/DuckDuckGo/Tab/View/BrowserTabViewController.swift +++ b/DuckDuckGo/Tab/View/BrowserTabViewController.swift @@ -323,6 +323,8 @@ final class BrowserTabViewController: NSViewController { self.adjustFirstResponder(force: true) removeExistingDialog() + + self.updateCurrentActivity(url: selectedTabViewModel?.tab.content.urlForWebView) } .store(in: &cancellables) } @@ -615,6 +617,13 @@ final class BrowserTabViewController: NSViewController { tabViewModel?.tab.webViewDidFinishNavigationPublisher.sink { [weak self] in self?.updateStateAndPresentContextualOnboarding() }.store(in: &tabViewModelCancellables) + + tabViewModel?.tab.webView.publisher(for: \.url) + .dropFirst() + .receive(on: DispatchQueue.main) + .sink { [weak self] url in + self?.updateCurrentActivity(url: url) + }.store(in: &tabViewModelCancellables) } private func subscribeToUserDialogs(of tabViewModel: TabViewModel?) { @@ -1510,3 +1519,40 @@ private extension NSViewController { } } + +extension BrowserTabViewController { + override func restoreUserActivityState(_ userActivity: NSUserActivity) { + guard userActivity.activityType == "com.duckduckgo.mobile.ios.web-browsing", let url = userActivity.webpageURL else { + return + } + openNewTab(with: .url(url, credential: nil, source: .appOpenUrl)) + } + + func becomeCurrentActivity() { + if userActivity?.webpageURL == nil { + userActivity?.invalidate() + userActivity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb) + userActivity?.webpageURL = nil + } + + userActivity?.becomeCurrent() + } + + private func updateCurrentActivity(url: URL?) { + let newURL: URL? = { + guard let url, let scheme = url.scheme, ["http", "https"].contains(scheme) else { return nil } + return url + }() + guard newURL != userActivity?.webpageURL else { return } + + userActivity?.invalidate() + if newURL != nil { + userActivity = NSUserActivity(activityType: "com.duckduckgo.mobile.ios.web-browsing") + } else { + userActivity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb) + } + userActivity?.webpageURL = newURL + + userActivity?.becomeCurrent() + } +}