Add bad_query support for iOS 26–27.0b4
Integrate forcequitOS/bad_query for sandbox-escaped PosterBoard writes, on-device container UUID detection (no PC/Nugget required), descriptor wipe for Reset Collections, and Mond-style WebKit respring that works under LiveContainer. Rebrand fork to com.mak5er.Pocket-Poster; credit Mak5er.
This commit is contained in:
1 parent
fb07aa4558
commit
18a67ec171
21 files changed
+1223
-102
No files matched your search
@@ -58,10 +58,15 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
if pbHash == "" {
|
||||
if pbHash == "" && !SymHandler.prefersBadQuery {
|
||||
Text("Enter your PosterBoard app hash in Settings.")
|
||||
} else {
|
||||
VStack {
|
||||
if pbHash == "" && SymHandler.prefersBadQuery {
|
||||
Text("No hash set — will auto-detect via bad_query on Apply.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
if !pbManager.selectedTendies.isEmpty || !pbManager.videos.isEmpty {
|
||||
Button(action: {
|
||||
UIImpactFeedbackGenerator(style: .soft).impactOccurred()
|
||||
@@ -69,21 +74,23 @@ struct ContentView: View {
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
do {
|
||||
try pbManager.applyTendies(appHash: pbHash)
|
||||
var hash = pbHash
|
||||
if hash.isEmpty {
|
||||
UIApplication.shared.change(title: NSLocalizedString("Applying Wallpapers...", comment: ""), body: "Detecting PosterBoard container…")
|
||||
hash = try BadQuery.findPosterBoardHash()
|
||||
DispatchQueue.main.async { pbHash = hash }
|
||||
}
|
||||
try pbManager.applyTendies(appHash: hash)
|
||||
SymHandler.cleanup() // just to be extra sure
|
||||
try? FileManager.default.removeItem(at: pbManager.getTendiesStoreURL())
|
||||
UIApplication.shared.dismissAlert(animated: false)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
pbManager.selectedTendies.removeAll()
|
||||
pbManager.videos.removeAll()
|
||||
Haptic.shared.notify(.success)
|
||||
UIApplication.shared.confirmAlert(title: NSLocalizedString("Success!", comment: ""), body: NSLocalizedString("The PosterBoard app will now open. Please close it from the app switcher.", comment: ""), onOK: {
|
||||
if !pbManager.openPosterBoard() {
|
||||
UIApplication.shared.confirmAlert(title: NSLocalizedString("Falling Back to Shortcut", comment: ""), body: NSLocalizedString("PosterBoard failed to open directly. The fallback shortcut will now be opened.", comment: ""), onOK: {
|
||||
pbManager.runShortcut(named: "PosterBoard")
|
||||
}, noCancel: true)
|
||||
}
|
||||
}, noCancel: true)
|
||||
})
|
||||
// Instant Mond-style respring (no alert delay)
|
||||
RespringHelper.respring()
|
||||
}
|
||||
} catch CocoaError.fileWriteUnknown {
|
||||
presentError(ApplyError.wrongAppHash)
|
||||
} catch CocoaError.fileWriteFileExists {
|
||||
@@ -99,21 +106,41 @@ struct ContentView: View {
|
||||
.buttonStyle(TintedButton(color: .blue, fullwidth: true))
|
||||
}
|
||||
Button(action: {
|
||||
if #available(iOS 18.0, *) {
|
||||
guard let lang = UserDefaults.standard.stringArray(forKey: "AppleLanguages")?.first else {
|
||||
hideResetHelp = false // fallback to tutorial
|
||||
return
|
||||
}
|
||||
UIApplication.shared.confirmAlert(title: NSLocalizedString("Reset Collections", comment: ""), body: NSLocalizedString("Do you want to reset collections?", comment: ""), onOK: {
|
||||
if pbManager.setSystemLanguage(to: lang) {
|
||||
UIApplication.shared.alert(title: NSLocalizedString("Collections Successfully Reset!", comment: ""), body: NSLocalizedString("Your PosterBoard will refresh automatically.", comment: ""))
|
||||
} else {
|
||||
UIApplication.shared.alert(body: "The API failed to call correctly.\nSystem Locale Code: \(lang)")
|
||||
UIApplication.shared.confirmAlert(
|
||||
title: NSLocalizedString("Reset Collections", comment: ""),
|
||||
body: SymHandler.prefersBadQuery
|
||||
? "This will wipe custom PosterBoard descriptors via bad_query, then respring."
|
||||
: NSLocalizedString("Do you want to reset collections?", comment: ""),
|
||||
onOK: {
|
||||
UIApplication.shared.alert(
|
||||
title: "Resetting…",
|
||||
body: "Please wait",
|
||||
animated: true,
|
||||
withButton: false
|
||||
)
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
do {
|
||||
var hash = pbHash
|
||||
if hash.isEmpty && SymHandler.prefersBadQuery {
|
||||
hash = try BadQuery.findPosterBoardHash()
|
||||
DispatchQueue.main.async { pbHash = hash }
|
||||
}
|
||||
guard !hash.isEmpty else {
|
||||
throw ApplyError.wrongAppHash
|
||||
}
|
||||
try pbManager.resetCollections(appHash: hash)
|
||||
DispatchQueue.main.async {
|
||||
Haptic.shared.notify(.success)
|
||||
// Instant Mond-style respring
|
||||
RespringHelper.respring()
|
||||
}
|
||||
} catch {
|
||||
presentError(ApplyError.unexpected(info: error.localizedDescription))
|
||||
}
|
||||
}
|
||||
}, noCancel: false)
|
||||
} else {
|
||||
hideResetHelp = false
|
||||
}
|
||||
},
|
||||
noCancel: false
|
||||
)
|
||||
}) {
|
||||
Label("Reset Collections", systemImage: "arrow.clockwise.circle")
|
||||
}
|
||||
@@ -171,11 +198,11 @@ struct ContentView: View {
|
||||
|
||||
func presentError(_ error: ApplyError) {
|
||||
SymHandler.cleanup()
|
||||
UIApplication.shared.dismissAlert(animated: false)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
|
||||
DispatchQueue.main.async {
|
||||
Haptic.shared.notify(.error)
|
||||
UIApplication.shared.alert(body: error.localizedDescription)
|
||||
})
|
||||
// alert() dismisses any buttonless progress sheet first, always with OK
|
||||
UIApplication.shared.alert(body: error.localizedDescription, withButton: true)
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// Created by lemin on 5/31/25.
|
||||
//
|
||||
|
||||
import SwiftUICore
|
||||
import SwiftUI
|
||||
|
||||
struct OnBoardingPage: Identifiable {
|
||||
var id = UUID()
|
||||
|
||||
@@ -15,11 +15,12 @@ struct SettingsView: View {
|
||||
|
||||
@State var checkingForHash: Bool = false
|
||||
@State var hashCheckTask: Task<Void, any Error>? = nil
|
||||
@State var detectingOnDevice: Bool = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
VStack {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
TextField("Enter PosterBoard App Hash", text: $pbHash)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.font(.system(.body, design: .monospaced))
|
||||
@@ -28,8 +29,30 @@ struct SettingsView: View {
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.font(.system(.body, design: .monospaced))
|
||||
}
|
||||
|
||||
if SymHandler.prefersBadQuery {
|
||||
Text("bad_query available — on-device detect works (no PC).")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
// On-device detect via bad_query (iOS 26/27)
|
||||
if SymHandler.prefersBadQuery {
|
||||
Button(action: {
|
||||
detectOnDevice()
|
||||
}) {
|
||||
if detectingOnDevice {
|
||||
ProgressView()
|
||||
} else {
|
||||
Text("Detect On-Device")
|
||||
}
|
||||
}
|
||||
.foregroundStyle(.blue)
|
||||
.disabled(detectingOnDevice)
|
||||
}
|
||||
|
||||
// Run task to check until file exists from Nugget pc over AFC
|
||||
Button(action: {
|
||||
if !FileManager.default.fileExists(atPath: SymHandler.getPosterBoardHashURL().path()) {
|
||||
@@ -40,7 +63,7 @@ struct SettingsView: View {
|
||||
}
|
||||
startWaitForHash()
|
||||
}) {
|
||||
Text("Detect")
|
||||
Text(SymHandler.prefersBadQuery ? "Detect via Nugget" : "Detect")
|
||||
}
|
||||
.foregroundStyle(.green)
|
||||
.onChange(of: checkingForHash) { _ in
|
||||
@@ -138,10 +161,12 @@ struct SettingsView: View {
|
||||
|
||||
// MARK: Credits
|
||||
Section {
|
||||
LinkCell(imageName: "Mak5er", url: "https://github.com/Mak5er", title: "Mak5er", contribution: "bad_query port · iOS 27 build", circle: true)
|
||||
LinkCell(imageName: "leminlimez", url: "https://github.com/leminlimez", title: "LeminLimez", contribution: NSLocalizedString("Main Developer", comment: "leminlimez's contribution"), circle: true)
|
||||
LinkCell(imageName: "serstars", url: "https://github.com/SerStars", title: "SerStars", contribution: NSLocalizedString("Website Designer", comment: ""), circle: true)
|
||||
LinkCell(imageName: "Nathan", url: "https://github.com/verygenericname", title: "Nathan", contribution: NSLocalizedString("Exploit", comment: ""), circle: true)
|
||||
LinkCell(imageName: "duy", url: "https://github.com/khanhduytran0", title: "DuyKhanhTran", contribution: NSLocalizedString("Exploit", comment: ""), circle: true)
|
||||
LinkCell(imageName: "Nathan", url: "https://github.com/verygenericname", title: "Nathan", contribution: NSLocalizedString("Exploit (.Trash)", comment: ""), circle: true)
|
||||
LinkCell(imageName: "duy", url: "https://github.com/khanhduytran0", title: "DuyKhanhTran", contribution: NSLocalizedString("Exploit (.Trash)", comment: ""), circle: true)
|
||||
LinkCell(imageName: "sky", url: "https://github.com/forcequitOS/bad_query", title: "forcequitOS", contribution: "bad_query (iOS 26/27)", circle: false)
|
||||
LinkCell(imageName: "sky", url: "https://bsky.app/profile/did:plc:xykfeb7ieeo335g3aly6vev4", title: "dootskyre", contribution: NSLocalizedString("Fallback Shortcut Creator", comment: ""), circle: true)
|
||||
LinkCell(imageName: "POEditor", url: "https://poeditor.com/join/project/MPZOsunwVj", title: NSLocalizedString("Community Translators", comment: ""), contribution: "POEditor")
|
||||
} header: {
|
||||
@@ -150,6 +175,59 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan containers on-device with bad_query / fsgetpath — no computer needed.
|
||||
func detectOnDevice() {
|
||||
detectingOnDevice = true
|
||||
UIApplication.shared.alert(
|
||||
title: "Scanning containers…",
|
||||
body: "Looking for PosterBoard via bad_query. This may take a moment.",
|
||||
animated: true,
|
||||
withButton: false
|
||||
)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
var pb: String?
|
||||
var cp: String?
|
||||
var errMsg: String?
|
||||
|
||||
do {
|
||||
pb = try BadQuery.findPosterBoardHash()
|
||||
} catch {
|
||||
errMsg = error.localizedDescription
|
||||
}
|
||||
|
||||
if CarPlayManager.supportsCarPlay() {
|
||||
cp = try? BadQuery.findCarPlayHash()
|
||||
}
|
||||
|
||||
// Always show a closeable result alert (loading alert has no OK button —
|
||||
// dismiss must complete before the next alert is presented).
|
||||
DispatchQueue.main.async {
|
||||
detectingOnDevice = false
|
||||
|
||||
if let pb {
|
||||
pbHash = pb.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if let cp {
|
||||
cpHash = cp.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
Haptic.shared.notify(.success)
|
||||
let body = cp != nil
|
||||
? "PosterBoard:\n\(pbHash)\n\nCarPlay:\n\(cpHash)"
|
||||
: "PosterBoard:\n\(pbHash)"
|
||||
// alert() auto-dismisses any previous (buttonless) sheet first
|
||||
UIApplication.shared.alert(title: "Hash found!", body: body, withButton: true)
|
||||
} else {
|
||||
Haptic.shared.notify(.error)
|
||||
UIApplication.shared.alert(
|
||||
title: "Detect failed",
|
||||
body: errMsg ?? "Could not find PosterBoard container. Open Wallpaper settings once, then retry.",
|
||||
withButton: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startWaitForHash() {
|
||||
checkingForHash = true
|
||||
hashCheckTask = Task {
|
||||
|
||||
Reference in new issue
Block a user