Files
keyboard/keyBoard/AppDelegate.m
2025-11-18 13:48:22 +08:00

392 lines
15 KiB
Objective-C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// AppDelegate.m
// keyBoard
//
// Created by 张伟 on 2025/10/27.
//
#import "AppDelegate.h"
#import "KBPermissionViewController.h"
#import <AFNetworking/AFNetworking.h>
#if !DEBUG
#import <Bugly/Bugly.h>
#endif
#import "BaseTabBarController.h"
#import "LoginViewController.h"
#import "KBLoginSheetViewController.h"
#import "AppleSignInManager.h"
#import <objc/message.h>
#import "KBULBridge.h" // Darwin 通知常量用于通知扩展“UL已处理”
#import "LSTPopView.h"
#import "KBLoginPopView.h"
#import "IAPVerifyTransactionObj.h"
#import "FGIAPManager.h"
#import "KBSexSelVC.h"
#import <SystemConfiguration/CaptiveNetwork.h>
#import <CoreTelephony/CTCellularData.h>
// 注意:用于判断系统已启用本输入法扩展的 bundle id 需与扩展 target 的
// PRODUCT_BUNDLE_IDENTIFIER 完全一致。
// 当前工程的 CustomKeyboard target 为 com.loveKey.nyx.CustomKeyboard
static NSString * const kKBKeyboardExtensionBundleId = @"com.loveKey.nyx.CustomKeyboard";
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1. 配置内购服务
[[FGIAPManager shared] setConfigureWith:[IAPVerifyTransactionObj new]];
/// 2配置国际化
[KBLocalizationManager shared].supportedLanguageCodes = @[ @"en", @"zh-Hans"];
// 首次安装先进入性别选择页;点击 Skip 或确认后再进入主 TabBar
BOOL hasShownSexVC = [[NSUserDefaults standardUserDefaults] boolForKey:KBSexSelectShownKey];
if (hasShownSexVC) {
[self setupRootVC];
} else {
[self setupSexSelectRootVC];
}
// 主工程默认开启网络总开关(键盘扩展仍需用户允许完全访问后再行开启)
[KBNetworkManager shared].enabled = YES;
/// 获取网络权限
[self getNetJudge];
/// 触发一次简单网络请求,用于拉起系统的蜂窝数据权限弹窗
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self kb_fireStartupNetworkRequest];
// });
//
#if !DEBUG
/// Bugly
BuglyConfig *buglyConfig = [BuglyConfig new];
/// 设置GroupID进行配置
// buglyConfig.applicationGroupIdentifier = @"";
[Bugly startWithAppId:BuglyId config:buglyConfig];
#endif
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
// [self kb_checkNetworkAndShowAlertIfNeeded];
}
- (void)setupRootVC{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
BaseTabBarController *vc = [[BaseTabBarController alloc] init];
self.window.rootViewController = vc;
}
/// 首次安装时,展示性别选择页作为根控制器
- (void)setupSexSelectRootVC {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
KBSexSelVC *sexVC = [KBSexSelVC new];
__weak typeof(self) weakSelf = self;
sexVC.didFinishSelectBlock = ^{
// 记录已经展示过性别选择页,下次启动直接进 TabBar
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:KBSexSelectShownKey];
[[NSUserDefaults standardUserDefaults] synchronize];
// 切换根控制器到主 TabBar
[weakSelf setupRootVC];
};
self.window.rootViewController = sexVC;
}
#pragma mark - Permission presentation
#pragma mark - Deep Link
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *url = userActivity.webpageURL;
if (!url) return NO;
NSString *host = url.host.lowercaseString ?: @"";
if ([host hasSuffix:@"app.tknb.net"]) {
NSString *path = url.path.lowercaseString ?: @"";
if ([path hasPrefix:@"/ul/settings"]) { [self kb_openAppSettings]; return YES; }
if ([path hasPrefix:@"/ul/login"]) {
// 通知扩展UL 已被主 App 接收,扩展可取消回退到 Scheme
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(),
(__bridge CFStringRef)KBDarwinULHandled,
NULL, NULL, true);
[self kb_presentLoginSheetIfNeeded];
return YES;
}
}
}
return NO;
}
// iOS 9+
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
if (!url) return NO;
// 注意:已对 scheme 做了小写化,比较值也应为小写
if ([[url.scheme lowercaseString] isEqualToString:@"kbkeyboardappextension"]) {
NSString *urlHost = url.host ?: @"";
NSString *host = [urlHost lowercaseString];
if ([host isEqualToString:@"login"]) { // kbkeyboard://login
// 兜底路径Scheme 也发一次“已处理”通知,避免扩展重复尝试
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(),
(__bridge CFStringRef)KBDarwinULHandled,
NULL, NULL, true);
[self kb_presentLoginSheetIfNeeded];
return YES;
} else if ([host isEqualToString:@"settings"]) { // kbkeyboard://settings
[self kb_openAppSettings];
return YES;
}
return NO;
}
return NO;
}
- (void)kb_presentLoginSheetIfNeeded {
// 已登录则不提示
// BOOL loggedIn = ([AppleSignInManager shared].storedUserIdentifier.length > 0);
// if (loggedIn) return;
// UIViewController *top = [UIViewController kb_topMostViewController];
// if (!top) return;
// [KBLoginSheetViewController presentIfNeededFrom:top];
[self goLogin];
}
- (void)goLogin{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
KBLoginPopView *view = [[KBLoginPopView alloc] initWithFrame:CGRectMake(0, 0, KB_SCREEN_WIDTH, KB_SCREEN_WIDTH)];
// 创建并弹出
LSTPopView *pop = [LSTPopView initWithCustomView:view
parentView:nil
popStyle:LSTPopStyleSmoothFromBottom
dismissStyle:LSTDismissStyleSmoothToBottom];
pop.hemStyle = LSTHemStyleBottom;
pop.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
pop.isClickBgDismiss = YES; // 点击背景关闭
pop.cornerRadius = 0; // 自定义 view 自处理圆角
__weak typeof(pop) weakPop = pop;
view.appleLoginHandler = ^{
[weakPop dismiss];
// 交给 VM 统一处理 Apple 登录 + 服务端登录
[[KBLoginVM shared] signInWithAppleFromViewController:KB_CURRENT_NAV completion:^(BOOL success, NSError * _Nullable error) {
if (success) {
[KBHUD showInfo:KBLocalized(@"Signed in successfully")];
} else {
NSString *msg = error.localizedDescription ?: KBLocalized(@"Sign-in failed");
[KBHUD showInfo:msg];
}
}];
};
view.closeHandler = ^{ [weakPop dismiss]; };
[pop pop];
});
}
- (void)kb_openAppSettings {
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
UIApplication *app = [UIApplication sharedApplication];
if ([app canOpenURL:url]) {
if (@available(iOS 10.0, *)) {
[app openURL:url options:@{} completionHandler:nil];
} else {
[app openURL:url];
}
}
}
- (void)kb_presentPermissionIfNeeded
{
// 该逻辑已迁移到 KBGuideVC保留占位以兼容旧调用但不再执行任何操作
}
/// 使用系统网络栈发起一次简单请求(例如拉起蜂窝数据权限弹窗)。
/// 说明:只要真正访问网络,系统在需要时就会弹出“是否允许使用蜂窝数据”等提示。
- (void)kb_fireStartupNetworkRequest {
// [[KBNetworkManager shared] GET:@"https://www.apple.com" parameters:nil headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//
// }];
NSURL *url = [NSURL URLWithString:@"https://www.apple.com"];
if (!url) { return; }
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask *task = [session dataTaskWithURL:url
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
// 回到主线程弹出一个简单提示,证明网络请求已经发起
dispatch_async(dispatch_get_main_queue(), ^{
});
}];
[task resume];
}
-(void)getNetJudge {
AFNetworkReachabilityManager *netManager = [AFNetworkReachabilityManager sharedManager];
[netManager startMonitoring];
[netManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
if (status == AFNetworkReachabilityStatusReachableViaWiFi){
// [PublicObj saveNetReachability:@"wifi"];
}else if (status == AFNetworkReachabilityStatusReachableViaWWAN){
// [PublicObj saveNetReachability:@"wwan"];
}else{
// [PublicObj saveNetReachability:@"unknown"];
}
}];
}
static BOOL KBIsKeyboardEnabled(void) {
for (UITextInputMode *mode in [UITextInputMode activeInputModes]) {
NSString *identifier = nil;
@try {
identifier = [mode valueForKey:@"identifier"]; // not a public API
} @catch (__unused NSException *e) {
identifier = nil;
}
if ([identifier isKindOfClass:[NSString class]] &&
[identifier rangeOfString:kKBKeyboardExtensionBundleId].location != NSNotFound) {
return YES;
}
}
return NO;
}
#pragma mark - Network check & alert
- (void)kb_checkNetworkAndShowAlertIfNeeded {
AFNetworkReachabilityStatus status = [AFNetworkReachabilityManager sharedManager].networkReachabilityStatus;
// 只有在“明确不可达”时才弹
if (status == AFNetworkReachabilityStatusNotReachable ||
status == AFNetworkReachabilityStatusUnknown) {
[self kb_showNoNetworkAlert];
}
}
- (void)kb_showNoNetworkAlert {
UIViewController *rootVC = self.window.rootViewController;
if (!rootVC) return;
// 防止重复弹(例如已经有一个 alert 在上面了)
if ([rootVC.presentedViewController isKindOfClass:[UIAlertController class]]) {
return;
}
UIAlertController *alert = [UIAlertController alertControllerWithTitle:KBLocalized(@"Network unavailable")
message:KBLocalized(@"Please check this app's wireless-data permission or network connection in Settings.")
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *settings = [UIAlertAction actionWithTitle:KBLocalized(@"Open Settings")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[self kb_openAppSettings]; // 你已经实现好的跳设置方法
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:KBLocalized(@"Cancel")
style:UIAlertActionStyleCancel
handler:nil];
[alert addAction:settings];
[alert addAction:cancel];
[rootVC presentViewController:alert animated:YES completion:nil];
}
///*
// 获取网络权限状态
// */
//- (void)networkStatus:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// //2.根据权限执行相应的交互
// CTCellularData *cellularData = [[CTCellularData alloc] init];
// /*
// 此函数会在网络权限改变时再次调用
// */
// cellularData.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState state) {
// switch (state) {
// case kCTCellularDataRestricted:
//
// NSLog(@"Restricted");
// //2.1权限关闭的情况下 再次请求网络数据会弹出设置网络提示
//
// break;
// case kCTCellularDataNotRestricted:
//
// NSLog(@"NotRestricted");
// //2.2已经开启网络权限 监听网络状态
// [self addReachabilityManager:application didFinishLaunchingWithOptions:launchOptions];
// break;
// case kCTCellularDataRestrictedStateUnknown:
//
// NSLog(@"Unknown");
//
// break;
//
// default:
// break;
// }
// };
//}
//
///**
// 实时检查当前网络状态
// */
//- (void)addReachabilityManager:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// AFNetworkReachabilityManager *afNetworkReachabilityManager = [AFNetworkReachabilityManager sharedManager];
//
//
// [afNetworkReachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
// switch (status) {
// case AFNetworkReachabilityStatusNotReachable:{
// NSLog(@"网络不通:%@",@(status) );
// break;
// }
// case AFNetworkReachabilityStatusReachableViaWiFi:{
// NSLog(@"网络通过WIFI连接%@",@(status));
//
// [self getInfo_application:application didFinishLaunchingWithOptions:launchOptions];
//
// break;
// }
// case AFNetworkReachabilityStatusReachableViaWWAN:{
// NSLog(@"网络通过无线连接:%@",@(status) );
//
// [self getInfo_application:application didFinishLaunchingWithOptions:launchOptions];
//
// break;
// }
// default:
// break;
// }
// }];
//
// [afNetworkReachabilityManager startMonitoring]; //开启网络监视器;
//}
//
////把以前写在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions里面的一些初始化操作放在该方法
//- (void)getInfo_application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//
//// [self.updateBusiness checkUpdateWithBothApi];//检查app更新
//
////发送通知给APP首屏页面让其有网络时重新请求
// [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil];
//}
//
//
//
@end