mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-14 17:23:25 +01:00
XCode project for app, moved app project to folder
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// IntentHandler.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 7/6/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SideStoreCore
|
||||
import Intents
|
||||
|
||||
@available(iOS 14, *)
|
||||
public final class IntentHandler: NSObject, RefreshAllIntentHandling {
|
||||
private let queue = DispatchQueue(label: "io.altstore.IntentHandler")
|
||||
|
||||
private var completionHandlers = [RefreshAllIntent: (RefreshAllIntentResponse) -> Void]()
|
||||
private var queuedResponses = [RefreshAllIntent: RefreshAllIntentResponse]()
|
||||
|
||||
private var operations = [RefreshAllIntent: BackgroundRefreshAppsOperation]()
|
||||
|
||||
public func confirm(intent: RefreshAllIntent, completion: @escaping (RefreshAllIntentResponse) -> Void) {
|
||||
// Refreshing apps usually, but not always, completes within alotted time.
|
||||
// As a workaround, we'll start refreshing apps in confirm() so we can
|
||||
// take advantage of some extra time before starting handle() timeout timer.
|
||||
|
||||
completionHandlers[intent] = { response in
|
||||
if response.code != .ready {
|
||||
// Operation finished before confirmation "timeout".
|
||||
// Cache response to return it when handle() is called.
|
||||
self.queuedResponses[intent] = response
|
||||
}
|
||||
|
||||
completion(RefreshAllIntentResponse(code: .ready, userActivity: nil))
|
||||
}
|
||||
|
||||
// Give ourselves 9 extra seconds before starting handle() timeout timer.
|
||||
// 10 seconds or longer results in timeout regardless.
|
||||
queue.asyncAfter(deadline: .now() + 9.0) {
|
||||
self.finish(intent, response: RefreshAllIntentResponse(code: .ready, userActivity: nil))
|
||||
}
|
||||
|
||||
if !DatabaseManager.shared.isStarted {
|
||||
DatabaseManager.shared.start { error in
|
||||
if let error = error {
|
||||
self.finish(intent, response: RefreshAllIntentResponse.failure(localizedDescription: error.localizedDescription))
|
||||
} else {
|
||||
self.refreshApps(intent: intent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
refreshApps(intent: intent)
|
||||
}
|
||||
}
|
||||
|
||||
public func handle(intent: RefreshAllIntent, completion: @escaping (RefreshAllIntentResponse) -> Void) {
|
||||
completionHandlers[intent] = { response in
|
||||
// Ignore .ready response from confirm() timeout.
|
||||
guard response.code != .ready else { return }
|
||||
completion(response)
|
||||
}
|
||||
|
||||
if let response = queuedResponses[intent] {
|
||||
queuedResponses[intent] = nil
|
||||
finish(intent, response: response)
|
||||
} else {
|
||||
queue.asyncAfter(deadline: .now() + 7.0) {
|
||||
if let operation = self.operations[intent] {
|
||||
// We took too long to finish and return the final result,
|
||||
// so we'll now present a normal notification when finished.
|
||||
operation.presentsFinishedNotification = true
|
||||
}
|
||||
|
||||
self.finish(intent, response: RefreshAllIntentResponse(code: .inProgress, userActivity: nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
private extension IntentHandler {
|
||||
func finish(_ intent: RefreshAllIntent, response: RefreshAllIntentResponse) {
|
||||
queue.async {
|
||||
if let completionHandler = self.completionHandlers[intent] {
|
||||
self.completionHandlers[intent] = nil
|
||||
completionHandler(response)
|
||||
} else if response.code != .ready && response.code != .inProgress {
|
||||
// Queue response in case refreshing finishes after confirm() but before handle().
|
||||
self.queuedResponses[intent] = response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func refreshApps(intent: RefreshAllIntent) {
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { context in
|
||||
let installedApps = InstalledApp.fetchActiveApps(in: context)
|
||||
let operation = AppManager.shared.backgroundRefresh(installedApps, presentsNotifications: false) { result in
|
||||
do {
|
||||
let results = try result.get()
|
||||
|
||||
for (_, result) in results {
|
||||
guard case let .failure(error) = result else { continue }
|
||||
throw error
|
||||
}
|
||||
|
||||
self.finish(intent, response: RefreshAllIntentResponse(code: .success, userActivity: nil))
|
||||
} catch RefreshError.noInstalledApps {
|
||||
self.finish(intent, response: RefreshAllIntentResponse(code: .success, userActivity: nil))
|
||||
} catch let error as NSError {
|
||||
print("Failed to refresh apps in background.", error)
|
||||
self.finish(intent, response: RefreshAllIntentResponse.failure(localizedDescription: error.localizedFailureReason ?? error.localizedDescription))
|
||||
}
|
||||
|
||||
self.operations[intent] = nil
|
||||
}
|
||||
|
||||
self.operations[intent] = operation
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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>KyhEWE</string>
|
||||
<key>INIntentDefinitionSystemVersion</key>
|
||||
<string>20A5354i</string>
|
||||
<key>INIntentDefinitionToolsBuildVersion</key>
|
||||
<string>12A8189n</string>
|
||||
<key>INIntentDefinitionToolsVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>INIntents</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentCategory</key>
|
||||
<string>generic</string>
|
||||
<key>INIntentConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentDescriptionID</key>
|
||||
<string>62S1rm</string>
|
||||
<key>INIntentLastParameterTag</key>
|
||||
<integer>3</integer>
|
||||
<key>INIntentManagedParameterCombinations</key>
|
||||
<dict>
|
||||
<key></key>
|
||||
<dict>
|
||||
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
|
||||
<true/>
|
||||
<key>INIntentParameterCombinationTitle</key>
|
||||
<string>Refresh All Apps</string>
|
||||
<key>INIntentParameterCombinationTitleID</key>
|
||||
<string>cJxa2I</string>
|
||||
<key>INIntentParameterCombinationUpdatesLinked</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>INIntentName</key>
|
||||
<string>RefreshAll</string>
|
||||
<key>INIntentParameterCombinations</key>
|
||||
<dict>
|
||||
<key></key>
|
||||
<dict>
|
||||
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
|
||||
<true/>
|
||||
<key>INIntentParameterCombinationTitle</key>
|
||||
<string>Refresh All Apps</string>
|
||||
<key>INIntentParameterCombinationTitleID</key>
|
||||
<string>DKTGdO</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>INIntentResponse</key>
|
||||
<dict>
|
||||
<key>INIntentResponseCodes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentResponseCodeConciseFormatString</key>
|
||||
<string>All apps have been refreshed.</string>
|
||||
<key>INIntentResponseCodeConciseFormatStringID</key>
|
||||
<string>3WMWsJ</string>
|
||||
<key>INIntentResponseCodeFormatString</key>
|
||||
<string>All apps have been refreshed.</string>
|
||||
<key>INIntentResponseCodeFormatStringID</key>
|
||||
<string>BjInD3</string>
|
||||
<key>INIntentResponseCodeName</key>
|
||||
<string>success</string>
|
||||
<key>INIntentResponseCodeSuccess</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentResponseCodeConciseFormatString</key>
|
||||
<string>${localizedDescription}</string>
|
||||
<key>INIntentResponseCodeConciseFormatStringID</key>
|
||||
<string>GJdShK</string>
|
||||
<key>INIntentResponseCodeFormatString</key>
|
||||
<string>${localizedDescription}</string>
|
||||
<key>INIntentResponseCodeFormatStringID</key>
|
||||
<string>oXAiOU</string>
|
||||
<key>INIntentResponseCodeName</key>
|
||||
<string>failure</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INIntentResponseLastParameterTag</key>
|
||||
<integer>3</integer>
|
||||
<key>INIntentResponseParameters</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentResponseParameterDisplayName</key>
|
||||
<string>Localized Description</string>
|
||||
<key>INIntentResponseParameterDisplayNameID</key>
|
||||
<string>wdy22v</string>
|
||||
<key>INIntentResponseParameterDisplayPriority</key>
|
||||
<integer>1</integer>
|
||||
<key>INIntentResponseParameterName</key>
|
||||
<string>localizedDescription</string>
|
||||
<key>INIntentResponseParameterTag</key>
|
||||
<integer>3</integer>
|
||||
<key>INIntentResponseParameterType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>INIntentTitle</key>
|
||||
<string>Refresh All Apps</string>
|
||||
<key>INIntentTitleID</key>
|
||||
<string>2b6Xto</string>
|
||||
<key>INIntentType</key>
|
||||
<string>Custom</string>
|
||||
<key>INIntentVerb</key>
|
||||
<string>Do</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INTypes</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,179 @@
|
||||
<?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>TPucbY</string>
|
||||
<key>INIntentDefinitionSystemVersion</key>
|
||||
<string>20A5354i</string>
|
||||
<key>INIntentDefinitionToolsBuildVersion</key>
|
||||
<string>12A8189n</string>
|
||||
<key>INIntentDefinitionToolsVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>INIntents</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentCategory</key>
|
||||
<string>information</string>
|
||||
<key>INIntentConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentDescription</key>
|
||||
<string>Select App</string>
|
||||
<key>INIntentDescriptionID</key>
|
||||
<string>sb9c7F</string>
|
||||
<key>INIntentEligibleForWidgets</key>
|
||||
<true/>
|
||||
<key>INIntentIneligibleForSuggestions</key>
|
||||
<true/>
|
||||
<key>INIntentLastParameterTag</key>
|
||||
<integer>2</integer>
|
||||
<key>INIntentManagedParameterCombinations</key>
|
||||
<dict>
|
||||
<key>app</key>
|
||||
<dict>
|
||||
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
|
||||
<true/>
|
||||
<key>INIntentParameterCombinationUpdatesLinked</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>INIntentName</key>
|
||||
<string>ViewApp</string>
|
||||
<key>INIntentParameters</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentParameterConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentParameterDisplayName</key>
|
||||
<string>App</string>
|
||||
<key>INIntentParameterDisplayNameID</key>
|
||||
<string>QwLCXY</string>
|
||||
<key>INIntentParameterDisplayPriority</key>
|
||||
<integer>1</integer>
|
||||
<key>INIntentParameterName</key>
|
||||
<string>app</string>
|
||||
<key>INIntentParameterObjectType</key>
|
||||
<string>App</string>
|
||||
<key>INIntentParameterObjectTypeNamespace</key>
|
||||
<string>TPucbY</string>
|
||||
<key>INIntentParameterPromptDialogs</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Configuration</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Primary</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INIntentParameterSupportsDynamicEnumeration</key>
|
||||
<true/>
|
||||
<key>INIntentParameterTag</key>
|
||||
<integer>2</integer>
|
||||
<key>INIntentParameterType</key>
|
||||
<string>Object</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>View App</string>
|
||||
<key>INIntentTitleID</key>
|
||||
<string>7aGoWn</string>
|
||||
<key>INIntentType</key>
|
||||
<string>Custom</string>
|
||||
<key>INIntentVerb</key>
|
||||
<string>View</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INTypeDisplayName</key>
|
||||
<string>App</string>
|
||||
<key>INTypeDisplayNameID</key>
|
||||
<string>cUl1NZ</string>
|
||||
<key>INTypeLastPropertyTag</key>
|
||||
<integer>99</integer>
|
||||
<key>INTypeName</key>
|
||||
<string>App</string>
|
||||
<key>INTypeProperties</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>1</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>identifier</string>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>1</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>2</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>displayString</string>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>2</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>3</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>pronunciationHint</string>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>3</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>4</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>alternativeSpeakableMatches</string>
|
||||
<key>INTypePropertySupportsMultipleValues</key>
|
||||
<true/>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>4</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>SpeakableString</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// ViewAppIntentHandler.swift
|
||||
// ViewAppIntentHandler
|
||||
//
|
||||
// Created by Riley Testut on 7/10/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Intents
|
||||
import Shared
|
||||
import SideStoreCore
|
||||
|
||||
@available(iOS 14, *)
|
||||
public class ViewAppIntentHandler: NSObject, ViewAppIntentHandling {
|
||||
public func provideAppOptionsCollection(for _: ViewAppIntent, with completion: @escaping (INObjectCollection<App>?, 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
|
||||
App(identifier: installedApp.bundleIdentifier, display: installedApp.name)
|
||||
}
|
||||
|
||||
let collection = INObjectCollection(items: apps)
|
||||
completion(collection, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user