mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-15 20:10:30 +00:00
App List
This commit is contained in:
parent
11863ff763
commit
3fd7d86455
4 changed files with 221 additions and 5 deletions
|
|
@ -5,3 +5,4 @@
|
|||
#include "idevice.h"
|
||||
#include "heartbeat.h"
|
||||
#include "jit.h"
|
||||
#include "applist.h"
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@
|
|||
import SwiftUI
|
||||
|
||||
struct HomeView: View {
|
||||
@AppStorage("bundleID") private var bundleID: String = "com.stossy11.MeloNX"
|
||||
@AppStorage("bundleID") private var bundleID: String = ""
|
||||
@State private var isProcessing = false
|
||||
|
||||
@State private var isShowingInstalledApps = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color(.systemGroupedBackground)
|
||||
|
|
@ -29,13 +30,16 @@ struct HomeView: View {
|
|||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding(.top, 40)
|
||||
|
||||
RoundedTextField(placeholder: "Enter Bundle ID", text: $bundleID)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
Button(action: {
|
||||
HapticFeedbackHelper.trigger()
|
||||
startJITInBackground()
|
||||
startJITInBackground(with: bundleID)
|
||||
}) {
|
||||
Label(isProcessing ? "Enabling..." : "Enable JIT", systemImage: isProcessing ? "hourglass" : "bolt.fill")
|
||||
Label(isProcessing ? "Enabling..." : "Enable JIT",
|
||||
systemImage: isProcessing ? "hourglass" : "bolt.fill")
|
||||
.font(.system(.title3, design: .rounded))
|
||||
.fontWeight(.semibold)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
|
@ -49,13 +53,36 @@ struct HomeView: View {
|
|||
.disabled(isProcessing)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
Button(action: {
|
||||
isShowingInstalledApps = true
|
||||
}) {
|
||||
Label("View Installed Apps", systemImage: "list.bullet")
|
||||
.font(.system(.title3, design: .rounded))
|
||||
.fontWeight(.semibold)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.green)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(16)
|
||||
.shadow(color: Color.green.opacity(0.3), radius: 8, x: 0, y: 4)
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.sheet(isPresented: $isShowingInstalledApps) {
|
||||
InstalledAppsListView { selectedBundle in
|
||||
bundleID = selectedBundle
|
||||
isShowingInstalledApps = false
|
||||
HapticFeedbackHelper.trigger()
|
||||
startJITInBackground(with: selectedBundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func startJITInBackground() {
|
||||
private func startJITInBackground(with bundleID: String) {
|
||||
isProcessing = true
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
guard let cBundleID = strdup(bundleID) else {
|
||||
|
|
@ -78,6 +105,79 @@ struct HomeView: View {
|
|||
}
|
||||
}
|
||||
|
||||
class InstalledAppsViewModel: ObservableObject {
|
||||
@Published var apps: [String] = []
|
||||
|
||||
init() {
|
||||
loadApps()
|
||||
}
|
||||
|
||||
func loadApps() {
|
||||
guard let rawPointer = list_installed_apps() else {
|
||||
self.apps = []
|
||||
return
|
||||
}
|
||||
|
||||
let output = String(cString: rawPointer)
|
||||
free(rawPointer)
|
||||
|
||||
if output.hasPrefix("Error:") {
|
||||
self.apps = []
|
||||
} else {
|
||||
self.apps = output.components(separatedBy: "\n").filter { !$0.isEmpty }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InstalledAppsListView: View {
|
||||
@StateObject var viewModel = InstalledAppsViewModel()
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State private var searchText: String = ""
|
||||
|
||||
var onSelect: (String) -> Void
|
||||
|
||||
var filteredApps: [String] {
|
||||
if searchText.isEmpty {
|
||||
return viewModel.apps
|
||||
} else {
|
||||
return viewModel.apps.filter { $0.localizedCaseInsensitiveContains(searchText) }
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ZStack {
|
||||
Color(.systemGroupedBackground)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
List {
|
||||
ForEach(filteredApps, id: \.self) { app in
|
||||
Button(action: {
|
||||
onSelect(app)
|
||||
}) {
|
||||
Text(app)
|
||||
.font(.system(.body, design: .rounded))
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.listRowBackground(Color(.secondarySystemGroupedBackground))
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
.listStyle(InsetGroupedListStyle())
|
||||
.navigationTitle("Installed Apps")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Done") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.searchable(text: $searchText, prompt: "Search Bundle IDs")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
HomeView()
|
||||
}
|
||||
|
|
|
|||
102
StikJIT/idevice/applist.c
Normal file
102
StikJIT/idevice/applist.c
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// applist.c
|
||||
// StikJIT
|
||||
//
|
||||
// Created by Stephen on 3/27/25.
|
||||
//
|
||||
|
||||
#include "idevice.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <limits.h>
|
||||
|
||||
char *list_installed_apps() {
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LOCKDOWN_PORT);
|
||||
if (inet_pton(AF_INET, "10.7.0.1", &addr.sin_addr) <= 0) {
|
||||
return strdup("Error: Invalid IP address");
|
||||
}
|
||||
|
||||
CFURLRef pairingFileURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
|
||||
CFSTR("pairing_file"),
|
||||
CFSTR("plist"),
|
||||
NULL);
|
||||
if (pairingFileURL == NULL) {
|
||||
return strdup("Error: Pairing file not found");
|
||||
}
|
||||
|
||||
char pairingFilePath[PATH_MAX];
|
||||
if (!CFURLGetFileSystemRepresentation(pairingFileURL, TRUE, (UInt8 *)pairingFilePath, PATH_MAX)) {
|
||||
CFRelease(pairingFileURL);
|
||||
return strdup("Error: Pairing file path conversion failed");
|
||||
}
|
||||
CFRelease(pairingFileURL);
|
||||
|
||||
IdevicePairingFile *pairing_file = NULL;
|
||||
IdeviceErrorCode err = idevice_pairing_file_read(pairingFilePath, &pairing_file);
|
||||
if (err != IdeviceSuccess) {
|
||||
return strdup("Error: Failed to read pairing file");
|
||||
}
|
||||
|
||||
TcpProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file, "ExampleProvider", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
idevice_pairing_file_free(pairing_file);
|
||||
return strdup("Error: Failed to create TCP provider");
|
||||
}
|
||||
|
||||
InstallationProxyClientHandle *client = NULL;
|
||||
err = installation_proxy_connect_tcp(provider, &client);
|
||||
if (err != IdeviceSuccess) {
|
||||
tcp_provider_free(provider);
|
||||
return strdup("Error: Failed to connect to installation proxy");
|
||||
}
|
||||
|
||||
void *apps = NULL;
|
||||
size_t apps_len = 0;
|
||||
err = installation_proxy_get_apps(client, NULL, NULL, 0, &apps, &apps_len);
|
||||
if (err != IdeviceSuccess) {
|
||||
installation_proxy_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
return strdup("Error: Failed to get apps");
|
||||
}
|
||||
|
||||
plist_t *app_list = (plist_t *)apps;
|
||||
char *result = malloc(8192); // Allocate memory for output
|
||||
result[0] = '\0';
|
||||
|
||||
for (size_t i = 0; i < apps_len; i++) {
|
||||
plist_t app = app_list[i];
|
||||
// Check if the app has an "Entitlements" dictionary.
|
||||
plist_t entitlements = plist_dict_get_item(app, "Entitlements");
|
||||
if (entitlements) {
|
||||
// Look for the "get-task-allow" key.
|
||||
plist_t taskAllowNode = plist_dict_get_item(entitlements, "get-task-allow");
|
||||
if (taskAllowNode) {
|
||||
int isAllowed = 0;
|
||||
plist_get_bool_val(taskAllowNode, &isAllowed);
|
||||
if (isAllowed) {
|
||||
// Retrieve the bundle identifier if the entitlement is true.
|
||||
plist_t bundle_id_node = plist_dict_get_item(app, "CFBundleIdentifier");
|
||||
if (bundle_id_node) {
|
||||
char *bundle_id = NULL;
|
||||
plist_get_string_val(bundle_id_node, &bundle_id);
|
||||
strcat(result, bundle_id);
|
||||
strcat(result, "\n");
|
||||
free(bundle_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
installation_proxy_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
|
||||
return result;
|
||||
}
|
||||
13
StikJIT/idevice/applist.h
Normal file
13
StikJIT/idevice/applist.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// applist.h
|
||||
// StikJIT
|
||||
//
|
||||
// Created by Stephen on 3/27/25.
|
||||
//
|
||||
|
||||
#ifndef APPLIST_H
|
||||
#define APPLIST_H
|
||||
|
||||
char *list_installed_apps(void);
|
||||
|
||||
#endif /* APPLIST_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue