diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index 1d2cf40..3f9f560 100755 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -6,7 +6,6 @@ development com.apple.developer.associated-domains - applinks:alf57p.openinstall.com applinks:alf57p.oplinking.com com.apple.developer.networking.networkextension diff --git a/ios/Runner/RunnerRelease.entitlements b/ios/Runner/RunnerRelease.entitlements index 1d2cf40..3f9f560 100755 --- a/ios/Runner/RunnerRelease.entitlements +++ b/ios/Runner/RunnerRelease.entitlements @@ -6,7 +6,6 @@ development com.apple.developer.associated-domains - applinks:alf57p.openinstall.com applinks:alf57p.oplinking.com com.apple.developer.networking.networkextension diff --git a/lib/app/model/response/kr_node_list.dart b/lib/app/model/response/kr_node_list.dart index 6b25e72..c7589d8 100755 --- a/lib/app/model/response/kr_node_list.dart +++ b/lib/app/model/response/kr_node_list.dart @@ -150,9 +150,9 @@ class KrNodeListItem { final protocolsList = jsonDecode(protocols) as List; final currentProtocol = json['protocol']?.toString().toLowerCase() ?? ''; - KRLogUtil.kr_i('📋 protocols 字段内容 (${protocolsList.length} 个协议):', tag: 'NodeList'); + // KRLogUtil.kr_i('📋 protocols 字段内容 (${protocolsList.length} 个协议):', tag: 'NodeList'); for (var i = 0; i < protocolsList.length; i++) { - KRLogUtil.kr_i(' 协议 $i: ${jsonEncode(protocolsList[i])}', tag: 'NodeList'); + // KRLogUtil.kr_i(' 协议 $i: ${jsonEncode(protocolsList[i])}', tag: 'NodeList'); } if (protocolsList.isNotEmpty) { @@ -170,7 +170,7 @@ class KrNodeListItem { (currentProtocol == 'shadowsocks' && protocolType == 'ss') || (currentProtocol == 'ss' && protocolType == 'shadowsocks')) { matchedProtocolConfig = configMap; - KRLogUtil.kr_i('🎯 找到匹配的协议配置: $protocolType', tag: 'NodeList'); + // KRLogUtil.kr_i('🎯 找到匹配的协议配置: $protocolType', tag: 'NodeList'); break; } } @@ -182,22 +182,22 @@ class KrNodeListItem { // 这样可以保留顶层的正确端口(如 53441),不被 protocols 数组中的端口(如 287)覆盖 if (port == 0 && targetProtocol['port'] != null) { port = _parseIntSafely(targetProtocol['port']); - KRLogUtil.kr_i('✅ 从 protocols 解析端口: $port', tag: 'NodeList'); + // KRLogUtil.kr_i('✅ 从 protocols 解析端口: $port', tag: 'NodeList'); } else { - KRLogUtil.kr_i('✅ 保留顶层端口: $port (protocols中的端口: ${targetProtocol['port']})', tag: 'NodeList'); + // KRLogUtil.kr_i('✅ 保留顶层端口: $port (protocols中的端口: ${targetProtocol['port']})', tag: 'NodeList'); } // 提取 cipher(加密方法) if (targetProtocol['cipher'] != null && targetProtocol['cipher'].toString().isNotEmpty) { method = targetProtocol['cipher'].toString(); - KRLogUtil.kr_i('✅ 从 protocols 解析 cipher: $method', tag: 'NodeList'); + // KRLogUtil.kr_i('✅ 从 protocols 解析 cipher: $method', tag: 'NodeList'); } } } catch (e) { KRLogUtil.kr_w('⚠️ 解析 protocols 字段失败: $e', tag: 'NodeList'); } } - KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'NodeList'); + // KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'NodeList'); return KrNodeListItem( id: _parseIntSafely(json['id']), diff --git a/lib/app/modules/hi_node_list/views/hi_page_node_view.dart b/lib/app/modules/hi_node_list/views/hi_page_node_view.dart index bc6384e..ebcfd14 100644 --- a/lib/app/modules/hi_node_list/views/hi_page_node_view.dart +++ b/lib/app/modules/hi_node_list/views/hi_page_node_view.dart @@ -132,12 +132,12 @@ class _HINodePageViewState extends State { TextSpan( children: [ TextSpan( - text: '${AppTranslations.kr_setting.connectionTypeRule}:', + text: '智能模式:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.sp, color: Colors.black), ), TextSpan( - text: AppTranslations.kr_setting.connectionTypeRuleRemark, - style: TextStyle(fontSize: 14.sp, color: Colors.black54), + text: '绕过所在地IP,只代理外网服务器,速度更快', + style: TextStyle(fontSize: 14.sp,), ), ], ), @@ -147,12 +147,12 @@ class _HINodePageViewState extends State { TextSpan( children: [ TextSpan( - text: '${AppTranslations.kr_setting.connectionTypeGlobal}:', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.sp, color: Colors.black), + text: '全局模式:', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.sp), ), TextSpan( - text: AppTranslations.kr_setting.connectionTypeGlobalRemark, - style: TextStyle(fontSize: 14.sp, color: Colors.black54), + text: '代理所有网络服务器,保障隐私', + style: TextStyle(fontSize: 14.sp), ), ], ), diff --git a/lib/app/modules/hi_user_info/controllers/hi_user_info_controller.dart b/lib/app/modules/hi_user_info/controllers/hi_user_info_controller.dart index e8aeb44..518a80a 100755 --- a/lib/app/modules/hi_user_info/controllers/hi_user_info_controller.dart +++ b/lib/app/modules/hi_user_info/controllers/hi_user_info_controller.dart @@ -16,6 +16,7 @@ import 'dart:convert'; import 'package:kaer_with_panels/utils/snackbar_util.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart'; import 'package:kaer_with_panels/app/common/app_config.dart'; +import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; class HIUserInfoController extends GetxController { /// 订阅服务 @@ -212,7 +213,8 @@ class HIUserInfoController extends GetxController { KRLogUtil.kr_e('订阅信息刷新失败: $e', tag: 'DeviceManagement'); } - KRSnackBarUtil.show(AppTranslations.kr_dialog.success, '退出登录成功'); + // KRSnackBarUtil.show(AppTranslations.kr_dialog.success, '退出登录成功'); + KRCommonUtil.kr_showToast('退出登录成功'); Get.offAllNamed(Routes.KR_HOME); }, ); diff --git a/lib/app/modules/hi_user_info/views/hi_user_info_view.dart b/lib/app/modules/hi_user_info/views/hi_user_info_view.dart index 1705bd7..045a703 100755 --- a/lib/app/modules/hi_user_info/views/hi_user_info_view.dart +++ b/lib/app/modules/hi_user_info/views/hi_user_info_view.dart @@ -273,6 +273,7 @@ class HIUserInfoView extends GetView { confirmText: '返回', // 3. onCancel 对应“确认注销”按钮的点击事件 onCancel: () { + Get.back(); // 执行页面跳转到注销账户页面 Get.toNamed(Routes.KR_DELETE_ACCOUNT); }, @@ -547,7 +548,7 @@ class HIUserInfoView extends GetView { RichText( text: TextSpan( style: TextStyle( - fontSize: 10.sp, + fontSize: 12.sp, color: Colors.black, // ), children: [ diff --git a/lib/app/modules/kr_home/controllers/kr_home_controller.dart b/lib/app/modules/kr_home/controllers/kr_home_controller.dart index 8845046..d8cc687 100755 --- a/lib/app/modules/kr_home/controllers/kr_home_controller.dart +++ b/lib/app/modules/kr_home/controllers/kr_home_controller.dart @@ -206,6 +206,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { return; } bool isExpired = false; + print('current.expireTime${current.expireTime}'); if (current.expireTime.isNotEmpty) { try { isExpired = diff --git a/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart b/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart index aa13d1f..89570e8 100755 --- a/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart +++ b/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart @@ -152,7 +152,7 @@ class KRInviteController extends GetxController { /// 处理绑定邀请码 Future kr_handleBindInviteCode() async { - final text = otherInviteCodeController.text; + final text = otherInviteCodeController.text.trim(); if (text.isEmpty) { KRCommonUtil.kr_showToast('请输入邀请码'); return; diff --git a/lib/app/modules/kr_invite/views/kr_invite_view.dart b/lib/app/modules/kr_invite/views/kr_invite_view.dart index 5fec251..0dff52d 100755 --- a/lib/app/modules/kr_invite/views/kr_invite_view.dart +++ b/lib/app/modules/kr_invite/views/kr_invite_view.dart @@ -172,9 +172,10 @@ class KRInviteView extends GetView { textInputAction: TextInputAction.done, onSubmitted: (_) => controller.kr_handleBindInviteCode(), textAlign: TextAlign.center, + maxLength: 10, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), decoration: InputDecoration( - hintText: '填入邀请人邀请码兑换免费时长...', + hintText: '填码领免费时长...', hintStyle: const TextStyle(color: Color(0xFFA6A6A6)), filled: true, fillColor: Colors.transparent, diff --git a/lib/app/modules/kr_login/controllers/kr_login_controller.dart b/lib/app/modules/kr_login/controllers/kr_login_controller.dart index f82a49c..2fd8729 100755 --- a/lib/app/modules/kr_login/controllers/kr_login_controller.dart +++ b/lib/app/modules/kr_login/controllers/kr_login_controller.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; @@ -11,6 +12,7 @@ import 'package:kaer_with_panels/app/localization/app_translations.dart'; import 'package:kaer_with_panels/app/utils/kr_event_bus.dart'; import 'package:kaer_with_panels/app/services/kr_site_config_service.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; +import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart'; import '../../../localization/kr_language_utils.dart'; import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart'; @@ -97,6 +99,9 @@ class KRLoginController extends GetxController RxList kr_emailList = [].obs; RxBool kr_isDropdownVisible = false.obs; + /// 邮箱历史记录 + RxList kr_emailHistory = [].obs; + /// 定位 final LayerLink kr_layerLink = LayerLink(); OverlayEntry? overlayEntry; // 悬浮框 @@ -169,14 +174,13 @@ class KRLoginController extends GetxController } else if (entry == 'bind_email') { // 如果是从“绑定邮箱”进入,直接设置为“注册-发送验证码”状态 kr_loginStatus.value = KRLoginProgressStatus.kr_registerSetPsd; - }else if (entry == 'login') { + } else if (entry == 'login') { // 登录 kr_loginStatus.value = KRLoginProgressStatus.kr_loginByPsd; } // 如果没有匹配的 entry 值,则保持默认的 kr_loginByPsd 状态 } - // 初始化计时器 _timer = Timer(Duration.zero, () {}); @@ -279,6 +283,20 @@ class KRLoginController extends GetxController });*/ kr_initFocus(); + + // 加载邮箱历史记录 + KRSecureStorage() + .kr_readData(key: KRSecureStorage.EMAIL_HISTORY_KEY) + .then((historyString) { + if (historyString != null && historyString.isNotEmpty) { + try { + final List decodedList = jsonDecode(historyString); + kr_emailHistory.assignAll(decodedList.cast()); + } catch (e) { + print('Error decoding email history: $e'); + } + } + }); } // 判断是否是手机号 @@ -338,8 +356,7 @@ class KRLoginController extends GetxController }, (r) async { HIDialog.show( title: '*重要提示', - message: - '验证邮件已发送至邮箱,如无法找到,请检查垃圾邮件箱或营销邮件箱。', + message: '验证邮件已发送至邮箱,如无法找到,请检查垃圾邮件箱或营销邮件箱。', ); _startCountdown(); @@ -353,9 +370,8 @@ class KRLoginController extends GetxController return; } - final either = await KRAuthApi().kr_login( - accountController.text, - psdController.text); + final either = + await KRAuthApi().kr_login(accountController.text, psdController.text); either.fold((l) { KRCommonUtil.kr_showToast(l.msg); }, (r) async { @@ -403,45 +419,49 @@ class KRLoginController extends GetxController // 定义注册请求逻辑 Future performRegister() async { final either = await KRAuthApi().kr_register( - accountController.text, - psdController.text, - code: codeController.text.isEmpty ? null : codeController.text, - inviteCode: inviteCodeController.text.isEmpty ? null : inviteCodeController.text, + accountController.text, + psdController.text, + code: codeController.text.isEmpty ? null : codeController.text, + inviteCode: inviteCodeController.text.isEmpty + ? null + : inviteCodeController.text, ); either.fold( - (l) { - HIDialog.show( - message: l.msg, - preventBackDismiss: true, - confirmText: '确定', - ); - return; - }, - (r) async { + (l) { + HIDialog.show( + message: l.msg, + preventBackDismiss: true, + confirmText: '确定', + ); + return; + }, + (r) async { _saveLoginData(r); KRCommonUtil.kr_showToast('登录成功'); }, ); } + // 检查是否已有订阅 - final subscriptionResult = await KRAuthApi().kr_checkSubscription(accountController.text); + final subscriptionResult = + await KRAuthApi().kr_checkSubscription(accountController.text); subscriptionResult.fold( - (error) { + (error) { KRCommonUtil.kr_showToast(error.msg); }, - (isFullySubscribed) async { - if (isFullySubscribed) { - HIDialog.show( - message: '当前邮箱已有套餐,继续绑定会丢失设备当前套餐,是否继续?', - confirmText: '继续', - cancelText: '取消', - onConfirm: performRegister, - ); - } else { - await performRegister(); - } + (isFullySubscribed) async { + if (isFullySubscribed) { + HIDialog.show( + message: '当前邮箱已有套餐,继续绑定会丢失设备当前套餐,是否继续?', + confirmText: '继续', + cancelText: '取消', + onConfirm: performRegister, + ); + } else { + await performRegister(); + } }, ); } @@ -479,7 +499,7 @@ class KRLoginController extends GetxController accountController.text, codeController.text, kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode - ? 2 // 注册验证码类型为2 + ? 2 // 注册验证码类型为2 : 3); // 重置密码验证码类型为3 either.fold((l) { KRCommonUtil.kr_showToast(l.msg); @@ -508,9 +528,7 @@ class KRLoginController extends GetxController } final either = await KRAuthApi().kr_setNewPsdByForgetPsd( - accountController.text, - codeController.text, - psdController.text); + accountController.text, codeController.text, psdController.text); either.fold((l) { KRCommonUtil.kr_showToast(l.msg); }, (r) async { @@ -547,15 +565,43 @@ class KRLoginController extends GetxController /// 验证邮箱格式 bool validateEmail(String str) { - return RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(str); + return RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') + .hasMatch(str); } /// 设置登录数据(仅支持邮箱) void _saveLoginData(String token) { KRAppRunData.getInstance().kr_saveUserInfo( - token, - accountController.text, - ); + token, + accountController.text, + ); + + // 保存邮箱到历史记录 + KRSecureStorage() + .kr_readData(key: KRSecureStorage.EMAIL_HISTORY_KEY) + .then((historyString) async { + List history = []; + if (historyString != null && historyString.isNotEmpty) { + try { + final List decodedList = jsonDecode(historyString); + history = decodedList.cast(); + } catch (e) { + print('Error decoding email history for saving: $e'); + } + } + + // 确保不重复添加 + if (!history.contains(accountController.text)) { + history.add(accountController.text); + } + // 限制历史记录数量,例如只保留最新的5个 + if (history.length > 5) { + history = history.sublist(history.length - 5); + } + await KRSecureStorage().kr_saveData( + key: KRSecureStorage.EMAIL_HISTORY_KEY, value: jsonEncode(history)); + }); + kr_loginStatus.value = KRLoginProgressStatus.kr_check; // 登录/注册成功后,发送消息触发订阅服务刷新 diff --git a/lib/app/modules/kr_login/views/kr_login_view.dart b/lib/app/modules/kr_login/views/kr_login_view.dart index 71d767c..bacf572 100755 --- a/lib/app/modules/kr_login/views/kr_login_view.dart +++ b/lib/app/modules/kr_login/views/kr_login_view.dart @@ -124,10 +124,17 @@ class KRLoginView extends GetView { constraints: BoxConstraints(minHeight: 300.w), child: Column( children: [ - _buildStandardInputField( - controller: controller.accountController, - hintText: 'Email', - ), + Obx(() => _buildStandardInputField( + controller: controller.accountController, + hintText: '请输入邮箱地址', + suffixes: const [ + '@gmail.com', + '@outlook.com', + '@qq.com', + '@163.com' + ], + historyEmails: controller.kr_emailHistory.toList(), + )), // SizedBox(height: 10.w), // _buildStandardInputField( // controller: controller.psdController, @@ -157,11 +164,18 @@ class KRLoginView extends GetView { required TextEditingController controller, required String hintText, bool isPassword = false, + List? suffixes, + List? historyEmails, }) { - return SizedBox( - // height: 50, // 固定高度 - child: TextField( + // 基础输入框构建逻辑提取 + Widget buildTextField({ + FocusNode? focusNode, + VoidCallback? onEditingComplete, + }) { + return TextField( controller: controller, + focusNode: focusNode, + onEditingComplete: onEditingComplete, obscureText: isPassword, style: KrAppTextStyle( fontSize: 16, @@ -189,6 +203,122 @@ class KRLoginView extends GetView { borderSide: BorderSide(color: Colors.white, width: 2), ), ), + ); + } + + // 如果即没有后缀配置也没有历史记录,直接返回普通输入框 + if ((suffixes == null || suffixes.isEmpty) && + (historyEmails == null || historyEmails.isEmpty)) { + return SizedBox( + child: buildTextField(), + ); + } + + // 使用 RawAutocomplete 实现带提示的输入框 + return SizedBox( + child: RawAutocomplete( + textEditingController: controller, + focusNode: FocusNode(), + optionsBuilder: (TextEditingValue textEditingValue) { + final inputText = textEditingValue.text; + + // 结果列表 + List options = []; + + // 1. 匹配历史记录 (只要输入内容匹配历史记录的开头,或者输入为空) + if (historyEmails != null) { + if (inputText.isEmpty) { + // 理论上 RawAutocomplete 默认不显示空输入的 options,除非自定义 fieldViewBuilder 监听 + // 但 RawAutocomplete 的 optionsBuilder 在 text 变化时触发。 + // 若要空内容显示,通常需要 Focus 触发。这里先处理有内容的情况, + // 或者如果 RawAutocomplete 支持空内容(通过 initialValue? 不行,得看 triggerMode) + // 简单处理:如果为空,返回所有历史记录 + options.addAll(historyEmails); + } else { + options.addAll(historyEmails + .where((email) => email.startsWith(inputText))); + } + } + + // 2. 匹配后缀 (仅当有输入且不含 @ 或含 @ 但未完整时) + if (suffixes != null && inputText.isNotEmpty) { + if (!inputText.contains('@')) { + options.addAll(suffixes.map((suffix) => '$inputText$suffix')); + } else { + final atIndex = inputText.indexOf('@'); + final prefix = inputText.substring(0, atIndex); + final domainInput = inputText.substring(atIndex); // 包含 @ + + options.addAll(suffixes + .where((suffix) => suffix.startsWith(domainInput)) + .map((suffix) => '$prefix$suffix')); + } + } + + // 去重 + return options.toSet().toList(); + }, + fieldViewBuilder: ( + BuildContext context, + TextEditingController textEditingController, + FocusNode focusNode, + VoidCallback onFieldSubmitted, + ) { + // 这里我们使用传入的 controller,而不是 fieldViewBuilder 提供的 textEditingController + // 因为我们需要外部控制 controller。 + // 注意:RawAutocomplete 默认会监听 textEditingController, + // 如果我们传入了自己的 controller 给 RawAutocomplete, + // fieldViewBuilder 的 textEditingController 其实就是我们传入的那个。 + return buildTextField( + focusNode: focusNode, + onEditingComplete: onFieldSubmitted, + ); + }, + optionsViewBuilder: ( + BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options, + ) { + return Align( + alignment: Alignment.topLeft, + child: Material( + elevation: 4.0, + color: const Color(0xFF1E1E1E), // 深色背景 + borderRadius: BorderRadius.circular(8.r), + child: Container( + constraints: BoxConstraints( + maxHeight: 200.w, + maxWidth: 300.w, //略小于屏幕宽度,或者根据父级宽度动态计算更好,这里简单给定 + ), + width: MediaQuery.of(context).size.width - 80.w, // 减去两边 padding + child: ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: options.length, + itemBuilder: (BuildContext context, int index) { + final String option = options.elementAt(index); + return InkWell( + onTap: () { + onSelected(option); + }, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 16.w, vertical: 12.w), + child: Text( + option, + style: KrAppTextStyle( + fontSize: 14, + color: Colors.white, + ), + ), + ), + ); + }, + ), + ), + ), + ); + }, ), ); } diff --git a/lib/app/modules/kr_purchase_membership/views/kr_purchase_membership_view.dart b/lib/app/modules/kr_purchase_membership/views/kr_purchase_membership_view.dart index 5906c25..bb7cef6 100755 --- a/lib/app/modules/kr_purchase_membership/views/kr_purchase_membership_view.dart +++ b/lib/app/modules/kr_purchase_membership/views/kr_purchase_membership_view.dart @@ -247,38 +247,6 @@ class KRPurchaseMembershipView extends GetView ); } - - // 账号部分 - Widget _kr_buildAccountSection(BuildContext context) { - return Container( - margin: EdgeInsets.all(16), - padding: EdgeInsets.all(16), - decoration: BoxDecoration( - color: Theme.of(context).cardColor, - borderRadius: BorderRadius.circular(12), - ), - child: Row( - children: [ - Text( - AppTranslations.kr_purchaseMembership.myAccount, - style: KrAppTextStyle( - fontSize: 14, - color: Theme.of(context).textTheme.bodyMedium?.color, - ), - ), - const Spacer(), - Obx(() => Text( - controller.kr_userEmail.value, - style: KrAppTextStyle( - fontSize: 14, - color: Theme.of(context).textTheme.bodySmall?.color, - ), - )), - ], - ), - ); - } - // 套餐选项卡片 Widget _kr_buildPlanOptionCard( KRPackageListItem plan, @@ -428,9 +396,7 @@ class KRPurchaseMembershipView extends GetView style: TextStyle( color: Colors.black, // 白色文字更清晰 fontSize: 14, - fontWeight: index == 1 - ? FontWeight.w300 - : FontWeight.w600, + fontWeight: FontWeight.w600, ), textAlign: TextAlign.center, ), diff --git a/lib/app/network/http_util.dart b/lib/app/network/http_util.dart index 9ef6945..08004e0 100755 --- a/lib/app/network/http_util.dart +++ b/lib/app/network/http_util.dart @@ -259,7 +259,7 @@ class HttpUtil { /// 自定义简洁 HTTP 拦截器(无边框符号) class _KRSimpleHttpInterceptor extends Interceptor { /// 常量:手动控制是否打印拦截器日志与解密结果 - static const bool KR_HTTP_PRINT = true; + static const bool KR_HTTP_PRINT = false; final Dio _dio; _KRSimpleHttpInterceptor(this._dio); static String? _lastPath; diff --git a/lib/app/services/singbox_imp/kr_sing_box_imp.dart b/lib/app/services/singbox_imp/kr_sing_box_imp.dart index 633f9be..aed02f2 100755 --- a/lib/app/services/singbox_imp/kr_sing_box_imp.dart +++ b/lib/app/services/singbox_imp/kr_sing_box_imp.dart @@ -607,6 +607,15 @@ class KRSingBoxImp { KRLogUtil.kr_i('✅ [智能代理模式] region 设为 $effectiveRegion', tag: 'SingBox'); } + final useWindowsDnsDefaults = Platform.isWindows; + final remoteDnsAddress = useWindowsDnsDefaults + ? 'udp://1.1.1.1' + : 'https://dns.google/dns-query'; + final directDnsAddress = useWindowsDnsDefaults + ? (effectiveRegion == 'cn' ? 'udp://223.5.5.5' : 'udp://1.1.1.1') + : 'local'; + final dnsDomainStrategy = useWindowsDnsDefaults ? '' : 'prefer_ipv4'; + final op = { "region": effectiveRegion, // 🔧 修复:根据出站模式动态设置 region "block-ads": false, // 参考 hiddify-app: 默认关闭广告拦截 @@ -616,11 +625,10 @@ class KRSingBoxImp { "resolve-destination": false, "ipv6-mode": "ipv4_only", // 参考 hiddify-app: 仅使用 IPv4 (有效值: ipv4_only, prefer_ipv4, prefer_ipv6, ipv6_only) - "remote-dns-address": - "https://dns.google/dns-query", // 使用 Google DoH,避免中转节点 DNS 死锁 - "remote-dns-domain-strategy": "prefer_ipv4", - "direct-dns-address": "local", // 使用系统 DNS,确保中转服务器域名能被解析 - "direct-dns-domain-strategy": "prefer_ipv4", + "remote-dns-address": remoteDnsAddress, // 使用 Google DoH,避免中转节点 DNS 死锁 + "remote-dns-domain-strategy": dnsDomainStrategy, + "direct-dns-address": directDnsAddress, // 使用系统 DNS,确保中转服务器域名能被解析 + "direct-dns-domain-strategy": dnsDomainStrategy, "mixed-port": kr_port, "tproxy-port": kr_port, "local-dns-port": 36450, diff --git a/lib/app/utils/kr_common_util.dart b/lib/app/utils/kr_common_util.dart index 3e1125c..6693363 100755 --- a/lib/app/utils/kr_common_util.dart +++ b/lib/app/utils/kr_common_util.dart @@ -22,7 +22,6 @@ class KRCommonUtil { _kr_isOpeningCustomerService = true; try { - kr_showToast('userInfo.openingCustomerService'.tr); await Get.toNamed(Routes.KR_CHATWOOT); } catch (e) { debugPrint('打开客服失败: $e'); diff --git a/lib/app/utils/kr_secure_storage.dart b/lib/app/utils/kr_secure_storage.dart index fabe6b5..7380c14 100755 --- a/lib/app/utils/kr_secure_storage.dart +++ b/lib/app/utils/kr_secure_storage.dart @@ -17,6 +17,9 @@ class KRSecureStorage { // 存储箱名称 static const String _boxName = 'kaer_secure_storage'; + // 邮箱历史记录的键 + static const String EMAIL_HISTORY_KEY = 'email_history'; + // 加密密钥 static const String _encryptionKey = 'kaer_secure_storage_key'; diff --git a/lib/app/widgets/hi_base_scaffold.dart b/lib/app/widgets/hi_base_scaffold.dart index b7b4d7e..4fbf01a 100644 --- a/lib/app/widgets/hi_base_scaffold.dart +++ b/lib/app/widgets/hi_base_scaffold.dart @@ -179,7 +179,6 @@ class HIBaseScaffold extends StatelessWidget { style: TextStyle( color: Colors.black, // 副标题使用带透明度的白色 fontSize: 14, - fontWeight: FontWeight.w100, ), ), ), diff --git a/lib/singbox/service/ffi_singbox_service.dart b/lib/singbox/service/ffi_singbox_service.dart index 7883b0f..5fd8b5c 100755 --- a/lib/singbox/service/ffi_singbox_service.dart +++ b/lib/singbox/service/ffi_singbox_service.dart @@ -53,20 +53,21 @@ class FFISingboxService with InfraLogger implements SingboxService { return p.join("libcore", libName); } - // 🔧 修复:开发环境使用绝对路径 - // 尝试开发环境路径(相对于当前工作目录) - final devPath = p.join("libcore", "bin", libName); - if (kDebugMode) { - print('🔍 [FFI] 检查开发环境路径: $devPath'); - print('🔍 [FFI] 当前工作目录: ${Directory.current.path}'); - print('🔍 [FFI] 文件是否存在: ${File(devPath).existsSync()}'); - } + if (Platform.isMacOS) { + // 当前 app binary 路径 + final execPath = File(Platform.resolvedExecutable).absolute.path; - if (File(devPath).existsSync()) { - if (kDebugMode) { - print('✅ [FFI] 使用开发环境路径: $devPath'); + // App binary 在 Contents/MacOS, libcore 在 Contents/Frameworks + final bundleDir = p.dirname(p.dirname(execPath)); // 上两级到 Contents + final frameworksPath = p.join(bundleDir, 'Frameworks', libName); + + if (File(frameworksPath).existsSync()) { + if (kDebugMode) print('✅ [FFI] macOS bundle 相对路径: $frameworksPath'); + return frameworksPath; + } else { + print('⚠️ [FFI] 未找到 libcore.dylib,fallback 到默认路径: $libName'); + return libName; } - return devPath; } // 生产环境:使用相对路径(bundle中的路径) @@ -82,22 +83,28 @@ class FFISingboxService with InfraLogger implements SingboxService { print('🚀 [FFI] init() 开始'); } loggy.debug("initializing"); + final box = FFISingboxService._box; + box.setupOnce(NativeApi.initializeApiDLData); // 注意:setupOnce 会在 worker isolate 中调用(见 _ffiLoadLibrary) // 在主 isolate 中调用会导致阻塞,因此这里跳过 if (kDebugMode) { print('⏭️ [FFI] 跳过主 isolate 中的 setupOnce(将在 worker isolate 中执行)'); } - + print('📡 [FFI] 主 Isolate setupOnce 执行完毕,Port 已创建'); if (kDebugMode) { print('📡 [FFI] 创建 ReceivePort'); } _statusReceiver = ReceivePort('service status receiver'); + print('📡 [FFI] Dart port created: ${_statusReceiver.sendPort.nativePort}'); if (kDebugMode) { print('🔄 [FFI] 设置状态流'); } - final source = _statusReceiver.asBroadcastStream().map((event) => jsonDecode(event as String)).map(SingboxStatus.fromEvent); + final source = _statusReceiver + .asBroadcastStream() + .map((event) => jsonDecode(event as String)) + .map(SingboxStatus.fromEvent); _status = ValueConnectableStream.seeded( source, const SingboxStopped(), @@ -125,7 +132,7 @@ class FFISingboxService with InfraLogger implements SingboxService { _logger.debug('[黑屏调试] setup() 开始调用 libcore.dll - $startTime'); final err = await IsolateWorker().execute( - () => _ffiSetup(baseDir, workingDir, tempDir, port, debugFlag), + () => _ffiSetup(baseDir, workingDir, tempDir, port, debugFlag), allowSyncFallback: false, ); @@ -137,9 +144,15 @@ class FFISingboxService with InfraLogger implements SingboxService { _logger.error('[黑屏调试] setup() 错误: $err'); return left(err); } + if (kDebugMode) { + print('✅ [FFI][ORDER] setup() finished OK'); + } return right(unit); } catch (e) { _logger.error('[黑屏调试] setup() 异常: $e'); + if (kDebugMode) { + print('❌ [FFI][ORDER] setup() threw: $e'); + } return left(e.toString()); } }); @@ -147,15 +160,15 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither validateConfigByPath( - String path, - String tempPath, - bool debug, - ) { + String path, + String tempPath, + bool debug, + ) { final debugFlag = debug ? 1 : 0; return TaskEither(() async { try { final err = await IsolateWorker().execute( - () => _ffiValidateConfig(path, tempPath, debugFlag), + () => _ffiValidateConfig(path, tempPath, debugFlag), allowSyncFallback: false, ); if (err != null && err.isNotEmpty) { @@ -177,7 +190,7 @@ class FFISingboxService with InfraLogger implements SingboxService { _logger.debug('[黑屏调试] changeOptions 开始调用 libcore.dll - $startTime'); final err = await IsolateWorker().execute( - () => _ffiChangeOptions(json), + () => _ffiChangeOptions(json), allowSyncFallback: false, ); @@ -199,12 +212,12 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither generateFullConfigByPath( - String path, - ) { + String path, + ) { return TaskEither(() async { try { final result = await IsolateWorker().execute( - () => _ffiGenerateFullConfig(path), + () => _ffiGenerateFullConfig(path), allowSyncFallback: false, ); final ok = result.isNotEmpty && result[0] == true; @@ -221,18 +234,21 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither start( - String configPath, - String name, - bool disableMemoryLimit, - ) { + String configPath, + String name, + bool disableMemoryLimit, + ) { loggy.debug("starting, memory limit: [${!disableMemoryLimit}]"); + if (kDebugMode) { + print('🚦 [FFI][ORDER] start() entered (after setup)'); + } return TaskEither(() async { try { final startTime = DateTime.now(); _logger.debug('[黑屏调试] start() 开始调用 libcore.dll - $startTime'); final err = await IsolateWorker().execute( - () => _ffiStart(configPath, disableMemoryLimit), + () => _ffiStart(configPath, disableMemoryLimit), allowSyncFallback: false, ); @@ -244,9 +260,15 @@ class FFISingboxService with InfraLogger implements SingboxService { _logger.error('[黑屏调试] start() 错误: $err'); return left(err); } + if (kDebugMode) { + print('✅ [FFI][ORDER] start() finished OK'); + } return right(unit); } catch (e) { _logger.error('[黑屏调试] start() 异常: $e'); + if (kDebugMode) { + print('❌ [FFI][ORDER] start() threw: $e'); + } return left(e.toString()); } }); @@ -282,15 +304,15 @@ class FFISingboxService with InfraLogger implements SingboxService { @override TaskEither restart( - String configPath, - String name, - bool disableMemoryLimit, - ) { + String configPath, + String name, + bool disableMemoryLimit, + ) { loggy.debug("restarting, memory limit: [${!disableMemoryLimit}]"); return TaskEither(() async { try { final err = await IsolateWorker().execute( - () => _ffiRestart(configPath, disableMemoryLimit), + () => _ffiRestart(configPath, disableMemoryLimit), allowSyncFallback: false, ); if (err != null && err.isNotEmpty) { @@ -328,7 +350,7 @@ class FFISingboxService with InfraLogger implements SingboxService { _serviceStatsStream = null; }, ).map( - (event) { + (event) { if (event case String _) { if (event.startsWith('error:')) { loggy.error("[service stats client] error received: $event"); @@ -343,12 +365,18 @@ class FFISingboxService with InfraLogger implements SingboxService { }, ); - final err = _box.startCommandClient(1, receiver.sendPort.nativePort).cast().toDartString(); + if (kDebugMode) { + print( + '📡 [FFI][ORDER] watchStats startCommandClient port=${receiver.sendPort.nativePort}'); + } + final err = _box + .startCommandClient(1, receiver.sendPort.nativePort) + .cast() + .toDartString(); if (err.isNotEmpty) { loggy.error("error starting status command: $err"); throw err; } - return _serviceStatsStream = statusStream; } @@ -368,7 +396,7 @@ class FFISingboxService with InfraLogger implements SingboxService { } }, ).map( - (event) { + (event) { if (event case String _) { if (event.startsWith('error:')) { logger.error("error received: $event"); @@ -385,7 +413,14 @@ class FFISingboxService with InfraLogger implements SingboxService { ); try { - final err = _box.startCommandClient(5, receiver.sendPort.nativePort).cast().toDartString(); + if (kDebugMode) { + print( + '📡 [FFI][ORDER] watchGroups startCommandClient port=${receiver.sendPort.nativePort}'); + } + final err = _box + .startCommandClient(5, receiver.sendPort.nativePort) + .cast() + .toDartString(); if (err.isNotEmpty) { logger.error("error starting group command: $err"); throw err; @@ -412,7 +447,7 @@ class FFISingboxService with InfraLogger implements SingboxService { } }, ).map( - (event) { + (event) { if (event case String _) { if (event.startsWith('error:')) { logger.error(event); @@ -429,7 +464,14 @@ class FFISingboxService with InfraLogger implements SingboxService { ); try { - final err = _box.startCommandClient(13, receiver.sendPort.nativePort).cast().toDartString(); + if (kDebugMode) { + print( + '📡 [FFI][ORDER] watchActiveGroups startCommandClient port=${receiver.sendPort.nativePort}'); + } + final err = _box + .startCommandClient(13, receiver.sendPort.nativePort) + .cast() + .toDartString(); if (err.isNotEmpty) { logger.error("error starting: $err"); throw err; @@ -447,7 +489,7 @@ class FFISingboxService with InfraLogger implements SingboxService { return TaskEither(() async { try { final err = await IsolateWorker().execute( - () => _ffiSelectOutbound(groupTag, outboundTag), + () => _ffiSelectOutbound(groupTag, outboundTag), allowSyncFallback: false, ); if (err != null && err.isNotEmpty) { @@ -465,7 +507,7 @@ class FFISingboxService with InfraLogger implements SingboxService { return TaskEither(() async { try { final err = await IsolateWorker().execute( - () => _ffiUrlTest(groupTag), + () => _ffiUrlTest(groupTag), allowSyncFallback: false, ); if (err != null && err.isNotEmpty) { @@ -484,7 +526,9 @@ class FFISingboxService with InfraLogger implements SingboxService { @override Stream> watchLogs(String path) async* { yield await _readLogFile(File(path)); - yield* Watcher(path, pollingDelay: const Duration(seconds: 1)).events.asyncMap((event) async { + yield* Watcher(path, pollingDelay: const Duration(seconds: 1)) + .events + .asyncMap((event) async { if (event.type == ChangeType.MODIFY) { await _readLogFile(File(path)); } @@ -502,7 +546,8 @@ class FFISingboxService with InfraLogger implements SingboxService { Future> _readLogFile(File file) async { if (_logFilePosition == 0 && file.lengthSync() == 0) return []; - final content = await file.openRead(_logFilePosition).transform(utf8.decoder).join(); + final content = + await file.openRead(_logFilePosition).transform(utf8.decoder).join(); _logFilePosition = file.lengthSync(); final lines = const LineSplitter().convert(content); if (lines.length > 300) { @@ -527,7 +572,8 @@ class FFISingboxService with InfraLogger implements SingboxService { return TaskEither(() async { try { final result = await IsolateWorker().execute( - () => _ffiGenerateWarpConfig(licenseKey, previousAccountId, previousAccessToken), + () => _ffiGenerateWarpConfig( + licenseKey, previousAccountId, previousAccessToken), allowSyncFallback: false, ); final ok = result.isNotEmpty && result[0] == true; @@ -552,38 +598,45 @@ SingboxNativeLibrary _ffiLoadLibrary() { } String? _ffiSetup( - String baseDir, - String workingDir, - String tempDir, - int statusPort, - int debugFlag, - ) { + String baseDir, + String workingDir, + String tempDir, + int statusPort, + int debugFlag, +) { + if (kDebugMode) { + print( + '🧭 [FFI][ORDER] _ffiSetup inputs statusPort=$statusPort base="$baseDir" working="$workingDir" temp="$tempDir" debug="$debugFlag"'); + } final box = _ffiLoadLibrary(); final err = box .setup( - baseDir.toNativeUtf8().cast(), - workingDir.toNativeUtf8().cast(), - tempDir.toNativeUtf8().cast(), - statusPort, - debugFlag, - ) + baseDir.toNativeUtf8().cast(), + workingDir.toNativeUtf8().cast(), + tempDir.toNativeUtf8().cast(), + statusPort, + debugFlag, + ) .cast() .toDartString(); + if (kDebugMode) { + print('🧭 [FFI][ORDER] _ffiSetup returned err="${err.isEmpty ? '' : err}"'); + } return err.isEmpty ? null : err; } String? _ffiValidateConfig( - String path, - String tempPath, - int debugFlag, - ) { + String path, + String tempPath, + int debugFlag, +) { final box = _ffiLoadLibrary(); final err = box .parse( - path.toNativeUtf8().cast(), - tempPath.toNativeUtf8().cast(), - debugFlag, - ) + path.toNativeUtf8().cast(), + tempPath.toNativeUtf8().cast(), + debugFlag, + ) .cast() .toDartString(); return err.isEmpty ? null : err; @@ -591,7 +644,10 @@ String? _ffiValidateConfig( String? _ffiChangeOptions(String optionsJson) { final box = _ffiLoadLibrary(); - final err = box.changeHiddifyOptions(optionsJson.toNativeUtf8().cast()).cast().toDartString(); + final err = box + .changeHiddifyOptions(optionsJson.toNativeUtf8().cast()) + .cast() + .toDartString(); return err.isEmpty ? null : err; } @@ -599,8 +655,8 @@ List _ffiGenerateFullConfig(String path) { final box = _ffiLoadLibrary(); final response = box .generateConfig( - path.toNativeUtf8().cast(), - ) + path.toNativeUtf8().cast(), + ) .cast() .toDartString(); if (response.startsWith("error")) { @@ -613,9 +669,9 @@ String? _ffiStart(String configPath, bool disableMemoryLimit) { final box = _ffiLoadLibrary(); final err = box .start( - configPath.toNativeUtf8().cast(), - disableMemoryLimit ? 1 : 0, - ) + configPath.toNativeUtf8().cast(), + disableMemoryLimit ? 1 : 0, + ) .cast() .toDartString(); return err.isEmpty ? null : err; @@ -631,9 +687,9 @@ String? _ffiRestart(String configPath, bool disableMemoryLimit) { final box = _ffiLoadLibrary(); final err = box .restart( - configPath.toNativeUtf8().cast(), - disableMemoryLimit ? 1 : 0, - ) + configPath.toNativeUtf8().cast(), + disableMemoryLimit ? 1 : 0, + ) .cast() .toDartString(); return err.isEmpty ? null : err; @@ -643,9 +699,9 @@ String? _ffiSelectOutbound(String groupTag, String outboundTag) { final box = _ffiLoadLibrary(); final err = box .selectOutbound( - groupTag.toNativeUtf8().cast(), - outboundTag.toNativeUtf8().cast(), - ) + groupTag.toNativeUtf8().cast(), + outboundTag.toNativeUtf8().cast(), + ) .cast() .toDartString(); return err.isEmpty ? null : err; @@ -653,22 +709,23 @@ String? _ffiSelectOutbound(String groupTag, String outboundTag) { String? _ffiUrlTest(String groupTag) { final box = _ffiLoadLibrary(); - final err = box.urlTest(groupTag.toNativeUtf8().cast()).cast().toDartString(); + final err = + box.urlTest(groupTag.toNativeUtf8().cast()).cast().toDartString(); return err.isEmpty ? null : err; } List _ffiGenerateWarpConfig( - String licenseKey, - String previousAccountId, - String previousAccessToken, - ) { + String licenseKey, + String previousAccountId, + String previousAccessToken, +) { final box = _ffiLoadLibrary(); final response = box .generateWarpConfig( - licenseKey.toNativeUtf8().cast(), - previousAccountId.toNativeUtf8().cast(), - previousAccessToken.toNativeUtf8().cast(), - ) + licenseKey.toNativeUtf8().cast(), + previousAccountId.toNativeUtf8().cast(), + previousAccessToken.toNativeUtf8().cast(), + ) .cast() .toDartString(); if (response.startsWith("error:")) { diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 5109d28..060bd7c 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -564,6 +564,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /usr/lib/swift, + "'@executable_path/../Frameworks'", + "\"${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", + ); MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; @@ -581,10 +587,11 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN; @@ -595,6 +602,7 @@ MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "HiFastVPN-Mac-Pord"; SWIFT_VERSION = 5.0; }; name = Profile; @@ -654,6 +662,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /usr/lib/swift, + "'@executable_path/../Frameworks'", + "\"${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", + ); MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -703,6 +717,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /usr/lib/swift, + "'@executable_path/../Frameworks'", + "\"${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", + ); MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; @@ -720,10 +740,11 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN; @@ -734,6 +755,7 @@ MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "HiFastVPN-Mac-Pord"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; @@ -748,10 +770,11 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN; @@ -762,6 +785,7 @@ MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "HiFastVPN-Mac-Pord"; SWIFT_VERSION = 5.0; }; name = Release; diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index 24db5ca..b1cc9fc 100755 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -3,23 +3,19 @@ com.apple.security.app-sandbox - - com.apple.security.application-groups - - $(TeamIdentifierPrefix) - - com.apple.security.assets.pictures.read-write - - com.apple.security.personal-information.photos-library - + com.apple.security.cs.allow-dyld-environment-variables com.apple.security.cs.allow-jit com.apple.security.cs.allow-unsigned-executable-memory + com.apple.security.cs.disable-executable-page-protection + com.apple.security.cs.disable-library-validation + com.apple.security.files.user-selected.read-write + com.apple.security.network.client com.apple.security.network.server diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 64b0785..b1cc9fc 100755 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -3,19 +3,19 @@ com.apple.security.app-sandbox - - com.apple.security.assets.pictures.read-write - - com.apple.security.personal-information.photos-library - + com.apple.security.cs.allow-dyld-environment-variables com.apple.security.cs.allow-jit com.apple.security.cs.allow-unsigned-executable-memory + com.apple.security.cs.disable-executable-page-protection + com.apple.security.cs.disable-library-validation + com.apple.security.files.user-selected.read-write + com.apple.security.network.client com.apple.security.network.server diff --git a/pubspec.yaml b/pubspec.yaml index 34713dc..f4496e9 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+102 +version: 1.0.0+103 environment: sdk: ">=3.5.0 <4.0.0"