mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-16 04:20:30 +00:00
Implement App Names
This commit is contained in:
parent
d4fb2512b3
commit
8f3180f69a
3 changed files with 149 additions and 71 deletions
|
|
@ -126,7 +126,7 @@ struct HomeView: View {
|
|||
}
|
||||
|
||||
class InstalledAppsViewModel: ObservableObject {
|
||||
@Published var apps: [String] = []
|
||||
@Published var apps: [String: String] = [:]
|
||||
|
||||
init() {
|
||||
loadApps()
|
||||
|
|
@ -134,78 +134,40 @@ class InstalledAppsViewModel: ObservableObject {
|
|||
|
||||
func loadApps() {
|
||||
guard let rawPointer = list_installed_apps() else {
|
||||
self.apps = []
|
||||
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 }
|
||||
|
||||
guard let jsonData = output.data(using: .utf8) else {
|
||||
print("Error: Failed to convert string to data")
|
||||
self.apps = [:]
|
||||
return
|
||||
}
|
||||
|
||||
if output.hasPrefix("{\"error\":") {
|
||||
self.apps = [:]
|
||||
}
|
||||
|
||||
print(output)
|
||||
|
||||
// Decode the JSON into a Swift dictionary
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
let apps = try decoder.decode([String: String].self, from: jsonData)
|
||||
self.apps = apps
|
||||
return
|
||||
} catch {
|
||||
print("Error: Failed to decode JSON - \(error)")
|
||||
self.apps = [:]
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct InstalledAppsListView: View {
|
||||
@AppStorage("username") private var username = "User"
|
||||
@AppStorage("customBackgroundColor") private var customBackgroundColorHex: String = Color.primaryBackground.toHex() ?? "#000000"
|
||||
@State private var selectedBackgroundColor: Color = Color(hex: UserDefaults.standard.string(forKey: "customBackgroundColor") ?? "#000000") ?? Color.primaryBackground
|
||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
@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 {
|
||||
selectedBackgroundColor .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 Apps")
|
||||
}
|
||||
.onReceive(timer) { _ in
|
||||
refreshBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
private func refreshBackground() {
|
||||
selectedBackgroundColor = Color(hex: customBackgroundColorHex) ?? Color.primaryBackground
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#Preview {
|
||||
|
|
|
|||
70
StikJIT/Views/InstalledAppsListView.swift
Normal file
70
StikJIT/Views/InstalledAppsListView.swift
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// InstalledAppsListView.swift
|
||||
// StikJIT
|
||||
//
|
||||
// Created by Stossy11 on 28/03/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct InstalledAppsListView: View {
|
||||
@AppStorage("username") private var username = "User"
|
||||
@AppStorage("customBackgroundColor") private var customBackgroundColorHex: String = Color.primaryBackground.toHex() ?? "#000000"
|
||||
@State private var selectedBackgroundColor: Color = Color(hex: UserDefaults.standard.string(forKey: "customBackgroundColor") ?? "#000000") ?? Color.primaryBackground
|
||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
@StateObject var viewModel = InstalledAppsViewModel()
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State private var searchText: String = ""
|
||||
|
||||
var onSelect: (String) -> Void
|
||||
|
||||
var filteredApps: [String] {
|
||||
if searchText.isEmpty {
|
||||
return Array(viewModel.apps.keys) // Use the keys (app names)
|
||||
} else {
|
||||
return viewModel.apps.keys.filter { $0.localizedCaseInsensitiveContains(searchText) }
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ZStack {
|
||||
selectedBackgroundColor.edgesIgnoringSafeArea(.all)
|
||||
|
||||
List {
|
||||
ForEach(filteredApps, id: \.self) { app in
|
||||
Button(action: {
|
||||
onSelect(viewModel.apps[app] ?? "") // Select using the app's bundle ID
|
||||
}) {
|
||||
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 Apps")
|
||||
}
|
||||
.onReceive(timer) { _ in
|
||||
refreshBackground()
|
||||
}
|
||||
.onAppear {
|
||||
print(filteredApps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func refreshBackground() {
|
||||
selectedBackgroundColor = Color(hex: customBackgroundColorHex) ?? Color.primaryBackground
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ char *list_installed_apps() {
|
|||
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");
|
||||
return strdup("{\"error\": \"Invalid IP address\"}");
|
||||
}
|
||||
|
||||
char pairingFilePath[1024];
|
||||
|
|
@ -37,21 +37,21 @@ char *list_installed_apps() {
|
|||
IdevicePairingFile *pairing_file = NULL;
|
||||
IdeviceErrorCode err = idevice_pairing_file_read(pairingFilePath, &pairing_file);
|
||||
if (err != IdeviceSuccess) {
|
||||
return strdup("Error: Failed to read pairing file");
|
||||
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");
|
||||
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");
|
||||
return strdup("{\"error\": \"Failed to connect to installation proxy\"}");
|
||||
}
|
||||
|
||||
void *apps = NULL;
|
||||
|
|
@ -60,13 +60,15 @@ char *list_installed_apps() {
|
|||
if (err != IdeviceSuccess) {
|
||||
installation_proxy_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
return strdup("Error: Failed to get apps");
|
||||
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';
|
||||
strcat(result, "{\n");
|
||||
|
||||
int first_entry = 1;
|
||||
for (size_t i = 0; i < apps_len; i++) {
|
||||
plist_t app = app_list[i];
|
||||
// Check if the app has an "Entitlements" dictionary.
|
||||
|
|
@ -83,15 +85,59 @@ char *list_installed_apps() {
|
|||
if (bundle_id_node) {
|
||||
char *bundle_id = NULL;
|
||||
plist_get_string_val(bundle_id_node, &bundle_id);
|
||||
strcat(result, bundle_id);
|
||||
strcat(result, "\n");
|
||||
|
||||
// Skip if bundle ID is empty
|
||||
if (bundle_id == NULL || strlen(bundle_id) == 0) {
|
||||
free(bundle_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the app name
|
||||
plist_t app_name_node = plist_dict_get_item(app, "CFBundleName");
|
||||
char *app_name = NULL;
|
||||
if (app_name_node) {
|
||||
plist_get_string_val(app_name_node, &app_name);
|
||||
} else {
|
||||
app_name = strdup("Unknown");
|
||||
}
|
||||
|
||||
// Escape special characters in app name and bundle ID
|
||||
char escaped_app_name[1024] = {0};
|
||||
char escaped_bundle_id[1024] = {0};
|
||||
for (int j = 0, k = 0; app_name[j] != '\0'; j++, k++) {
|
||||
if (app_name[j] == '"' || app_name[j] == '\\') {
|
||||
escaped_app_name[k++] = '\\';
|
||||
}
|
||||
escaped_app_name[k] = app_name[j];
|
||||
}
|
||||
for (int j = 0, k = 0; bundle_id[j] != '\0'; j++, k++) {
|
||||
if (bundle_id[j] == '"' || bundle_id[j] == '\\') {
|
||||
escaped_bundle_id[k++] = '\\';
|
||||
}
|
||||
escaped_bundle_id[k] = bundle_id[j];
|
||||
}
|
||||
|
||||
// Add the app name and bundle ID to the result in JSON format
|
||||
if (!first_entry) {
|
||||
strcat(result, ",\n");
|
||||
}
|
||||
strcat(result, " \"");
|
||||
strcat(result, escaped_app_name);
|
||||
strcat(result, "\": \"");
|
||||
strcat(result, escaped_bundle_id);
|
||||
strcat(result, "\"");
|
||||
|
||||
first_entry = 0;
|
||||
free(bundle_id);
|
||||
free(app_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strcat(result, "\n}\n");
|
||||
|
||||
installation_proxy_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue