mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Adds "Enable JIT" context menu action for active apps
Allows users to manually enable JIT for apps that don't explicitly support AltKit.
This commit is contained in:
@@ -347,6 +347,8 @@
|
|||||||
BFF7C90F257844C900E55F36 /* AltXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = BFF7C904257844C900E55F36 /* AltXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
BFF7C90F257844C900E55F36 /* AltXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = BFF7C904257844C900E55F36 /* AltXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
BFF7C920257844FA00E55F36 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; };
|
BFF7C920257844FA00E55F36 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; };
|
||||||
BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; };
|
BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; };
|
||||||
|
D57F2C9126E0070200B9FA39 /* EnableJITOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */; };
|
||||||
|
D57F2C9426E01BC700B9FA39 /* UIDevice+Vibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57F2C9326E01BC700B9FA39 /* UIDevice+Vibration.swift */; };
|
||||||
D58D5F2E26DFE68E00E55E38 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = D58D5F2D26DFE68E00E55E38 /* LaunchAtLogin */; };
|
D58D5F2E26DFE68E00E55E38 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = D58D5F2D26DFE68E00E55E38 /* LaunchAtLogin */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
@@ -795,6 +797,8 @@
|
|||||||
BFF7EC4C25081E9300BDE521 /* AltStore 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 8.xcdatamodel"; sourceTree = "<group>"; };
|
BFF7EC4C25081E9300BDE521 /* AltStore 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
BFFCFA45248835530077BFCE /* AltDaemon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltDaemon.entitlements; sourceTree = "<group>"; };
|
BFFCFA45248835530077BFCE /* AltDaemon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltDaemon.entitlements; 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; };
|
||||||
|
D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableJITOperation.swift; sourceTree = "<group>"; };
|
||||||
|
D57F2C9326E01BC700B9FA39 /* UIDevice+Vibration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+Vibration.swift"; sourceTree = "<group>"; };
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
FC3822AB1C4CF1D4CDF7445D /* Pods_AltServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltServer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
FC3822AB1C4CF1D4CDF7445D /* Pods_AltServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltServer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -1556,6 +1560,7 @@
|
|||||||
BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */,
|
BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */,
|
||||||
BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */,
|
BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */,
|
||||||
BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */,
|
BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */,
|
||||||
|
D57F2C9326E01BC700B9FA39 /* UIDevice+Vibration.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1620,6 +1625,7 @@
|
|||||||
BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */,
|
BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */,
|
||||||
BFDBBD7F246CB84F004ED2F3 /* RemoveAppBackupOperation.swift */,
|
BFDBBD7F246CB84F004ED2F3 /* RemoveAppBackupOperation.swift */,
|
||||||
BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */,
|
BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */,
|
||||||
|
D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */,
|
||||||
);
|
);
|
||||||
path = Operations;
|
path = Operations;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2482,10 +2488,12 @@
|
|||||||
BFDB6A0D22AAFC1A007EA6D6 /* OperationError.swift in Sources */,
|
BFDB6A0D22AAFC1A007EA6D6 /* OperationError.swift in Sources */,
|
||||||
BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */,
|
BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */,
|
||||||
BF3D649D22E7AC1B00E9056B /* PermissionPopoverViewController.swift in Sources */,
|
BF3D649D22E7AC1B00E9056B /* PermissionPopoverViewController.swift in Sources */,
|
||||||
|
D57F2C9426E01BC700B9FA39 /* UIDevice+Vibration.swift in Sources */,
|
||||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
||||||
BF56D2AC23DF8E170006506D /* FetchAppIDsOperation.swift in Sources */,
|
BF56D2AC23DF8E170006506D /* FetchAppIDsOperation.swift in Sources */,
|
||||||
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */,
|
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */,
|
||||||
BFE6073A231ADF82002B0E8E /* SettingsViewController.swift in Sources */,
|
BFE6073A231ADF82002B0E8E /* SettingsViewController.swift in Sources */,
|
||||||
|
D57F2C9126E0070200B9FA39 /* EnableJITOperation.swift in Sources */,
|
||||||
BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */,
|
BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */,
|
||||||
BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */,
|
BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */,
|
||||||
BFB6B21E231870160022A802 /* NewsViewController.swift in Sources */,
|
BFB6B21E231870160022A802 /* NewsViewController.swift in Sources */,
|
||||||
|
|||||||
46
AltStore/Extensions/UIDevice+Vibration.swift
Normal file
46
AltStore/Extensions/UIDevice+Vibration.swift
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// UIDevice+Vibration.swift
|
||||||
|
// AltStore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/1/21.
|
||||||
|
// Copyright © 2021 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AudioToolbox
|
||||||
|
import CoreHaptics
|
||||||
|
|
||||||
|
private extension SystemSoundID
|
||||||
|
{
|
||||||
|
static let pop = SystemSoundID(1520)
|
||||||
|
static let cancelled = SystemSoundID(1521)
|
||||||
|
static let tryAgain = SystemSoundID(1102)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13, *)
|
||||||
|
extension UIDevice
|
||||||
|
{
|
||||||
|
enum VibrationPattern
|
||||||
|
{
|
||||||
|
case success
|
||||||
|
case error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13, *)
|
||||||
|
extension UIDevice
|
||||||
|
{
|
||||||
|
var isVibrationSupported: Bool {
|
||||||
|
return CHHapticEngine.capabilitiesForHardware().supportsHaptics
|
||||||
|
}
|
||||||
|
|
||||||
|
func vibrate(pattern: VibrationPattern)
|
||||||
|
{
|
||||||
|
guard self.isVibrationSupported else { return }
|
||||||
|
|
||||||
|
switch pattern
|
||||||
|
{
|
||||||
|
case .success: AudioServicesPlaySystemSound(.tryAgain)
|
||||||
|
case .error: AudioServicesPlaySystemSound(.cancelled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ extension AppManager
|
|||||||
static let didFetchSourceNotification = Notification.Name("com.altstore.AppManager.didFetchSource")
|
static let didFetchSourceNotification = Notification.Name("com.altstore.AppManager.didFetchSource")
|
||||||
|
|
||||||
static let expirationWarningNotificationID = "altstore-expiration-warning"
|
static let expirationWarningNotificationID = "altstore-expiration-warning"
|
||||||
|
static let enableJITResultNotificationID = "altstore-enable-jit"
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 13, *)
|
@available(iOS 13, *)
|
||||||
@@ -540,6 +541,28 @@ extension AppManager
|
|||||||
self.run([removeAppOperation, removeAppBackupOperation], context: authenticationContext)
|
self.run([removeAppOperation, removeAppBackupOperation], context: authenticationContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
func enableJIT(for installedApp: InstalledApp, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
||||||
|
{
|
||||||
|
class Context: OperationContext, EnableJITContext
|
||||||
|
{
|
||||||
|
var installedApp: InstalledApp?
|
||||||
|
}
|
||||||
|
|
||||||
|
let context = Context()
|
||||||
|
context.installedApp = installedApp
|
||||||
|
|
||||||
|
let findServerOperation = self.findServer(context: context) { _ in }
|
||||||
|
|
||||||
|
let enableJITOperation = EnableJITOperation(context: context)
|
||||||
|
enableJITOperation.resultHandler = { (result) in
|
||||||
|
completionHandler(result)
|
||||||
|
}
|
||||||
|
enableJITOperation.addDependency(findServerOperation)
|
||||||
|
|
||||||
|
self.run([enableJITOperation], context: context, requiresSerialQueue: true)
|
||||||
|
}
|
||||||
|
|
||||||
func installationProgress(for app: AppProtocol) -> Progress?
|
func installationProgress(for app: AppProtocol) -> Progress?
|
||||||
{
|
{
|
||||||
let progress = self.installationProgress[app.bundleIdentifier]
|
let progress = self.installationProgress[app.bundleIdentifier]
|
||||||
|
|||||||
@@ -1349,6 +1349,22 @@ private extension MyAppsViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
func enableJIT(for installedApp: InstalledApp)
|
||||||
|
{
|
||||||
|
AppManager.shared.enableJIT(for: installedApp) { result in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .success: break
|
||||||
|
case .failure(let error):
|
||||||
|
let toastView = ToastView(error: error)
|
||||||
|
toastView.show(in: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MyAppsViewController
|
private extension MyAppsViewController
|
||||||
@@ -1555,6 +1571,11 @@ extension MyAppsViewController
|
|||||||
self.remove(installedApp)
|
self.remove(installedApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let jitAction = UIAction(title: NSLocalizedString("Enable JIT", comment: ""), image: UIImage(systemName: "bolt")) { (action) in
|
||||||
|
guard #available(iOS 14, *) else { return }
|
||||||
|
self.enableJIT(for: installedApp)
|
||||||
|
}
|
||||||
|
|
||||||
let backupAction = UIAction(title: NSLocalizedString("Back Up", comment: ""), image: UIImage(systemName: "doc.on.doc")) { (action) in
|
let backupAction = UIAction(title: NSLocalizedString("Back Up", comment: ""), image: UIImage(systemName: "doc.on.doc")) { (action) in
|
||||||
self.backup(installedApp)
|
self.backup(installedApp)
|
||||||
}
|
}
|
||||||
@@ -1601,6 +1622,11 @@ extension MyAppsViewController
|
|||||||
actions.append(activateAction)
|
actions.append(activateAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if installedApp.isActive, #available(iOS 14, *)
|
||||||
|
{
|
||||||
|
actions.append(jitAction)
|
||||||
|
}
|
||||||
|
|
||||||
#if BETA
|
#if BETA
|
||||||
actions.append(changeIconMenu)
|
actions.append(changeIconMenu)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
136
AltStore/Operations/EnableJITOperation.swift
Normal file
136
AltStore/Operations/EnableJITOperation.swift
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
//
|
||||||
|
// EnableJITOperation.swift
|
||||||
|
// EnableJITOperation
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/1/21.
|
||||||
|
// Copyright © 2021 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
import AltStoreCore
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
protocol EnableJITContext
|
||||||
|
{
|
||||||
|
var server: Server? { get }
|
||||||
|
var installedApp: InstalledApp? { get }
|
||||||
|
|
||||||
|
var error: Error? { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
class EnableJITOperation<Context: EnableJITContext>: ResultOperation<Void>
|
||||||
|
{
|
||||||
|
let context: Context
|
||||||
|
|
||||||
|
private var cancellable: AnyCancellable?
|
||||||
|
|
||||||
|
init(context: Context)
|
||||||
|
{
|
||||||
|
self.context = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override func main()
|
||||||
|
{
|
||||||
|
super.main()
|
||||||
|
|
||||||
|
if let error = self.context.error
|
||||||
|
{
|
||||||
|
self.finish(.failure(error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let server = self.context.server, let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||||
|
guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { return self.finish(.failure(OperationError.unknownUDID)) }
|
||||||
|
|
||||||
|
installedApp.managedObjectContext?.perform {
|
||||||
|
guard let bundle = Bundle(url: installedApp.fileURL),
|
||||||
|
let processName = bundle.executableURL?.lastPathComponent
|
||||||
|
else { return self.finish(.failure(OperationError.invalidApp)) }
|
||||||
|
|
||||||
|
let appName = installedApp.name
|
||||||
|
let openAppURL = installedApp.openAppURL
|
||||||
|
|
||||||
|
ServerManager.shared.connect(to: server) { result in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): self.finish(.failure(error))
|
||||||
|
case .success(let connection):
|
||||||
|
print("Sending enable JIT request...")
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
|
||||||
|
// Launch app to make sure it is running in foreground.
|
||||||
|
UIApplication.shared.open(openAppURL) { success in
|
||||||
|
guard success else { return self.finish(.failure(OperationError.openAppFailed(name: appName))) }
|
||||||
|
|
||||||
|
// Combine immediately finishes if an error is thrown, but we want to wait at least until app enters background.
|
||||||
|
// As a workaround, we set error type to Never and use Result<Void, Error> as the value type instead.
|
||||||
|
let result = Future<Result<Void, Error>, Never> { promise in
|
||||||
|
let request = EnableUnsignedCodeExecutionRequest(udid: udid, processName: processName)
|
||||||
|
connection.send(request) { result in
|
||||||
|
print("Sent enable JIT request!")
|
||||||
|
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): promise(.success(.failure(error)))
|
||||||
|
case .success:
|
||||||
|
print("Waiting for enable JIT response...")
|
||||||
|
connection.receiveResponse() { result in
|
||||||
|
print("Received enable JIT response:", result)
|
||||||
|
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): promise(.success(.failure(error)))
|
||||||
|
case .success(.error(let response)): promise(.success(.failure(response.error)))
|
||||||
|
case .success(.enableUnsignedCodeExecution): promise(.success(.success(())))
|
||||||
|
case .success: promise(.success(.failure(ALTServerError(.unknownResponse))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Handle case where app does not enter background (e.g. iPad multitasking).
|
||||||
|
self.cancellable = result
|
||||||
|
.combineLatest(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification, object: nil))
|
||||||
|
.first()
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { (result, _) in
|
||||||
|
let content = UNMutableNotificationContent()
|
||||||
|
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error):
|
||||||
|
content.title = String(format: NSLocalizedString("Could not enable JIT for %@", comment: ""), appName)
|
||||||
|
content.body = error.localizedDescription
|
||||||
|
|
||||||
|
UIDevice.current.vibrate(pattern: .error)
|
||||||
|
|
||||||
|
case .success:
|
||||||
|
content.title = String(format: NSLocalizedString("Enabled JIT for %@", comment: ""), appName)
|
||||||
|
content.body = String(format: NSLocalizedString("JIT will remain enabled until you quit the app.", comment: ""))
|
||||||
|
|
||||||
|
UIDevice.current.vibrate(pattern: .success)
|
||||||
|
}
|
||||||
|
|
||||||
|
if UIApplication.shared.applicationState == .background
|
||||||
|
{
|
||||||
|
// For some reason, notification won't show up reliably unless we provide a trigger (as of iOS 15).
|
||||||
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
|
||||||
|
|
||||||
|
let request = UNNotificationRequest(identifier: AppManager.enableJITResultNotificationID, content: content, trigger: trigger)
|
||||||
|
UNUserNotificationCenter.current().add(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.finish(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user