diff --git a/android/app/build.gradle b/android/app/build.gradle
index cdfe9ce..3f1ca4b 100755
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -62,7 +62,8 @@ android {
versionName flutterVersionName
multiDexEnabled true
manifestPlaceholders = [
- 'android.permission.ACCESS_NETWORK_STATE': true
+ 'android.permission.ACCESS_NETWORK_STATE': true,
+ 'OPENINSTALL_APPKEY' : "alf57p",
]
android.defaultConfig.manifestPlaceholders += [
'android:screenOrientation': "portrait"
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 016ad70..400d4fc 100755
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -23,10 +23,6 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2.8.2)
- - Flutter
- device_info_plus (0.0.1):
- Flutter
- EasyPermissionX/Camera (0.0.2)
@@ -30,6 +24,10 @@ PODS:
- in_app_purchase_storekit (0.0.1):
- Flutter
- FlutterMacOS
+ - libOpenInstallSDK (2.9.2)
+ - openinstall_flutter_plugin (0.0.1):
+ - Flutter
+ - libOpenInstallSDK
- OrderedSet (6.0.3)
- package_info_plus (0.4.5):
- Flutter
@@ -47,7 +45,6 @@ PODS:
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- - crisp_chat (from `.symlinks/plugins/crisp_chat/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- EasyPermissionX/Camera
- Flutter (from `Flutter`)
@@ -56,6 +53,7 @@ DEPENDENCIES:
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
- gal (from `.symlinks/plugins/gal/darwin`)
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
+ - openinstall_flutter_plugin (from `.symlinks/plugins/openinstall_flutter_plugin/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
@@ -64,8 +62,8 @@ DEPENDENCIES:
SPEC REPOS:
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
- - Crisp
- EasyPermissionX
+ - libOpenInstallSDK
- OrderedSet
- ReachabilitySwift
- SAMKeychain
@@ -73,8 +71,6 @@ SPEC REPOS:
EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
- crisp_chat:
- :path: ".symlinks/plugins/crisp_chat/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
@@ -89,6 +85,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/gal/darwin"
in_app_purchase_storekit:
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
+ openinstall_flutter_plugin:
+ :path: ".symlinks/plugins/openinstall_flutter_plugin/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@@ -102,8 +100,6 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
- Crisp: 6747c96b2b2c2a81babf1eaecd1688a65d98edd4
- crisp_chat: 30994104495de23443af8d5b2c041a6df1e8464d
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
EasyPermissionX: ff4c438f6ee80488f873b4cb921e32d982523067
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
@@ -112,6 +108,8 @@ SPEC CHECKSUMS:
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
in_app_purchase_storekit: a1ce04056e23eecc666b086040239da7619cd783
+ libOpenInstallSDK: 1e123fde796902007e6a25797cdf34c20552fc6e
+ openinstall_flutter_plugin: e6b8486f834eb60b336546442a8b747d4b664cf4
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index ea138b8..1198af1 100755
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -762,11 +762,9 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = PacketTunnel/HiddifyPacketTunnel.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB;
+ DEVELOPMENT_TEAM = NJRRF427XB;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
EXCLUDED_ARCHS = armv7;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -792,7 +790,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).PacketTunnel";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord-PacketTunnel";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
@@ -818,11 +815,9 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnelRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB;
+ DEVELOPMENT_TEAM = NJRRF427XB;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
EXCLUDED_ARCHS = armv7;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -848,7 +843,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).PacketTunnel";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord-PacketTunnel";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
@@ -872,11 +866,9 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = PacketTunnel/HiddifyPacketTunnel.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB;
+ DEVELOPMENT_TEAM = NJRRF427XB;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
EXCLUDED_ARCHS = armv7;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -902,7 +894,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).PacketTunnel";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord-PacketTunnel";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
@@ -980,11 +971,9 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB;
+ DEVELOPMENT_TEAM = NJRRF427XB;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7;
INFOPLIST_FILE = Runner/Info.plist;
@@ -1016,7 +1005,6 @@
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.taw.hifastvpn;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
@@ -1212,11 +1200,9 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB;
+ DEVELOPMENT_TEAM = NJRRF427XB;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
@@ -1248,7 +1234,6 @@
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.taw.hifastvpn;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
@@ -1270,11 +1255,9 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB;
+ DEVELOPMENT_TEAM = NJRRF427XB;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7;
INFOPLIST_FILE = Runner/Info.plist;
@@ -1306,7 +1289,6 @@
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.taw.hifastvpn;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 9cd4b8c..0d936ad 100755
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -15,6 +15,14 @@ import Libcore
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+ override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
+ return super.application(app, open: url, options: options)
+ }
+
+ override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
+ return super.application(application, continue: userActivity, restorationHandler: restorationHandler)
+ }
+
func setupFileManager() {
try? FileManager.default.createDirectory(at: FilePath.workingDirectory, withIntermediateDirectories: true)
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index aed9fe8..3ad8e58 100755
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -88,5 +88,7 @@
UIViewControllerBasedStatusBarAppearance
+ com.openinstall.APP_KEY
+ alf57p
diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements
index 53d974b..1d2cf40 100755
--- a/ios/Runner/Runner.entitlements
+++ b/ios/Runner/Runner.entitlements
@@ -4,6 +4,11 @@
aps-environment
development
+ com.apple.developer.associated-domains
+
+ applinks:alf57p.openinstall.com
+ applinks:alf57p.oplinking.com
+
com.apple.developer.networking.networkextension
packet-tunnel-provider
diff --git a/ios/Runner/RunnerRelease.entitlements b/ios/Runner/RunnerRelease.entitlements
index 53d974b..1d2cf40 100755
--- a/ios/Runner/RunnerRelease.entitlements
+++ b/ios/Runner/RunnerRelease.entitlements
@@ -4,6 +4,11 @@
aps-environment
development
+ com.apple.developer.associated-domains
+
+ applinks:alf57p.openinstall.com
+ applinks:alf57p.oplinking.com
+
com.apple.developer.networking.networkextension
packet-tunnel-provider
diff --git a/ios/exportOptions_dev.plist b/ios/exportOptions_dev.plist
new file mode 100755
index 0000000..af39c3a
--- /dev/null
+++ b/ios/exportOptions_dev.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ compileBitcode
+
+ method
+ development signingStyle
+ automatic thinning
+ <none>
+
+
\ No newline at end of file
diff --git a/lib/app/common/app_run_data.dart b/lib/app/common/app_run_data.dart
index 6d1847e..2c096e9 100755
--- a/lib/app/common/app_run_data.dart
+++ b/lib/app/common/app_run_data.dart
@@ -25,11 +25,15 @@ import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart';
import 'package:kaer_with_panels/app/services/api_service/kr_auth_api.dart';
import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart';
import 'package:kaer_with_panels/app/utils/kr_common_util.dart';
+import 'package:kaer_with_panels/app/utils/kr_device_util.dart';
+import 'package:openinstall_flutter_plugin/openinstall_flutter_plugin.dart';
+import 'dart:io' show Platform;
class KRAppRunData {
static final KRAppRunData _instance = KRAppRunData._internal();
static const String _keyUserInfo = 'USER_INFO';
+ static const bool inviteDebugMode = true;
/// 登录token
String? kr_token;
@@ -61,6 +65,9 @@ class KRAppRunData {
// 需要被监听的属性,用 obs 包装
final kr_isLogin = false.obs;
+ // 存储临时待绑定的邀请码(处理唤醒数据比登录完成早的情况)
+ String? _kr_pendingInviteCode;
+
KRAppRunData._internal();
factory KRAppRunData() => _instance;
@@ -447,6 +454,16 @@ class KRAppRunData {
kr_isLogin.value = true;
print('✅ 已标记为登录状态');
+
+ // 静默邀请绑定
+ if (inviteDebugMode) {
+ // Debug 模式下等待调试弹窗完成,避免页面跳转
+ await _kr_handleSilentInvitation();
+ } else {
+ // 正式环境异步执行,不阻塞主流程
+ _kr_handleSilentInvitation();
+ }
+
_logStepTiming('设备登录完成');
return true;
},
@@ -496,6 +513,9 @@ class KRAppRunData {
KRLogUtil.kr_i('✅ Token和账号验证通过,设置登录状态为true', tag: 'AppRunData');
KRLogUtil.kr_i('📊 恢复账号: ${kr_account.value}', tag: 'AppRunData');
kr_isLogin.value = true;
+
+ // 🔧 新增:恢复登录状态后也尝试检测一次静默邀请(重要:针对已安装后启动的情况)
+ _kr_handleSilentInvitation();
} else {
// 账号信息为空,清理旧数据
KRLogUtil.kr_w('⚠️ 账号信息为空,清理该条用户数据', tag: 'AppRunData');
@@ -633,4 +653,125 @@ class KRAppRunData {
KRLogUtil.kr_e('📚 [AppRunData] 错误堆栈: $stackTrace', tag: 'AppRunData');
}
}
+ /// 处理静默邀请
+ Future _kr_handleSilentInvitation() async {
+ KRLogUtil.kr_i('🚀 开始处理静默邀请...', tag: 'AppRunData');
+ String? inviteCode;
+
+ // 1. 先检查是否有之前通过唤醒/安装暂存的邀请码
+ if (_kr_pendingInviteCode != null && _kr_pendingInviteCode!.isNotEmpty) {
+ inviteCode = _kr_pendingInviteCode;
+ KRLogUtil.kr_i('📎 使用暂存的待绑定邀请码: $inviteCode', tag: 'AppRunData');
+ }
+
+ // 2. 如果没有暂存码,尝试从平台环境获取
+ if (inviteCode == null || inviteCode!.isEmpty) {
+ try {
+ if (Platform.isMacOS || Platform.isWindows) {
+ inviteCode = await KRDeviceUtil().kr_getDesktopInviteCode();
+ } else if (Platform.isAndroid || Platform.isIOS) {
+ final Completer completer = Completer();
+ OpeninstallFlutterPlugin().install((data) async {
+ final code = kr_parseInviteCodeFromData(data);
+ KRLogUtil.kr_i('收到 OpenInstall 安装数据: $data, 解析出邀请码: $code', tag: 'AppRunData');
+ if (!completer.isCompleted) completer.complete(code);
+ });
+ inviteCode = await completer.future
+ .timeout(const Duration(seconds: 8), onTimeout: () => null);
+ }
+ } catch (e) {
+ KRLogUtil.kr_e('获取静默邀请码异常: $e', tag: 'AppRunData');
+ }
+ }
+
+ if (inviteCode != null && inviteCode!.isNotEmpty) {
+ KRLogUtil.kr_i('🔍 最终识别到邀请码: $inviteCode', tag: 'AppRunData');
+
+ if (inviteDebugMode) {
+ // Debug 模式下弹出对话框确认
+ final bool isDesktop = Platform.isMacOS || Platform.isWindows;
+ await HIDialog.show(
+ title: isDesktop ? '调试:唤醒识别到邀请码' : '调试:邀请码绑定确认',
+ message: isDesktop
+ ? '桌面端识别到邀请码:$inviteCode\n是否进行绑定?'
+ : '识别到邀请码:$inviteCode\n是否进行绑定?',
+ confirmText: isDesktop ? '绑定' : '确认绑定',
+ cancelText: isDesktop ? '跳过' : '取消',
+ onConfirm: () async {
+ await _kr_performInviteBinding(inviteCode!);
+ _kr_pendingInviteCode = null; // 绑定后清除
+ },
+ onCancel: () {
+ _kr_pendingInviteCode = null; // 取消也清除,避免重复弹窗
+ },
+ );
+ } else {
+ // 正式环境静默绑定
+ await _kr_performInviteBinding(inviteCode!);
+ _kr_pendingInviteCode = null; // 绑定后清除
+ }
+ } else {
+ KRLogUtil.kr_i('⚠️ 未识别到有效的邀请码,跳外静默绑定', tag: 'AppRunData');
+ }
+ }
+
+ /// 执行邀请码绑定请求
+ Future _kr_performInviteBinding(String inviteCode) async {
+ KRLogUtil.kr_i('🚀 准备执行邀请码绑定: $inviteCode', tag: 'AppRunData');
+ final result =
+ await KRUserApi().hi_inviteCode(inviteCode, isSilentInvite: true);
+ result.fold(
+ (error) => KRLogUtil.kr_w('❌ 邀请绑定失败: ${error.msg}', tag: 'AppRunData'),
+ (_) => KRLogUtil.kr_i('✅ 邀请绑定成功', tag: 'AppRunData'),
+ );
+ }
+
+ /// 公开方法:直接处理 OpenInstall 返回的原始数据(用于唤醒等场景)
+ Future kr_handleOpenInstallData(Map data) async {
+ final code = kr_parseInviteCodeFromData(data);
+ if (code != null && code.isNotEmpty) {
+ KRLogUtil.kr_i('🔗 收到 OpenInstall 原始参数并触发解析: $code', tag: 'AppRunData');
+
+ // 暂存该邀请码
+ _kr_pendingInviteCode = code;
+
+ // 如果当前已经是登录状态,则由于是唤醒(Hot Start)触发,直接按业务逻辑处理
+ if (kr_isLogin.value) {
+ KRLogUtil.kr_i('✅ 用户已登录,立即处理唤醒绑定', tag: 'AppRunData');
+ if (inviteDebugMode) {
+ await HIDialog.show(
+ title: '调试:唤醒识别到邀请码',
+ message: '唤醒数据解析到邀请码:$code\n是否进行绑定?',
+ confirmText: '绑定',
+ cancelText: '跳过',
+ onConfirm: () async {
+ await _kr_performInviteBinding(code);
+ _kr_pendingInviteCode = null;
+ },
+ onCancel: () => _kr_pendingInviteCode = null,
+ );
+ } else {
+ _kr_performInviteBinding(code).then((_) => _kr_pendingInviteCode = null);
+ }
+ } else {
+ KRLogUtil.kr_i('⏳ 用户未登录,已暂存邀请码,等待登录完成后自动绑定', tag: 'AppRunData');
+ }
+ }
+ }
+
+ /// 从 OpenInstall 数据中解析邀请码
+ String? kr_parseInviteCodeFromData(Map data) {
+ try {
+ if (data.containsKey('bindData')) {
+ final bindDataStr = data['bindData'] as String?;
+ if (bindDataStr != null && bindDataStr.isNotEmpty) {
+ final Map bindData = jsonDecode(bindDataStr);
+ return bindData['inviteCode']?.toString();
+ }
+ }
+ } catch (e) {
+ KRLogUtil.kr_e('解析 OpenInstall 数据中邀请码失败: $e', tag: 'AppRunData');
+ }
+ return null;
+ }
}
diff --git a/lib/app/modules/kr_splash/controllers/kr_splash_controller.dart b/lib/app/modules/kr_splash/controllers/kr_splash_controller.dart
index abc5f06..8bbd460 100755
--- a/lib/app/modules/kr_splash/controllers/kr_splash_controller.dart
+++ b/lib/app/modules/kr_splash/controllers/kr_splash_controller.dart
@@ -1,5 +1,6 @@
import 'package:get/get.dart';
import 'dart:convert';
+import 'package:openinstall_flutter_plugin/openinstall_flutter_plugin.dart';
import 'dart:io' show Platform, SocketException;
import 'dart:math';
@@ -165,6 +166,9 @@ class KRSplashController extends GetxController {
}),
]);
+ // 静默邀请初始化(在主流程完成后进行,不影响启动速度)
+ _kr_initOpenInstall();
+
_initLog.logPhaseEnd('主初始化流程', success: true);
} on TimeoutException catch (e) {
// 🔧 P2优化:超时错误提供更友好的提示
@@ -558,6 +562,22 @@ class KRSplashController extends GetxController {
_kr_initialize();
}
+ /// 初始化 OpenInstall
+ void _kr_initOpenInstall() {
+ if (Platform.isAndroid || Platform.isIOS) {
+ try {
+ KRLogUtil.kr_i('🚀 初始化 OpenInstall...', tag: 'SplashController');
+ OpeninstallFlutterPlugin().init((data) async {
+ KRLogUtil.kr_i('收到 OpenInstall 唤醒数据: $data', tag: 'SplashController');
+ // 处理唤醒时的邀请绑定
+ KRAppRunData().kr_handleOpenInstallData(data);
+ });
+ } catch (e) {
+ KRLogUtil.kr_e('OpenInstall 初始化失败: $e', tag: 'SplashController');
+ }
+ }
+ }
+
// 🔧 P3优化:跳过初始化,直接进入主页
void kr_skipInitialization() {
KRLogUtil.kr_i('⏭️ 用户选择跳过初始化', tag: 'SplashController');
diff --git a/lib/app/network/http_util.dart b/lib/app/network/http_util.dart
index f96acb7..9ef6945 100755
--- a/lib/app/network/http_util.dart
+++ b/lib/app/network/http_util.dart
@@ -131,9 +131,11 @@ class HttpUtil {
}
}
- /// request请求:T为转换的实体类, path:请求地址,query:请求参数, method: 请求方法, isShowLoading(可选): 是否显示加载中的状态,默认true显示, false为不显示
+ /// request请求:T为转换的实体类, path:请求地址,query:请求参数, method: 请求方法, isShowLoading(可选): 是否显示加载中的状态,默认true显示, false为不显示, silentInvite: 是否为静默邀请
Future> request(String path, Map params,
- {HttpMethod method = HttpMethod.POST, bool isShowLoading = true}) async {
+ {HttpMethod method = HttpMethod.POST,
+ bool isShowLoading = true,
+ bool isSilentInvite = false}) async {
try {
// 每次请求前更新baseUrl,确保使用最新的域名
updateBaseUrl();
@@ -148,43 +150,40 @@ class HttpUtil {
// 初始化请求头
final headers = _initHeader('signature', 'userId', 'token');
+ if (isSilentInvite) {
+ headers['X-Client-Mode'] = 'invite_silent';
+ }
+
+ final options = Options(
+ contentType: "application/json",
+ headers: headers,
+ extra: {'silentInvite': isSilentInvite},
+ );
Response