Files
keyboard/CustomKeyboard/Utils/KBExtensionAppLauncher.m

162 lines
5.1 KiB
Mathematica
Raw Normal View History

2025-11-21 18:26:02 +08:00
//
// KBExtensionAppLauncher.m
// CustomKeyboard
//
#import "KBExtensionAppLauncher.h"
#if KB_URL_BRIDGE_ENABLE
2025-11-21 18:26:02 +08:00
#import <objc/message.h>
#endif
2025-11-21 18:26:02 +08:00
@implementation KBExtensionAppLauncher
#if KB_URL_BRIDGE_ENABLE
+ (BOOL)kb_openURLViaResponderChain:(NSURL *)url
source:(nullable UIResponder *)source
completion:(void (^ _Nullable)(BOOL success))completion {
if (!url) {
if (completion) { completion(NO); }
return NO;
}
UIResponder *responder = source;
// openURL:options:completionHandler:
// /宿 App
// options options nil
SEL openURLOptionsSel = NSSelectorFromString(@"openURL:options:completionHandler:");
while (responder) {
if ([responder respondsToSelector:openURLOptionsSel]) {
void (*msgSend)(id, SEL, NSURL *, id, void (^)(BOOL)) = (void *)objc_msgSend;
msgSend(responder, openURLOptionsSel, url, nil, ^(BOOL ok) {
if (completion) { completion(ok); }
});
return YES;
}
responder = responder.nextResponder;
}
// openURL:completionHandler:
responder = source;
SEL openURLCompletionSel = NSSelectorFromString(@"openURL:completionHandler:");
while (responder) {
if ([responder respondsToSelector:openURLCompletionSel]) {
void (*msgSend)(id, SEL, NSURL *, void (^)(BOOL)) = (void *)objc_msgSend;
msgSend(responder, openURLCompletionSel, url, ^(BOOL ok) {
if (completion) { completion(ok); }
});
return YES;
}
responder = responder.nextResponder;
}
// openURL:
responder = source;
SEL openURLSel = NSSelectorFromString(@"openURL:");
while (responder) {
if ([responder respondsToSelector:openURLSel]) {
BOOL (*msgSend)(id, SEL, NSURL *) = (void *)objc_msgSend;
BOOL ok = msgSend(responder, openURLSel, url);
if (completion) { completion(ok); }
return YES;
}
responder = responder.nextResponder;
}
if (completion) { completion(NO); }
return NO;
}
#endif
2025-11-21 18:26:02 +08:00
+ (void)openPrimaryURL:(NSURL * _Nullable)primaryURL
fallbackURL:(NSURL * _Nullable)fallbackURL
usingInputController:(UIInputViewController *)ivc
source:(UIResponder * _Nullable)source
2025-11-21 18:26:02 +08:00
completion:(void (^ _Nullable)(BOOL success))completion {
if (![NSThread isMainThread]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self openPrimaryURL:primaryURL
fallbackURL:fallbackURL
usingInputController:ivc
source:source
completion:completion];
});
return;
}
2025-11-21 18:26:02 +08:00
if (!ivc || (!primaryURL && !fallbackURL)) {
if (completion) { completion(NO); }
return;
}
// 线 dispatch
void (^finish)(BOOL) = ^(BOOL ok){
if (!completion) return;
if ([NSThread isMainThread]) {
completion(ok);
} else {
dispatch_async(dispatch_get_main_queue(), ^{ completion(ok); });
}
};
NSURL *first = primaryURL ?: fallbackURL;
NSURL *second = (first == primaryURL) ? fallbackURL : nil;
if (!first) {
finish(NO);
return;
}
[ivc.extensionContext openURL:first completionHandler:^(BOOL ok) {
if (ok) {
finish(YES);
return;
}
if (second) {
[ivc.extensionContext openURL:second completionHandler:^(BOOL ok2) {
if (ok2) {
finish(YES);
return;
}
#if KB_URL_BRIDGE_ENABLE
//
UIResponder *start = (source ?: (UIResponder *)ivc.view ?: (UIResponder *)ivc);
[self kb_openURLViaResponderChain:second
source:start
completion:^(BOOL ok3) {
finish(ok3);
}];
#else
finish(NO);
#endif
2025-11-21 18:26:02 +08:00
}];
} else {
#if KB_URL_BRIDGE_ENABLE
UIResponder *start = (source ?: (UIResponder *)ivc.view ?: (UIResponder *)ivc);
[self kb_openURLViaResponderChain:first
source:start
completion:^(BOOL ok3) {
finish(ok3);
}];
#else
finish(NO);
#endif
2025-11-21 18:26:02 +08:00
}
}];
}
+ (void)openScheme:(NSURL *)scheme
usingInputController:(UIInputViewController *)ivc
source:(UIResponder * _Nullable)source
2025-11-21 18:26:02 +08:00
completion:(void (^ _Nullable)(BOOL success))completion {
[self openPrimaryURL:scheme
fallbackURL:nil
usingInputController:ivc
source:source
completion:completion];
}
@end