fetch icon from springboard services

This commit is contained in:
Huge_Black 2025-03-30 18:31:02 +08:00
parent 56054b75fb
commit abf7b73f4d
10 changed files with 93 additions and 52 deletions

View file

@ -353,7 +353,7 @@ func startHeartbeatInBackground() {
}
}
JITEnableContext.shared().startHeartbeat(completionHandler: completionHandler, logger: nil)
JITEnableContext.shared.startHeartbeat(completionHandler: completionHandler, logger: nil)
}
heartBeat.qualityOfService = .background

View file

@ -7,6 +7,7 @@
import UIKit
// Not AppStore
class AppStoreIconFetcher {
static private var iconCache: [String: UIImage] = [:]
@ -17,55 +18,15 @@ class AppStoreIconFetcher {
return
}
// Hit the App Store API
let baseURLString = "https://itunes.apple.com/lookup?bundleId="
let urlString = baseURLString + bundleID
guard let url = URL(string: urlString) else {
DispatchQueue.main.async { completion(nil) }
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
DispatchQueue.main.async { completion(nil) }
return
}
Task {
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let results = json["results"] as? [[String: Any]],
!results.isEmpty,
let firstApp = results.first,
let iconURLString = firstApp["artworkUrl512"] as? String ?? firstApp["artworkUrl100"] as? String,
let iconURL = URL(string: iconURLString) {
// Download the icon image
downloadImage(from: iconURL) { image in
if let image = image {
// Cache the icon
iconCache[bundleID] = image
}
DispatchQueue.main.async {
completion(image)
}
}
} else {
DispatchQueue.main.async { completion(nil) }
}
let ans = try JITEnableContext.shared.getAppIcon(withBundleId: bundleID)
iconCache[bundleID] = ans
completion(ans)
} catch {
DispatchQueue.main.async { completion(nil) }
}
}.resume()
}
private static func downloadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data, let image = UIImage(data: data) {
completion(image)
} else {
print("Failed to get icon: \(error)")
completion(nil)
}
}.resume()
}
}
}
}

View file

@ -261,7 +261,7 @@ struct HomeView: View {
isProcessing = true
DispatchQueue.global(qos: .background).async {
JITEnableContext.shared().debugApp(withBundleID: bundleID, logger: nil)
JITEnableContext.shared.debugApp(withBundleID: bundleID, logger: nil)
DispatchQueue.main.async {
isProcessing = false
@ -279,7 +279,7 @@ class InstalledAppsViewModel: ObservableObject {
func loadApps() {
do {
self.apps = try JITEnableContext.shared().getAppList()
self.apps = try JITEnableContext.shared.getAppList()
} catch {
print(error)
self.apps = [:]

View file

@ -5,6 +5,7 @@
// Created by s s on 2025/3/28.
//
@import Foundation;
@import UIKit;
#include "idevice.h"
typedef void (^HeartbeatCompletionHandler)(int result, NSString *message);
@ -12,9 +13,10 @@ typedef void (^LogFuncC)(const char* message, ...);
typedef void (^LogFunc)(NSString *message);
@interface JITEnableContext : NSObject
+ (instancetype)shared;
@property (class, readonly)JITEnableContext* shared;
- (IdevicePairingFile*)getPairingFileWithError:(NSError**)error;
- (void)startHeartbeatWithCompletionHandler:(HeartbeatCompletionHandler)completionHandler logger:(LogFunc)logger;
- (void)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger;
- (NSDictionary<NSString*, NSString*>*)getAppListWithError:(NSError**)error;
- (UIImage*)getAppIconWithBundleId:(NSString*)bundleId error:(NSError**)error;
@end

View file

@ -115,6 +115,23 @@ JITEnableContext* sharedJITContext = nil;
}
}
- (UIImage*)getAppIconWithBundleId:(NSString*)bundleId error:(NSError**)error {
if(!provider) {
NSLog(@"Provider not initialized!");
*error = [self errorWithStr:@"Provider not initialized!" code:-1];
return nil;
}
NSString* errorStr = nil;
UIImage* ans = getAppIcon(provider, bundleId, &errorStr);
if(errorStr){
*error = [self errorWithStr:errorStr code:-17];
return nil;
} else {
return ans;
}
}
- (void)dealloc {
self->heartbeatSessionId = arc4random();
if(provider) {

View file

@ -8,7 +8,9 @@
#ifndef APPLIST_H
#define APPLIST_H
@import Foundation;
@import UIKit;
NSDictionary<NSString*, NSString*>* list_installed_apps(TcpProviderHandle* provider, NSString** error);
UIImage* getAppIcon(TcpProviderHandle* provider, NSString* bundleID, NSString** error);
#endif /* APPLIST_H */

View file

@ -84,3 +84,30 @@ NSDictionary<NSString*, NSString*>* list_installed_apps(TcpProviderHandle* provi
return ans;
}
UIImage* getAppIcon(TcpProviderHandle* provider, NSString* bundleID, NSString** error) {
IdeviceErrorCode err = IdeviceSuccess;
SpringBoardServicesClientHandle *client = NULL;
springboard_services_proxy_connect_tcp(provider, &client);
if (err != IdeviceSuccess) {
*error = @"Failed to connect to SpringBoard Services";
return nil;
}
void *pngData = NULL;
size_t data_len = 0;
err = springboard_services_proxy_get_icon(client, [bundleID UTF8String], &pngData, &data_len);
if (err != IdeviceSuccess) {
springboard_services_proxy_free(client);
*error = @"Failed to get app icon";
return nil;
}
NSData* pngNSData = [NSData dataWithBytes:pngData length:data_len];
free(pngData);
UIImage* ans = [UIImage imageWithData:pngNSData];
return ans;
}

View file

@ -116,6 +116,8 @@ typedef struct ProcessControlAdapterHandle ProcessControlAdapterHandle;
*/
typedef struct RemoteServerAdapterHandle RemoteServerAdapterHandle;
typedef struct SpringBoardServicesClientHandle SpringBoardServicesClientHandle;
typedef struct TcpProviderHandle TcpProviderHandle;
typedef struct UsbmuxdAddrHandle UsbmuxdAddrHandle;
@ -2009,4 +2011,35 @@ enum IdeviceErrorCode idevice_usbmuxd_unix_addr_new(const char *addr,
*/
void idevice_usbmuxd_addr_free(struct UsbmuxdAddrHandle *usbmuxd_addr);
enum IdeviceErrorCode springboard_services_proxy_connect_tcp(struct TcpProviderHandle *provider,
struct SpringBoardServicesClientHandle **client);
enum IdeviceErrorCode springboard_services_proxy_connect_usbmuxd(struct UsbmuxdProviderHandle *provider,
struct SpringBoardServicesClientHandle **client);
enum IdeviceErrorCode springboard_services_proxy_new(struct IdeviceHandle *socket,
struct SpringBoardServicesClientHandle **client);
/**
* Gets the icon of the specified app by bundle identifier
*
* # Arguments
* * `client` - A valid SpringBoardServicesClient handle
* * `bundle_identifier` - The identifiers of the app to get icon
* * `out_result` - On success, will be set to point to a newly allocated png data
*
* # Returns
* An error code indicating success or failure
*
* # Safety
* `client` must be a valid pointer to a handle allocated by this library
* `out_result` must be a valid, non-null pointer to a location where the result will be stored
*/
enum IdeviceErrorCode springboard_services_proxy_get_icon(struct SpringBoardServicesClientHandle *client,
const char *bundle_identifier,
void **out_result,
size_t *out_result_len);
void springboard_services_proxy_free(struct SpringBoardServicesClientHandle *handle);
#endif

View file

@ -7,7 +7,6 @@
// Jackson Coxson
#include "idevice.h"
#include <arpa/inet.h>
#include <inttypes.h>
#include <stdio.h>

Binary file not shown.