From 8e736a47985316d2554fdf28d750527e37e6b8ac Mon Sep 17 00:00:00 2001 From: Thatchapon Unprasert Date: Sun, 4 Feb 2024 10:27:41 +0700 Subject: [PATCH] App shortcuts (iOS 16+) support --- Intents.swift | 59 ++++++++++++++++++ Makefile | 4 +- README.md | 1 + Resources/Info.plist | 15 ++++- .../Metadata.appintents/extract.actionsdata | 1 + ...S.3b3c1ac8ff7d03aa7cc04f67ceedb451.version | Bin 0 -> 1 bytes .../Metadata.appintents/nlu/en_US.nlu.lzfse | Bin 0 -> 1360 bytes Resources/Metadata.appintents/root.ssu.yaml | 55 ++++++++++++++++ Resources/Metadata.appintents/version.json | 4 ++ ShortcutsProvider.swift | 31 +++++++++ TLAppDelegate.m | 10 +++ TLSceneDelegate.m | 12 ++++ control | 2 +- 13 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 Intents.swift create mode 100644 Resources/Metadata.appintents/extract.actionsdata create mode 100644 Resources/Metadata.appintents/nlu/en_US.3b3c1ac8ff7d03aa7cc04f67ceedb451.version create mode 100644 Resources/Metadata.appintents/nlu/en_US.nlu.lzfse create mode 100644 Resources/Metadata.appintents/root.ssu.yaml create mode 100644 Resources/Metadata.appintents/version.json create mode 100644 ShortcutsProvider.swift diff --git a/Intents.swift b/Intents.swift new file mode 100644 index 0000000..84fc20d --- /dev/null +++ b/Intents.swift @@ -0,0 +1,59 @@ +import AppIntents +import UIKit + +@available(iOS 16.0, *) +struct AmberOnIntent: AppIntent { + static let title: LocalizedStringResource = "Amber On" + static let description = IntentDescription( + "Turn on all amber LEDs", + categoryName: "Device" + ) + static let openAppWhenRun: Bool = true + + func perform() async throws -> some IntentResult { + if let url = URL(string: "leds://com.ps.TrollLEDs.AmberOn") { + if await UIApplication.shared.canOpenURL(url) { + await UIApplication.shared.open(url) + } + } + return .result() + } +} + +@available(iOS 16.0, *) +struct WhiteOnIntent: AppIntent { + static let title: LocalizedStringResource = "White On" + static let description = IntentDescription( + "Turn on all white LEDs", + categoryName: "Device" + ) + static let openAppWhenRun: Bool = true + + func perform() async throws -> some IntentResult { + if let url = URL(string: "leds://com.ps.TrollLEDs.WhiteOn") { + if await UIApplication.shared.canOpenURL(url) { + await UIApplication.shared.open(url) + } + } + return .result() + } +} + +@available(iOS 16.0, *) +struct AllOnIntent: AppIntent { + static let title: LocalizedStringResource = "All On" + static let description = IntentDescription( + "Turn on all LEDs", + categoryName: "Device" + ) + static let openAppWhenRun: Bool = true + + func perform() async throws -> some IntentResult { + if let url = URL(string: "leds://com.ps.TrollLEDs.AllOn") { + if await UIApplication.shared.canOpenURL(url) { + await UIApplication.shared.open(url) + } + } + return .result() + } +} diff --git a/Makefile b/Makefile index 70ffe0a..8fdee4d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ TARGET := iphone:clang:latest:11.0 INSTALL_TARGET_PROCESSES = TrollLEDs ARCHS = arm64 -PACKAGE_VERSION = 1.6.1 +PACKAGE_VERSION = 1.7.0 include $(THEOS)/makefiles/common.mk @@ -13,7 +13,7 @@ else IPA_NAME = $(APPLICATION_NAME) endif -$(APPLICATION_NAME)_FILES = main.m TLDeviceManager.m TLSceneDelegate.m TLAppDelegate.m TLRootViewController.m +$(APPLICATION_NAME)_FILES = main.m TLDeviceManager.m TLSceneDelegate.m TLAppDelegate.m TLRootViewController.m Intents.swift ShortcutsProvider.swift $(APPLICATION_NAME)_FRAMEWORKS = UIKit CoreGraphics CoreMedia $(APPLICATION_NAME)_CFLAGS = -fobjc-arc ifeq ($(UNSANDBOX),1) diff --git a/README.md b/README.md index 49a2648..21eaf68 100644 --- a/README.md +++ b/README.md @@ -75,3 +75,4 @@ Build `.tipa` (sandboxed and unsandboxed) and `.deb` (rootful and rootless) with 1. More accessible sliders (are they too small?) 2. Better error handling? +3. As AppIntents metadata can only be generated from Xcode, we should find a way to generate it from theos diff --git a/Resources/Info.plist b/Resources/Info.plist index 02375fb..98f57c6 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -44,9 +44,9 @@ iPhoneOS CFBundleShortVersionString - 1.6.1 + 1.7.0 CFBundleVersion - 1.6.1 + 1.7.0 LSRequiresIPhoneOS MinimumOSVersion @@ -117,5 +117,16 @@ com.ps.TrollLEDs.AllOn + CFBundleURLTypes + + + CFBundleURLName + com.ps.TrollLEDs + CFBundleURLSchemes + + leds + + + diff --git a/Resources/Metadata.appintents/extract.actionsdata b/Resources/Metadata.appintents/extract.actionsdata new file mode 100644 index 0000000..8797303 --- /dev/null +++ b/Resources/Metadata.appintents/extract.actionsdata @@ -0,0 +1 @@ +{"generator":{"version":"15.2","name":"xcode-tools"},"enums":[],"autoShortcutProviderMangledName":"9TrollLEDs0aB20AppShortcutsProviderV","entities":{},"autoShortcuts":[{"actionIdentifier":"AmberOnIntent","phraseTemplates":[{"key":"Turn on amber LEDs with ${applicationName}"}],"systemImageName":"flashlight.on.circle.fill","shortTitle":{"key":"Amber On"}},{"systemImageName":"flashlight.on.circle.fill","actionIdentifier":"WhiteOnIntent","shortTitle":{"key":"White On"},"phraseTemplates":[{"key":"Turn on white LEDs with ${applicationName}"}]},{"systemImageName":"flashlight.on.circle.fill","shortTitle":{"key":"All On"},"actionIdentifier":"AllOnIntent","phraseTemplates":[{"key":"Turn on all LEDs with ${applicationName}"}]}],"queries":{},"shortcutTileColor":14,"actions":{"AmberOnIntent":{"parameters":[],"presentationStyle":0,"identifier":"AmberOnIntent","outputFlags":0,"systemProtocols":[],"title":{"key":"Amber On"},"openAppWhenRun":true,"isDiscoverable":true,"mangledTypeNameByBundleIdentifier":{},"typeSpecificMetadata":[],"systemProtocolMetadata":[],"availabilityAnnotations":{"LNPlatformNameWildcard":{"introducedVersion":"*"},"LNPlatformNameIOS":{"introducedVersion":"16.0"}},"mangledTypeNameV2":"9TrollLEDs13AmberOnIntentV","mangledTypeName":"9TrollLEDs13AmberOnIntentV","effectiveBundleIdentifiers":[],"authenticationPolicy":0,"descriptionMetadata":{"descriptionText":{"key":"Turn on all amber LEDs"},"searchKeywords":[],"categoryName":{"title":{"key":"Device"}}},"requiredCapabilities":[],"mangledTypeNameByBundleIdentifierV2":{}},"WhiteOnIntent":{"identifier":"WhiteOnIntent","mangledTypeNameByBundleIdentifier":{},"isDiscoverable":true,"mangledTypeName":"9TrollLEDs13WhiteOnIntentV","requiredCapabilities":[],"openAppWhenRun":true,"authenticationPolicy":0,"mangledTypeNameByBundleIdentifierV2":{},"parameters":[],"typeSpecificMetadata":[],"descriptionMetadata":{"descriptionText":{"key":"Turn on all white LEDs"},"categoryName":{"title":{"key":"Device"}},"searchKeywords":[]},"systemProtocols":[],"presentationStyle":0,"outputFlags":0,"availabilityAnnotations":{"LNPlatformNameWildcard":{"introducedVersion":"*"},"LNPlatformNameIOS":{"introducedVersion":"16.0"}},"mangledTypeNameV2":"9TrollLEDs13WhiteOnIntentV","title":{"key":"White On"},"systemProtocolMetadata":[],"effectiveBundleIdentifiers":[]},"AllOnIntent":{"outputFlags":0,"systemProtocolMetadata":[],"availabilityAnnotations":{"LNPlatformNameWildcard":{"introducedVersion":"*"},"LNPlatformNameIOS":{"introducedVersion":"16.0"}},"presentationStyle":0,"isDiscoverable":true,"title":{"key":"All On"},"mangledTypeNameByBundleIdentifier":{},"descriptionMetadata":{"searchKeywords":[],"categoryName":{"title":{"key":"Device"}},"descriptionText":{"key":"Turn on all LEDs"}},"systemProtocols":[],"mangledTypeNameByBundleIdentifierV2":{},"parameters":[],"requiredCapabilities":[],"openAppWhenRun":true,"effectiveBundleIdentifiers":[],"authenticationPolicy":0,"mangledTypeName":"9TrollLEDs11AllOnIntentV","typeSpecificMetadata":[],"mangledTypeNameV2":"9TrollLEDs11AllOnIntentV","identifier":"AllOnIntent"}},"negativePhrases":[],"version":1} \ No newline at end of file diff --git a/Resources/Metadata.appintents/nlu/en_US.3b3c1ac8ff7d03aa7cc04f67ceedb451.version b/Resources/Metadata.appintents/nlu/en_US.3b3c1ac8ff7d03aa7cc04f67ceedb451.version new file mode 100644 index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d GIT binary patch literal 1 IcmZPo000310RR91 literal 0 HcmV?d00001 diff --git a/Resources/Metadata.appintents/nlu/en_US.nlu.lzfse b/Resources/Metadata.appintents/nlu/en_US.nlu.lzfse new file mode 100644 index 0000000000000000000000000000000000000000..c3f8cb1a726c3c9af8df930d6763860c118c5726 GIT binary patch literal 1360 zcmX9;Z)_7~7=OEU$)*xJ;3O{K%T0EHRy@CEUY*QR)~&L+ddA;S_KEJT%7V0*fWeFPR(s z)$5Ea;I5r<;6b)W(j?ZvNrOdLmXd6*rpj{1p*9_`1g5CCg7(f-_h2Q;3TzDPy3mJN zLuC&uiooi^prrJ%GB$SUpf-;URxn@}RqoPE`(oz{%y3@Aae1(=4em?(tV^cy-qJ`z zSW9z>xH;PxQDXo?--37r)r?$zUFGV*clQQxSeFb8>WNRL;w-uFVj(9)Spq5^$dZC} zRtT#J_o~5^`l5I`P0Nun=yVirv!rt!8dOkk7#HX&Fy^%LVZx+oY3wpa}g^ zpI~4%w)q^46$osj;x)8A*QQDRwpSlSB@IUmNmX>%Y7#g^#bT%PeJd8>U6a(WQ!S(C zbG=xWna^6vi~p06DKtf(fG zD2`f!>x!>8wYA^Y;a*F{>;V}k!B7cQie`3=2-@P`?0{qtwp)l0EQ7I9DLuV=#Vljy zSVc7?QHlsPXL6#Z#^AK6%+ruxw2ijE+nv^8gO6Z4%|hULTog0*dDbX%<|RYMQ5d7d z7o_6Pr&BMPw>l#l4!$O|USA}9nr5~O?T|l?I{AE6vVS=$84`AD7wsC;r7|Gb6u)tB>ECqubLtL4l3U4+?rGS`efKr zNHzD6iA#}$L3}AnMpx+UAw>ww81Byg!Ai<9f#WpO^aO3o^(HjMvXLm_YDoilxG0{s z1vk61{ZUC@fc{pS2`p)zduoc89Fy)O@hZjg?c_&|692ka>{nY|Z`>AggRHCqJN$>H z{b2M_X6I7}aKd{?h~Qo+27g*eb71=v@R}wpzNSGo(vEPYO6Iv0nah6A9*K2cHl$|vNQ3JH3(C8?7&7l z9p^bqC8~tO#k>#;V{PSOMZj}_8$Q;xf!i>~^45wxR!$x5aV94!-N5zA zWjk`&=BIw$U%cjQvyEprc^kRvA7)&8!h0fKAp%9QIm+!3gyu+u^NIVKBN#`+zP;Yk P#{{M4_Fshilb(M8o>C)> literal 0 HcmV?d00001 diff --git a/Resources/Metadata.appintents/root.ssu.yaml b/Resources/Metadata.appintents/root.ssu.yaml new file mode 100644 index 0000000..91dfb5e --- /dev/null +++ b/Resources/Metadata.appintents/root.ssu.yaml @@ -0,0 +1,55 @@ +apiVersion: ssu/v1 +nlu: + shortcuts: + bundleIdentifier: com.ps.TrollLEDs + intents: + - metadata: + name: AmberOnIntent_0 + title: Amber On + corpuses: + training: + - locale: en_US + utterances: + - Turn on amber LEDs with ${+applicationName} + - metadata: + name: WhiteOnIntent_1 + title: White On + corpuses: + training: + - locale: en_US + utterances: + - Turn on white LEDs with ${+applicationName} + - metadata: + name: AllOnIntent_2 + title: All On + corpuses: + training: + - locale: en_US + utterances: + - Turn on all LEDs with ${+applicationName} + - metadata: + name: +negative + corpuses: + training: + - locale: en_US + utterances: + - ${+standardAppShortcutNegatives} +variables: +- name: +applicationName + type: ssu/expansion + definitions: + - locale: en_US + synomyms: + - TrollLEDs +- name: +prefix + type: ssu/expansion + definitions: + - locale: en_US + synomyms: + $ref: ssu://Resources/prefix/en.yaml +- name: +standardAppShortcutNegatives + type: ssu/expansion + definitions: + - locale: en_US + synomyms: + $ref: ssu://Resources/standardAppShortcutNegatives/en.yaml diff --git a/Resources/Metadata.appintents/version.json b/Resources/Metadata.appintents/version.json new file mode 100644 index 0000000..cb4f265 --- /dev/null +++ b/Resources/Metadata.appintents/version.json @@ -0,0 +1,4 @@ +{ + "toolsVersion" : "15.2", + "version" : "3.0" +} \ No newline at end of file diff --git a/ShortcutsProvider.swift b/ShortcutsProvider.swift new file mode 100644 index 0000000..5e84efb --- /dev/null +++ b/ShortcutsProvider.swift @@ -0,0 +1,31 @@ +import AppIntents + +@available(iOS 16.0, *) +struct TrollLEDsAppShortcutsProvider: AppShortcutsProvider { + @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { + AppShortcut( + intent: AmberOnIntent(), + phrases: [ + "Turn on amber LEDs with \(.applicationName)" + ], + shortTitle: "Amber On", + systemImageName: "flashlight.on.circle.fill" + ) + AppShortcut( + intent: WhiteOnIntent(), + phrases: [ + "Turn on white LEDs with \(.applicationName)" + ], + shortTitle: "White On", + systemImageName: "flashlight.on.circle.fill" + ) + AppShortcut( + intent: AllOnIntent(), + phrases: [ + "Turn on all LEDs with \(.applicationName)" + ], + shortTitle: "All On", + systemImageName: "flashlight.on.circle.fill" + ) + } +} diff --git a/TLAppDelegate.m b/TLAppDelegate.m index bd81383..36490eb 100644 --- a/TLAppDelegate.m +++ b/TLAppDelegate.m @@ -41,4 +41,14 @@ - (void)appDidEnterBackground:(UIApplication *)application { }); } +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { + if (!url) return NO; + if ([url.scheme isEqualToString:@"leds"]) { + NSString *action = url.host; + if (action) + [_myViewController handleShortcutAction:action]; + } + return YES; +} + @end diff --git a/TLSceneDelegate.m b/TLSceneDelegate.m index a0156f1..9968443 100644 --- a/TLSceneDelegate.m +++ b/TLSceneDelegate.m @@ -35,4 +35,16 @@ - (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)) { }); } +- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts API_AVAILABLE(ios(13.0)) { + for (UIOpenURLContext *context in URLContexts) { + NSURL *url = context.URL; + if (!url) continue; + if ([url.scheme isEqualToString:@"leds"]) { + NSString *action = url.host; + if (action) + [_myViewController handleShortcutAction:action]; + } + } +} + @end diff --git a/control b/control index 25aa5b3..5daf271 100644 --- a/control +++ b/control @@ -1,7 +1,7 @@ Package: com.ps.trollleds Name: TrollLEDs Version: 1.0.0 -Depends: firmware (>= 11.0), gsc.camera-flash +Depends: firmware (>= 11.0), firmware (>= 12.2) | ${LIBSWIFT}, gsc.camera-flash Architecture: iphoneos-arm Description: Control flashlight LEDs with eases. Maintainer: PoomSmart