diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index fbd57881c..a629b6072 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -332,7 +332,7 @@ BFECAC9624FD98BB0077C41F /* NSError+ALTServerError.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314922A060F400370A3C /* NSError+ALTServerError.m */; }; BFF00D302501BD7D00746320 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */; }; BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */; }; - BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D332501BDCF00746320 /* IntentHandler.swift */; }; + BFF00D342501BDCF00746320 /* RefreshAllIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D332501BDCF00746320 /* RefreshAllIntentHandler.swift */; }; BFF0394B25F0551600BE607D /* MenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0394A25F0551600BE607D /* MenuController.swift */; }; BFF0B68E23219520007A79E1 /* PatreonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68D23219520007A79E1 /* PatreonViewController.swift */; }; BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */; }; @@ -370,6 +370,7 @@ D5F2F6A92720B7C20081CCF5 /* PatchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F2F6A82720B7C20081CCF5 /* PatchViewController.swift */; }; D5F99A1828D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */; }; D5F99A1A28D12B1400476A16 /* StoreApp10ToStoreApp11Policy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F99A1928D12B1400476A16 /* StoreApp10ToStoreApp11Policy.swift */; }; + D79F3FE42923206E0005D387 /* EnableJITIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79F3FE32923206E0005D387 /* EnableJITIntentHandler.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -798,7 +799,7 @@ BFE972E2260A8B2700D0BDAC /* NSError+libimobiledevice.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSError+libimobiledevice.mm"; sourceTree = ""; }; BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = ""; }; BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundRefreshAppsOperation.swift; sourceTree = ""; }; - BFF00D332501BDCF00746320 /* IntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; + BFF00D332501BDCF00746320 /* RefreshAllIntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshAllIntentHandler.swift; sourceTree = ""; }; BFF0394A25F0551600BE607D /* MenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuController.swift; sourceTree = ""; }; BFF0B68D23219520007A79E1 /* PatreonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonViewController.swift; sourceTree = ""; }; BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonComponents.swift; sourceTree = ""; }; @@ -843,6 +844,7 @@ D5F2F6A82720B7C20081CCF5 /* PatchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchViewController.swift; sourceTree = ""; }; D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore10ToAltStore11.xcmappingmodel; sourceTree = ""; }; D5F99A1928D12B1400476A16 /* StoreApp10ToStoreApp11Policy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp10ToStoreApp11Policy.swift; sourceTree = ""; }; + D79F3FE32923206E0005D387 /* EnableJITIntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnableJITIntentHandler.swift; sourceTree = ""; }; EA79A60285C6AF5848AA16E9 /* Pods-AltStore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStore.debug.xcconfig"; path = "Target Support Files/Pods-AltStore/Pods-AltStore.debug.xcconfig"; sourceTree = ""; }; FC3822AB1C4CF1D4CDF7445D /* Pods_AltServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltServer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -1737,8 +1739,9 @@ BFF00D2E2501BD4B00746320 /* Intents */ = { isa = PBXGroup; children = ( + D79F3FE32923206E0005D387 /* EnableJITIntentHandler.swift */, BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */, - BFF00D332501BDCF00746320 /* IntentHandler.swift */, + BFF00D332501BDCF00746320 /* RefreshAllIntentHandler.swift */, ); path = Intents; sourceTree = ""; @@ -2655,7 +2658,7 @@ BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */, BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */, BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */, - BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */, + BFF00D342501BDCF00746320 /* RefreshAllIntentHandler.swift in Sources */, BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */, BFF0B6942321CB85007A79E1 /* AuthenticationViewController.swift in Sources */, BF3432FB246B894F0052F4A1 /* BackupAppOperation.swift in Sources */, @@ -2665,6 +2668,7 @@ BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */, BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */, BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */, + D79F3FE42923206E0005D387 /* EnableJITIntentHandler.swift in Sources */, BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */, BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */, D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */, diff --git a/AltStore/AppDelegate.swift b/AltStore/AppDelegate.swift index 85b8c8619..50389b323 100644 --- a/AltStore/AppDelegate.swift +++ b/AltStore/AppDelegate.swift @@ -34,8 +34,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? @available(iOS 14, *) - private var intentHandler: IntentHandler { - get { _intentHandler as! IntentHandler } + private var intentHandler: RefreshAllIntentHandler { + get { _intentHandler as! RefreshAllIntentHandler } + set { _intentHandler = newValue } + } + + @available(iOS 14, *) + private var enableJITIntentHandler: EnableJITIntentHandler { + get { _intentHandler as! EnableJITIntentHandler } set { _intentHandler = newValue } } @@ -47,7 +53,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private lazy var _intentHandler: Any = { guard #available(iOS 14, *) else { fatalError() } - return IntentHandler() + return RefreshAllIntentHandler() + }() + + private lazy var _enableJITIntentHandler: Any = { + guard #available(iOS 14, *) else { fatalError() } + return EnableJITIntentHandler() }() private lazy var _viewAppIntentHandler: Any = { @@ -134,6 +145,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { switch intent { case is RefreshAllIntent: return self.intentHandler + case is EnableJITIntent: return self.enableJITIntentHandler case is ViewAppIntent: return self.viewAppIntentHandler default: return nil } diff --git a/AltStore/Info.plist b/AltStore/Info.plist index 8b17b705c..f384a15a2 100644 --- a/AltStore/Info.plist +++ b/AltStore/Info.plist @@ -92,6 +92,7 @@ AltStore uses the local network to find and communicate with AltServer. NSUserActivityTypes + EnableJITIntent RefreshAllIntent ViewAppIntent diff --git a/AltStore/Intents/EnableJITIntentHandler.swift b/AltStore/Intents/EnableJITIntentHandler.swift new file mode 100644 index 000000000..cecbf3342 --- /dev/null +++ b/AltStore/Intents/EnableJITIntentHandler.swift @@ -0,0 +1,71 @@ +// +// EnableJITIntentHandler.swift +// AltStore +// +// Created by Jhonatan A. on 11/11/22. +// Copyright © 2022 Riley Testut. All rights reserved. +// + +import Foundation + +import AltStoreCore +import Intents + +@available(iOS 14, *) +class EnableJITIntentHandler: NSObject, EnableJITIntentHandling +{ + public func provideAppOptionsCollection(for intent: EnableJITIntent, with completion: @escaping (INObjectCollection?, Error?) -> Void) + { + DatabaseManager.shared.start { (error) in + if let error = error + { + print("Error starting extension:", error) + } + + DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in + let apps = InstalledApp.all(in: context).map { (installedApp) in + return App(identifier: installedApp.bundleIdentifier, display: installedApp.name) + } + + let collection = INObjectCollection(items: apps) + completion(collection, nil) + } + } + } + + func handle(intent: EnableJITIntent, completion: @escaping (EnableJITIntentResponse) -> Void) + { + guard let requestedAppBundleIdentifier = intent.app?.identifier else { + completion(EnableJITIntentResponse(code: .failure, userActivity: nil)) + return + } + + DatabaseManager.shared.start { (error) in + if let _ = error + { + completion(EnableJITIntentResponse(code: .failure, userActivity: nil)) + return + } + + DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in + let predicate = NSPredicate(format: "%K == %@", #keyPath(InstalledApp.bundleIdentifier), requestedAppBundleIdentifier) + guard let installedApp = InstalledApp.first(satisfying: predicate, in: context) else { + completion(EnableJITIntentResponse(code: .failure, userActivity: nil)) + return + } + + AppManager.shared.enableJIT(for: installedApp) { result in + DispatchQueue.main.async { + switch result + { + case .success: + completion(EnableJITIntentResponse(code: .success, userActivity: nil)) + case .failure( _): + completion(EnableJITIntentResponse(code: .failure, userActivity: nil)) + } + } + } + } + } + } +} diff --git a/AltStore/Intents/Intents.intentdefinition b/AltStore/Intents/Intents.intentdefinition index 2271d049d..9ec761060 100644 --- a/AltStore/Intents/Intents.intentdefinition +++ b/AltStore/Intents/Intents.intentdefinition @@ -113,8 +113,184 @@ INIntentVerb Do + + INIntentCategory + generic + INIntentConfigurable + + INIntentDescriptionID + AammMZ + INIntentIneligibleForSuggestions + + INIntentLastParameterTag + 2 + INIntentManagedParameterCombinations + + app + + INIntentParameterCombinationSupportsBackgroundExecution + + INIntentParameterCombinationTitle + Enable JIT + INIntentParameterCombinationTitleID + zIkmRr + INIntentParameterCombinationUpdatesLinked + + + + INIntentName + EnableJIT + INIntentParameters + + + INIntentParameterConfigurable + + INIntentParameterDisplayName + App + INIntentParameterDisplayNameID + k9PKrs + INIntentParameterDisplayPriority + 1 + INIntentParameterName + app + INIntentParameterObjectType + App + INIntentParameterObjectTypeNamespace + KyhEWE + INIntentParameterPromptDialogs + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Configuration + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Primary + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + There are ${count} options matching ‘${app}’. + INIntentParameterPromptDialogFormatStringID + rqSFTo + INIntentParameterPromptDialogType + DisambiguationIntroduction + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + Just to confirm, you wanted ‘${app}’? + INIntentParameterPromptDialogFormatStringID + OuGBx2 + INIntentParameterPromptDialogType + Confirmation + + + INIntentParameterSupportsDynamicEnumeration + + INIntentParameterTag + 2 + INIntentParameterType + Object + + + INIntentResponse + + INIntentResponseCodes + + + INIntentResponseCodeName + success + INIntentResponseCodeSuccess + + + + INIntentResponseCodeName + failure + + + + INIntentTitle + Enable JIT + INIntentTitleID + qJbxKN + INIntentType + Custom + INIntentVerb + Do + INTypes - + + + INTypeDisplayName + App + INTypeDisplayNameID + UqN4iE + INTypeLastPropertyTag + 99 + INTypeName + App + INTypeProperties + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 1 + INTypePropertyName + identifier + INTypePropertyTag + 1 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 2 + INTypePropertyName + displayString + INTypePropertyTag + 2 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 3 + INTypePropertyName + pronunciationHint + INTypePropertyTag + 3 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 4 + INTypePropertyName + alternativeSpeakableMatches + INTypePropertySupportsMultipleValues + + INTypePropertyTag + 4 + INTypePropertyType + SpeakableString + + + + diff --git a/AltStore/Intents/IntentHandler.swift b/AltStore/Intents/RefreshAllIntentHandler.swift similarity index 98% rename from AltStore/Intents/IntentHandler.swift rename to AltStore/Intents/RefreshAllIntentHandler.swift index 4a11e6405..2bead44ae 100644 --- a/AltStore/Intents/IntentHandler.swift +++ b/AltStore/Intents/RefreshAllIntentHandler.swift @@ -11,7 +11,7 @@ import Foundation import AltStoreCore @available(iOS 14, *) -class IntentHandler: NSObject, RefreshAllIntentHandling +class RefreshAllIntentHandler: NSObject, RefreshAllIntentHandling { private let queue = DispatchQueue(label: "io.altstore.IntentHandler") @@ -92,7 +92,7 @@ class IntentHandler: NSObject, RefreshAllIntentHandling } @available(iOS 14, *) -private extension IntentHandler +private extension RefreshAllIntentHandler { func finish(_ intent: RefreshAllIntent, response: RefreshAllIntentResponse) { diff --git a/AltStoreCore/Intents/ViewAppIntentHandler.swift b/AltStoreCore/Intents/ViewAppIntentHandler.swift index 798f2a647..186cda53e 100644 --- a/AltStoreCore/Intents/ViewAppIntentHandler.swift +++ b/AltStoreCore/Intents/ViewAppIntentHandler.swift @@ -12,7 +12,7 @@ import AltStoreCore @available(iOS 14, *) public class ViewAppIntentHandler: NSObject, ViewAppIntentHandling { - public func provideAppOptionsCollection(for intent: ViewAppIntent, with completion: @escaping (INObjectCollection?, Error?) -> Void) + public func provideAppOptionsCollection(for intent: AltStoreCore.ViewAppIntent, with completion: @escaping (INObjectCollection?, Error?) -> Void) { DatabaseManager.shared.start { (error) in if let error = error @@ -22,7 +22,7 @@ public class ViewAppIntentHandler: NSObject, ViewAppIntentHandling DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in let apps = InstalledApp.all(in: context).map { (installedApp) in - return App(identifier: installedApp.bundleIdentifier, display: installedApp.name) + return AltStoreCore.App(identifier: installedApp.bundleIdentifier, display: installedApp.name) } let collection = INObjectCollection(items: apps)