Add Open URL intent

This commit is contained in:
Shadowfacts 2021-09-30 11:06:03 -04:00
parent 144695cc96
commit 2b82e3b545
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
8 changed files with 225 additions and 8 deletions

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import Intents
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
@ -32,6 +33,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Use this method to release any resources that were specific to the discarded scenes, as they will not return. // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
} }
func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any? {
switch intent {
case is OpenURLIntent:
return OpenURLIntentHandler()
default:
return nil
}
}
} }

View File

@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSUserActivityTypes</key>
<array>
<string>$(PRODUCE_BUNDLE_IDENTIFIER).activity.browse</string>
</array>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
@ -37,8 +33,17 @@
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>INIntentsSupported</key>
<array>
<string>OpenURLIntent</string>
</array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSUserActivityTypes</key>
<array>
<string>$(PRODUCE_BUNDLE_IDENTIFIER).activity.browse</string>
<string>OpenURLIntent</string>
</array>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>INEnums</key>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.2</string>
<key>INIntentDefinitionNamespace</key>
<string>YzEWx7</string>
<key>INIntentDefinitionSystemVersion</key>
<string>20F71</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>13A233</string>
<key>INIntentDefinitionToolsVersion</key>
<string>13.0</string>
<key>INIntents</key>
<array>
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentConfigurable</key>
<true/>
<key>INIntentDescription</key>
<string>Open a Gemini link in Rocketeer.</string>
<key>INIntentDescriptionID</key>
<string>q39SFr</string>
<key>INIntentInput</key>
<string>url</string>
<key>INIntentLastParameterTag</key>
<integer>2</integer>
<key>INIntentManagedParameterCombinations</key>
<dict>
<key>url</key>
<dict>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string>Open ${url}</string>
<key>INIntentParameterCombinationTitleID</key>
<string>S2D5Se</string>
<key>INIntentParameterCombinationUpdatesLinked</key>
<true/>
</dict>
</dict>
<key>INIntentName</key>
<string>OpenURL</string>
<key>INIntentParameterCombinations</key>
<dict>
<key>url</key>
<dict>
<key>INIntentParameterCombinationIsLinked</key>
<true/>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string>Open ${url}</string>
<key>INIntentParameterCombinationTitleID</key>
<string>vxeXI3</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterConfigurable</key>
<true/>
<key>INIntentParameterDisplayName</key>
<string>URL</string>
<key>INIntentParameterDisplayNameID</key>
<string>8RER8H</string>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>url</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Configuration</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>What URL would you like to open?</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>Bssksg</string>
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
</array>
<key>INIntentParameterSupportsResolution</key>
<true/>
<key>INIntentParameterTag</key>
<integer>2</integer>
<key>INIntentParameterType</key>
<string>URL</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
<dict>
<key>INIntentResponseCodeName</key>
<string>failure</string>
</dict>
</array>
</dict>
<key>INIntentTitle</key>
<string>Open URL</string>
<key>INIntentTitleID</key>
<string>yqBCLe</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>Open</string>
</dict>
</array>
<key>INTypes</key>
<array/>
</dict>
</plist>

View File

@ -0,0 +1,32 @@
//
// OpenURLIntentHandler.swift
// Gemini-iOS
//
// Created by Shadowfacts on 9/29/21.
//
import Intents
import UIKit
class OpenURLIntentHandler: NSObject, OpenURLIntentHandling {
func resolveUrl(for intent: OpenURLIntent, with completion: @escaping (INURLResolutionResult) -> Void) {
guard let url = intent.url,
url.scheme?.lowercased() == "gemini" else {
completion(.unsupported())
return
}
completion(.success(with: url))
}
func handle(intent: OpenURLIntent, completion: @escaping (OpenURLIntentResponse) -> Void) {
print("handling intent")
guard let url = intent.url,
url.scheme?.lowercased() == "gemini" else {
completion(OpenURLIntentResponse(code: .failure, userActivity: nil))
return
}
completion(OpenURLIntentResponse(code: .continueInApp, userActivity: NSUserActivity(geminiURL: url)))
}
}

View File

@ -10,6 +10,7 @@ import SwiftUI
import BrowserCore import BrowserCore
import SafariServices import SafariServices
import Combine import Combine
import Intents
class SceneDelegate: UIResponder, UIWindowSceneDelegate { class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@ -42,7 +43,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
Preferences.shared.$theme Preferences.shared.$theme
.sink { (newStyle) in .sink { [unowned self] (newStyle) in
self.window!.overrideUserInterfaceStyle = newStyle self.window!.overrideUserInterfaceStyle = newStyle
} }
.store(in: &cancellables) .store(in: &cancellables)
@ -54,6 +55,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
} }
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if let url = userActivity.geminiURL {
navigationManager.changeURL(url)
}
}
func sceneDidDisconnect(_ scene: UIScene) { func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system. // Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded. // This occurs shortly after the scene enters the background, or when its session is discarded.
@ -87,9 +94,21 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
private func createNavigationManager(for session: UISceneSession, with connectionOptions: UIScene.ConnectionOptions) -> NavigationManager { private func createNavigationManager(for session: UISceneSession, with connectionOptions: UIScene.ConnectionOptions) -> NavigationManager {
// try to restore the existing nav manager if there is one
if let manager = session.stateRestorationActivity?.navigationManager {
// if there's a user activity with a gemini url (e.g., from OpenURLIntentHandler),
// navigate the existing manager to that URL
if let newURL = connectionOptions.userActivities.first?.geminiURL {
manager.changeURL(newURL)
}
return manager
}
// otherwise, work out the initial URL and create a new manager
var initialURL: URL? = nil var initialURL: URL? = nil
if let activity = session.stateRestorationActivity ?? connectionOptions.userActivities.first { if let activity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
if let manager = activity.navigationManager { if let manager = activity.navigationManager {
return manager return manager
} else if let url = activity.geminiURL { } else if let url = activity.geminiURL {

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import BrowserCore import BrowserCore
private let type = "space.vaccor.Gemini.activity.browse" private let type = "\(Bundle.main.bundleIdentifier!).activity.browse"
private let encoder = PropertyListEncoder() private let encoder = PropertyListEncoder()
private let decoder = PropertyListDecoder() private let decoder = PropertyListDecoder()

View File

@ -52,6 +52,9 @@
D688F65A258C2256003A0A73 /* BrowserNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D688F659258C2256003A0A73 /* BrowserNavigationController.swift */; }; D688F65A258C2256003A0A73 /* BrowserNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D688F659258C2256003A0A73 /* BrowserNavigationController.swift */; };
D688F663258C2479003A0A73 /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = D688F662258C2479003A0A73 /* UIViewController+Children.swift */; }; D688F663258C2479003A0A73 /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = D688F662258C2479003A0A73 /* UIViewController+Children.swift */; };
D68C1E002703EA13002D642B /* UserActivities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68C1DFF2703EA13002D642B /* UserActivities.swift */; }; D68C1E002703EA13002D642B /* UserActivities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68C1DFF2703EA13002D642B /* UserActivities.swift */; };
D68C1E1927055E09002D642B /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = D68C1E1827055E09002D642B /* Intents.intentdefinition */; };
D68C1E1C27055EB0002D642B /* OpenURLIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68C1E1B27055EB0002D642B /* OpenURLIntentHandler.swift */; };
D68C1E1E270605A7002D642B /* BrowserHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68C1E1D270605A7002D642B /* BrowserHelper.swift */; };
D691A64E25217C6F00348C4B /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691A64D25217C6F00348C4B /* Preferences.swift */; }; D691A64E25217C6F00348C4B /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691A64D25217C6F00348C4B /* Preferences.swift */; };
D691A66725217FD800348C4B /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691A66625217FD800348C4B /* PreferencesView.swift */; }; D691A66725217FD800348C4B /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691A66625217FD800348C4B /* PreferencesView.swift */; };
D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */; }; D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */; };
@ -323,6 +326,9 @@
D688F659258C2256003A0A73 /* BrowserNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserNavigationController.swift; sourceTree = "<group>"; }; D688F659258C2256003A0A73 /* BrowserNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserNavigationController.swift; sourceTree = "<group>"; };
D688F662258C2479003A0A73 /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = "<group>"; }; D688F662258C2479003A0A73 /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = "<group>"; };
D68C1DFF2703EA13002D642B /* UserActivities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivities.swift; sourceTree = "<group>"; }; D68C1DFF2703EA13002D642B /* UserActivities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivities.swift; sourceTree = "<group>"; };
D68C1E1827055E09002D642B /* Intents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = "<group>"; };
D68C1E1B27055EB0002D642B /* OpenURLIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLIntentHandler.swift; sourceTree = "<group>"; };
D68C1E1D270605A7002D642B /* BrowserHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserHelper.swift; sourceTree = "<group>"; };
D691A64D25217C6F00348C4B /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; }; D691A64D25217C6F00348C4B /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
D691A66625217FD800348C4B /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; }; D691A66625217FD800348C4B /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiDataTask.swift; sourceTree = "<group>"; }; D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiDataTask.swift; sourceTree = "<group>"; };
@ -596,6 +602,14 @@
path = Resources; path = Resources;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D68C1E1A27055EA0002D642B /* Intents */ = {
isa = PBXGroup;
children = (
D68C1E1B27055EB0002D642B /* OpenURLIntentHandler.swift */,
);
path = Intents;
sourceTree = "<group>";
};
D6E152A324BFFDF500FDF9D3 /* Gemini-iOS */ = { D6E152A324BFFDF500FDF9D3 /* Gemini-iOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -615,9 +629,11 @@
D653F40A267996FF004E32B1 /* ActivityItemSource.swift */, D653F40A267996FF004E32B1 /* ActivityItemSource.swift */,
D653F40E2679A0AB004E32B1 /* SetHomepageActivity.swift */, D653F40E2679A0AB004E32B1 /* SetHomepageActivity.swift */,
D68C1DFF2703EA13002D642B /* UserActivities.swift */, D68C1DFF2703EA13002D642B /* UserActivities.swift */,
D68C1E1A27055EA0002D642B /* Intents */,
D688F618258AD231003A0A73 /* Resources */, D688F618258AD231003A0A73 /* Resources */,
D6376A6E26DDAF57005AD89C /* Vendor */, D6376A6E26DDAF57005AD89C /* Vendor */,
D6E152AA24BFFDF600FDF9D3 /* Assets.xcassets */, D6E152AA24BFFDF600FDF9D3 /* Assets.xcassets */,
D68C1E1827055E09002D642B /* Intents.intentdefinition */,
D6E152AF24BFFDF600FDF9D3 /* LaunchScreen.storyboard */, D6E152AF24BFFDF600FDF9D3 /* LaunchScreen.storyboard */,
D6E152B224BFFDF600FDF9D3 /* Info.plist */, D6E152B224BFFDF600FDF9D3 /* Info.plist */,
D6E152AC24BFFDF600FDF9D3 /* Preview Content */, D6E152AC24BFFDF600FDF9D3 /* Preview Content */,
@ -639,6 +655,7 @@
D6E152D224C0007200FDF9D3 /* BrowserCore.h */, D6E152D224C0007200FDF9D3 /* BrowserCore.h */,
D6E152D324C0007200FDF9D3 /* Info.plist */, D6E152D324C0007200FDF9D3 /* Info.plist */,
D69F00AF24BEA84D00E37622 /* NavigationManager.swift */, D69F00AF24BEA84D00E37622 /* NavigationManager.swift */,
D68C1E1D270605A7002D642B /* BrowserHelper.swift */,
D626646024BBF1C200DF9B88 /* BrowserView.swift */, D626646024BBF1C200DF9B88 /* BrowserView.swift */,
); );
path = BrowserCore; path = BrowserCore;
@ -1141,8 +1158,10 @@
D653F40F2679A0AB004E32B1 /* SetHomepageActivity.swift in Sources */, D653F40F2679A0AB004E32B1 /* SetHomepageActivity.swift in Sources */,
D688F65A258C2256003A0A73 /* BrowserNavigationController.swift in Sources */, D688F65A258C2256003A0A73 /* BrowserNavigationController.swift in Sources */,
D6BC9AD7258FC8B3008652BC /* TableOfContentsView.swift in Sources */, D6BC9AD7258FC8B3008652BC /* TableOfContentsView.swift in Sources */,
D68C1E1927055E09002D642B /* Intents.intentdefinition in Sources */,
D688F663258C2479003A0A73 /* UIViewController+Children.swift in Sources */, D688F663258C2479003A0A73 /* UIViewController+Children.swift in Sources */,
D653F40D26799F2F004E32B1 /* HomepagePrefView.swift in Sources */, D653F40D26799F2F004E32B1 /* HomepagePrefView.swift in Sources */,
D68C1E1C27055EB0002D642B /* OpenURLIntentHandler.swift in Sources */,
D691A64E25217C6F00348C4B /* Preferences.swift in Sources */, D691A64E25217C6F00348C4B /* Preferences.swift in Sources */,
D6BC9ABC258E9862008652BC /* NavigationBarView.swift in Sources */, D6BC9ABC258E9862008652BC /* NavigationBarView.swift in Sources */,
D68C1E002703EA13002D642B /* UserActivities.swift in Sources */, D68C1E002703EA13002D642B /* UserActivities.swift in Sources */,
@ -1153,6 +1172,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D68C1E1E270605A7002D642B /* BrowserHelper.swift in Sources */,
D6E152F124C000A000FDF9D3 /* NavigationManager.swift in Sources */, D6E152F124C000A000FDF9D3 /* NavigationManager.swift in Sources */,
D6E152F224C000CD00FDF9D3 /* BrowserView.swift in Sources */, D6E152F224C000CD00FDF9D3 /* BrowserView.swift in Sources */,
); );

View File

@ -2,7 +2,7 @@
"object": { "object": {
"pins": [ "pins": [
{ {
"package": "swift-html-entities", "package": "HTMLEntities",
"repositoryURL": "https://github.com/Kitura/swift-html-entities", "repositoryURL": "https://github.com/Kitura/swift-html-entities",
"state": { "state": {
"branch": null, "branch": null,