Converts legacy RefreshAllIntent into App Shortcut (iOS 17+)

This commit is contained in:
Riley Testut
2023-08-18 18:16:05 -05:00
committed by Magesh K
parent 9b885085c9
commit 7747994c80
9 changed files with 291 additions and 4 deletions

View File

@@ -341,6 +341,7 @@
BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; }; BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; };
BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; }; BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; };
D513F6162A12CE4E0061EAA1 /* SourceError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D571ADCD2A02FA7400B24B63 /* SourceError.swift */; }; 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 */; }; D5177B0D2A26944600270065 /* AltStore12ToAltStore13.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = D5177B0C2A26944600270065 /* AltStore12ToAltStore13.xcmappingmodel */; };
D5189C012A01BC6800F44625 /* UserInfoValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5189C002A01BC6800F44625 /* UserInfoValue.swift */; }; D5189C012A01BC6800F44625 /* UserInfoValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5189C002A01BC6800F44625 /* UserInfoValue.swift */; };
D5189C022A01BC6800F44625 /* 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 */; }; D540E93828EE1BDE000F1B0F /* ErrorDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D540E93728EE1BDE000F1B0F /* ErrorDetailsViewController.swift */; };
D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */; }; D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */; };
D552B1D82A042A740066216F /* AppPermissionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D552B1D72A042A740066216F /* AppPermissionsCard.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 */; }; D55E163728776CB700A627A1 /* ComplicationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55E163528776CB000A627A1 /* ComplicationView.swift */; };
D561B2EB28EF5A4F006752E4 /* AltSign-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = D561B2EA28EF5A4F006752E4 /* AltSign-Dynamic */; }; 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, ); }; }; 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>"; }; 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>"; }; 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; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; D571ADCD2A02FA7400B24B63 /* SourceError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceError.swift; sourceTree = "<group>"; };
@@ -1898,8 +1902,8 @@
BFF00D2E2501BD4B00746320 /* Intents */ = { BFF00D2E2501BD4B00746320 /* Intents */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */, D55467B02A8D5E2600F4CE90 /* App Intents */,
BFF00D332501BDCF00746320 /* IntentHandler.swift */, D55FEC9C2A8FEC600057D6E6 /* Legacy */,
); );
path = Intents; path = Intents;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1967,6 +1971,24 @@
path = Previews; path = Previews;
sourceTree = "<group>"; 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 */ = { D586D39928EF58B0000E101F /* AltTests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -2822,8 +2844,10 @@
BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */, BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */,
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */, BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */,
D57FE84428C7DB7100216002 /* ErrorLogViewController.swift in Sources */, D57FE84428C7DB7100216002 /* ErrorLogViewController.swift in Sources */,
D5151BD92A8FF64300C96F28 /* RefreshAllAppsIntent.swift in Sources */,
BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */, BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */,
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */, BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */,
D55467B82A8D5E2600F4CE90 /* AppShortcuts.swift in Sources */,
D593F1942717749A006E82DE /* PatchAppOperation.swift in Sources */, D593F1942717749A006E82DE /* PatchAppOperation.swift in Sources */,
BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */, BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */,
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */, BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,

View File

@@ -144,6 +144,17 @@
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <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> <key>UIRequiredDeviceCapabilities</key>
<array> <array>
<string>armv7</string> <string>armv7</string>

View 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
}
}

View 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)
}
}
}
}

View File

@@ -117,10 +117,10 @@ final class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<Inst
self.startListeningForRunningApps() 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 // a) give us time to discover AltServers
// b) give other processes a chance to respond to requestAppState notification // 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 { self.managedObjectContext.perform {
let filteredApps = self.installedApps.filter { !self.runningApplications.contains($0.bundleIdentifier) } 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 group.completionHandler = { (results) in
self.finish(.success(results)) self.finish(.success(results))
} }
self.progress.addChild(group.progress, withPendingUnitCount: 1)
} }
} }
} }

View File

@@ -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
}
}

View File

@@ -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
}
}