mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-08 22:33:26 +01:00
Converts legacy RefreshAllIntent into App Shortcut (iOS 17+)
This commit is contained in:
@@ -341,6 +341,7 @@
|
||||
BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; };
|
||||
BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; };
|
||||
D513F6162A12CE4E0061EAA1 /* SourceError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D571ADCD2A02FA7400B24B63 /* SourceError.swift */; };
|
||||
D5151BD92A8FF64300C96F28 /* RefreshAllAppsIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5151BD82A8FF64300C96F28 /* RefreshAllAppsIntent.swift */; };
|
||||
D5177B0D2A26944600270065 /* AltStore12ToAltStore13.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = D5177B0C2A26944600270065 /* AltStore12ToAltStore13.xcmappingmodel */; };
|
||||
D5189C012A01BC6800F44625 /* UserInfoValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5189C002A01BC6800F44625 /* UserInfoValue.swift */; };
|
||||
D5189C022A01BC6800F44625 /* UserInfoValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5189C002A01BC6800F44625 /* UserInfoValue.swift */; };
|
||||
@@ -358,6 +359,7 @@
|
||||
D540E93828EE1BDE000F1B0F /* ErrorDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D540E93728EE1BDE000F1B0F /* ErrorDetailsViewController.swift */; };
|
||||
D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */; };
|
||||
D552B1D82A042A740066216F /* AppPermissionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D552B1D72A042A740066216F /* AppPermissionsCard.swift */; };
|
||||
D55467B82A8D5E2600F4CE90 /* AppShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */; };
|
||||
D55E163728776CB700A627A1 /* ComplicationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55E163528776CB000A627A1 /* ComplicationView.swift */; };
|
||||
D561B2EB28EF5A4F006752E4 /* AltSign-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = D561B2EA28EF5A4F006752E4 /* AltSign-Dynamic */; };
|
||||
D561B2ED28EF5A4F006752E4 /* AltSign-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = D561B2EA28EF5A4F006752E4 /* AltSign-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
@@ -909,6 +911,7 @@
|
||||
D52C08ED28AEC37A006C4AE5 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = "<group>"; };
|
||||
D52E988928D002D30032BE6B /* AltStore 11.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 11.xcdatamodel"; sourceTree = "<group>"; };
|
||||
C9EEAA842DA87A88A870053B /* Pods_AltStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D5151BD82A8FF64300C96F28 /* RefreshAllAppsIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAllAppsIntent.swift; sourceTree = "<group>"; };
|
||||
D5177B0C2A26944600270065 /* AltStore12ToAltStore13.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore12ToAltStore13.xcmappingmodel; sourceTree = "<group>"; };
|
||||
D5189C002A01BC6800F44625 /* UserInfoValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoValue.swift; sourceTree = "<group>"; };
|
||||
D51AD27C29356B7B00967AAA /* ALTWrappedError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTWrappedError.h; sourceTree = "<group>"; };
|
||||
@@ -926,6 +929,7 @@
|
||||
D540E93728EE1BDE000F1B0F /* ErrorDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorDetailsViewController.swift; sourceTree = "<group>"; };
|
||||
D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D552B1D72A042A740066216F /* AppPermissionsCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermissionsCard.swift; sourceTree = "<group>"; };
|
||||
D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppShortcuts.swift; sourceTree = "<group>"; };
|
||||
D55E163528776CB000A627A1 /* ComplicationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationView.swift; sourceTree = "<group>"; };
|
||||
D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OperatingSystemVersion+Comparable.swift"; sourceTree = "<group>"; };
|
||||
D571ADCD2A02FA7400B24B63 /* SourceError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceError.swift; sourceTree = "<group>"; };
|
||||
@@ -1898,8 +1902,8 @@
|
||||
BFF00D2E2501BD4B00746320 /* Intents */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */,
|
||||
BFF00D332501BDCF00746320 /* IntentHandler.swift */,
|
||||
D55467B02A8D5E2600F4CE90 /* App Intents */,
|
||||
D55FEC9C2A8FEC600057D6E6 /* Legacy */,
|
||||
);
|
||||
path = Intents;
|
||||
sourceTree = "<group>";
|
||||
@@ -1967,6 +1971,24 @@
|
||||
path = Previews;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D55467B02A8D5E2600F4CE90 /* App Intents */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */,
|
||||
D5151BD82A8FF64300C96F28 /* RefreshAllAppsIntent.swift */,
|
||||
);
|
||||
path = "App Intents";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D55FEC9C2A8FEC600057D6E6 /* Legacy */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */,
|
||||
BFF00D332501BDCF00746320 /* IntentHandler.swift */,
|
||||
);
|
||||
path = Legacy;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D586D39928EF58B0000E101F /* AltTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2822,8 +2844,10 @@
|
||||
BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */,
|
||||
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */,
|
||||
D57FE84428C7DB7100216002 /* ErrorLogViewController.swift in Sources */,
|
||||
D5151BD92A8FF64300C96F28 /* RefreshAllAppsIntent.swift in Sources */,
|
||||
BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */,
|
||||
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */,
|
||||
D55467B82A8D5E2600F4CE90 /* AppShortcuts.swift in Sources */,
|
||||
D593F1942717749A006E82DE /* PatchAppOperation.swift in Sources */,
|
||||
BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */,
|
||||
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,
|
||||
|
||||
@@ -144,6 +144,17 @@
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict>
|
||||
<key>CFBundlePrimaryIcon</key>
|
||||
<dict>
|
||||
<key>NSAppIconComplementingColorNames</key>
|
||||
<array>
|
||||
<string>GradientTop</string>
|
||||
<string>GradientBottom</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
|
||||
29
AltStore/Intents/App Intents/AppShortcuts.swift
Normal file
29
AltStore/Intents/App Intents/AppShortcuts.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// AppShortcuts.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 8/23/22.
|
||||
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import AppIntents
|
||||
|
||||
@available(iOS 17, *)
|
||||
public struct ShortcutsProvider: AppShortcutsProvider
|
||||
{
|
||||
public static var appShortcuts: [AppShortcut] {
|
||||
AppShortcut(intent: RefreshAllAppsIntent(),
|
||||
phrases: [
|
||||
"Refresh \(.applicationName)",
|
||||
"Refresh \(.applicationName) apps",
|
||||
"Refresh my \(.applicationName) apps",
|
||||
"Refresh apps with \(.applicationName)",
|
||||
],
|
||||
shortTitle: "Refresh All Apps",
|
||||
systemImageName: "arrow.triangle.2.circlepath")
|
||||
}
|
||||
|
||||
public static var shortcutTileColor: ShortcutTileColor {
|
||||
return .teal
|
||||
}
|
||||
}
|
||||
181
AltStore/Intents/App Intents/RefreshAllAppsIntent.swift
Normal file
181
AltStore/Intents/App Intents/RefreshAllAppsIntent.swift
Normal file
@@ -0,0 +1,181 @@
|
||||
//
|
||||
// RefreshAllAppsIntent.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 8/18/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import AppIntents
|
||||
|
||||
import AltStoreCore
|
||||
|
||||
// Shouldn't conform types we don't own to protocols we don't own, so make custom
|
||||
// NSError subclass that conforms to CustomLocalizedStringResourceConvertible instead.
|
||||
//
|
||||
// Would prefer to just conform ALTLocalizedError to CustomLocalizedStringResourceConvertible,
|
||||
// but that can't be done without raising minimum version for ALTLocalizedError to iOS 16 :/
|
||||
@available(iOS 16, *)
|
||||
class IntentError: NSError, CustomLocalizedStringResourceConvertible
|
||||
{
|
||||
var localizedStringResource: LocalizedStringResource {
|
||||
return "\(self.localizedDescription)"
|
||||
}
|
||||
|
||||
init(_ error: some Error)
|
||||
{
|
||||
let serializedError = (error as NSError).sanitizedForSerialization()
|
||||
super.init(domain: serializedError.domain, code: serializedError.code, userInfo: serializedError.userInfo)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder)
|
||||
{
|
||||
super.init(coder: coder)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
extension RefreshAllAppsIntent
|
||||
{
|
||||
private actor OperationActor
|
||||
{
|
||||
private(set) var operation: BackgroundRefreshAppsOperation?
|
||||
|
||||
func set(_ operation: BackgroundRefreshAppsOperation?)
|
||||
{
|
||||
self.operation = operation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
struct RefreshAllAppsIntent: AppIntent, CustomIntentMigratedAppIntent, PredictableIntent, ProgressReportingIntent, ForegroundContinuableIntent
|
||||
{
|
||||
static let intentClassName = "RefreshAllIntent"
|
||||
|
||||
static var title: LocalizedStringResource = "Refresh All Apps"
|
||||
static var description = IntentDescription("Refreshes your sideloaded apps to prevent them from expiring.")
|
||||
|
||||
static var parameterSummary: some ParameterSummary {
|
||||
Summary("Refresh All Apps")
|
||||
}
|
||||
|
||||
static var predictionConfiguration: some IntentPredictionConfiguration {
|
||||
IntentPrediction {
|
||||
DisplayRepresentation(
|
||||
title: "Refresh All Apps",
|
||||
subtitle: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private let operationActor = OperationActor()
|
||||
|
||||
init()
|
||||
{
|
||||
self.progress.completedUnitCount = 0
|
||||
self.progress.totalUnitCount = 1
|
||||
}
|
||||
|
||||
func perform() async throws -> some IntentResult & ProvidesDialog
|
||||
{
|
||||
do
|
||||
{
|
||||
// Request foreground execution at ~27 seconds to gracefully handle timeout.
|
||||
let deadline: ContinuousClock.Instant = .now + .seconds(27)
|
||||
|
||||
try await withThrowingTaskGroup(of: Void.self) { taskGroup in
|
||||
taskGroup.addTask {
|
||||
try await self.refreshAllApps()
|
||||
}
|
||||
|
||||
taskGroup.addTask {
|
||||
try await Task.sleep(until: deadline)
|
||||
throw OperationError.timedOut
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
for try await _ in taskGroup.prefix(1)
|
||||
{
|
||||
// We only care about the first child task to complete.
|
||||
break
|
||||
}
|
||||
}
|
||||
catch OperationError.timedOut
|
||||
{
|
||||
// We took too long to finish and return the final result,
|
||||
// so we'll now present a normal notification when finished.
|
||||
let operation = await self.operationActor.operation
|
||||
operation?.presentsFinishedNotification = true
|
||||
|
||||
try await self.requestToContinueInForeground()
|
||||
}
|
||||
}
|
||||
|
||||
return .result(dialog: "All apps have been refreshed.")
|
||||
}
|
||||
catch
|
||||
{
|
||||
let intentError = IntentError(error)
|
||||
throw intentError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
private extension RefreshAllAppsIntent
|
||||
{
|
||||
func refreshAllApps() async throws
|
||||
{
|
||||
if !DatabaseManager.shared.isStarted
|
||||
{
|
||||
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
|
||||
DatabaseManager.shared.start { error in
|
||||
if let error
|
||||
{
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
else
|
||||
{
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||
let installedApps = await context.perform { InstalledApp.fetchAppsForRefreshingAll(in: context) }
|
||||
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
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
|
||||
}
|
||||
|
||||
continuation.resume()
|
||||
}
|
||||
catch ~RefreshErrorCode.noInstalledApps
|
||||
{
|
||||
continuation.resume()
|
||||
}
|
||||
catch
|
||||
{
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
|
||||
self.progress.addChild(operation.progress, withPendingUnitCount: 1)
|
||||
|
||||
Task {
|
||||
await self.operationActor.set(operation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,10 +117,10 @@ final class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<Inst
|
||||
|
||||
self.startListeningForRunningApps()
|
||||
|
||||
// Wait for 3 seconds (2 now, 1 later in FindServerOperation) to:
|
||||
// Wait for 2 seconds (1 now, 1 later in FindServerOperation) to:
|
||||
// a) give us time to discover AltServers
|
||||
// b) give other processes a chance to respond to requestAppState notification
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
self.managedObjectContext.perform {
|
||||
|
||||
let filteredApps = self.installedApps.filter { !self.runningApplications.contains($0.bundleIdentifier) }
|
||||
@@ -152,6 +152,8 @@ final class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<Inst
|
||||
group.completionHandler = { (results) in
|
||||
self.finish(.success(results))
|
||||
}
|
||||
|
||||
self.progress.addChild(group.progress, withPendingUnitCount: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "132",
|
||||
"green" : "128",
|
||||
"red" : "1"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "176",
|
||||
"green" : "200",
|
||||
"red" : "123"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user