|
|
|
@ -24,9 +24,16 @@ class SplashController extends GetxController { |
|
|
|
final _loadingProgress = 0.0.obs; |
|
|
|
double get loadingProgress => _loadingProgress.value; |
|
|
|
|
|
|
|
// 资源加载状态 |
|
|
|
final _isResourceLoaded = false.obs; |
|
|
|
bool get isResourceLoaded => _isResourceLoaded.value; |
|
|
|
// 各项任务完成状态 |
|
|
|
final _isTrackingPermissionRequested = false.obs; // 追踪权限请求完成 |
|
|
|
final _isVersionCheckCompleted = false.obs; // 版本检查完成 |
|
|
|
final _isTokenValidated = false.obs; // Token验证完成 |
|
|
|
final _isAppConfigLoaded = false.obs; // 应用配置加载完成 |
|
|
|
final _isAnimationCompleted = false.obs; // 动画完成 |
|
|
|
|
|
|
|
// 登录状态 |
|
|
|
final _isLoggedIn = false.obs; |
|
|
|
bool get isLoggedIn => _isLoggedIn.value; |
|
|
|
|
|
|
|
final _hasAcceptedPrivacy = false.obs; //是否同意隐私政策 |
|
|
|
|
|
|
|
@ -59,38 +66,45 @@ class SplashController extends GetxController { |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 异步处理用户追踪授权,不阻塞主流程 |
|
|
|
_requestTrackingPermissionAsync(); |
|
|
|
// 注意:追踪权限请求将在用户同意隐私政策后执行 |
|
|
|
} |
|
|
|
|
|
|
|
/// 异步请求追踪权限,不阻塞启动流程 |
|
|
|
void _requestTrackingPermissionAsync() { |
|
|
|
Future.delayed(const Duration(milliseconds: 500), () async { |
|
|
|
try { |
|
|
|
await AppTrackingTransparency.requestTrackingAuthorization(); |
|
|
|
Logger.d(_tag, '用户追踪权限请求完成'); |
|
|
|
} catch (e) { |
|
|
|
Logger.e(_tag, '用户追踪权限请求失败: $e'); |
|
|
|
} |
|
|
|
}); |
|
|
|
/// 异步请求追踪权限 |
|
|
|
Future<void> _requestTrackingPermissionAsync() async { |
|
|
|
try { |
|
|
|
await AppTrackingTransparency.requestTrackingAuthorization(); |
|
|
|
Logger.d(_tag, '用户追踪权限请求完成'); |
|
|
|
} catch (e) { |
|
|
|
Logger.e(_tag, '用户追踪权限请求失败: $e'); |
|
|
|
} finally { |
|
|
|
_isTrackingPermissionRequested.value = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void _startNormalFlow() { |
|
|
|
_startLoadingAnimation(); |
|
|
|
_loadAppResources(); |
|
|
|
_startParallelTasks(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 智能调度版本检查,在页面跳转后执行,完全不影响启动流程 |
|
|
|
void _scheduleVersionCheck() { |
|
|
|
// 在独立的微任务中执行,避免阻塞主线程 |
|
|
|
scheduleMicrotask(() async { |
|
|
|
try { |
|
|
|
Logger.d(_tag, '开始执行版本检查(后台任务)'); |
|
|
|
_checkVersionUpdateIfNeeded(); |
|
|
|
} catch (e) { |
|
|
|
Logger.e(_tag, '版本检查调度失败: $e'); |
|
|
|
} |
|
|
|
}); |
|
|
|
/// 启动并行任务:在1.5秒动画期间同时执行多个任务 |
|
|
|
void _startParallelTasks() { |
|
|
|
// 并行执行所有任务 |
|
|
|
_requestTrackingPermissionAsync(); |
|
|
|
_performVersionCheckAsync(); |
|
|
|
_validateTokenAsync(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 异步执行版本检查 |
|
|
|
Future<void> _performVersionCheckAsync() async { |
|
|
|
try { |
|
|
|
Logger.d(_tag, '开始执行版本检查'); |
|
|
|
await _checkVersionUpdateIfNeeded(); |
|
|
|
} catch (e) { |
|
|
|
Logger.e(_tag, '版本检查失败: $e'); |
|
|
|
} |
|
|
|
//finally { |
|
|
|
// _isVersionCheckCompleted.value = true; |
|
|
|
// } |
|
|
|
} |
|
|
|
|
|
|
|
void _onPrivacyAccepted() { |
|
|
|
@ -115,10 +129,10 @@ class SplashController extends GetxController { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// 进度条动画(持续2秒) |
|
|
|
// 进度条动画(持续1.5秒) |
|
|
|
void _startLoadingAnimation() { |
|
|
|
const totalDuration = 1500; // 动画总时长增加到1.5秒 |
|
|
|
const steps = totalDuration ~/ 30; // 30的间隔,更平滑的动画 |
|
|
|
const totalDuration = 1500; // 动画总时长1.5秒 |
|
|
|
const steps = totalDuration ~/ 30; // 30ms间隔,更平滑的动画 |
|
|
|
const stepValue = 1.0 / steps; |
|
|
|
|
|
|
|
Timer.periodic(const Duration(milliseconds: 30), (timer) { |
|
|
|
@ -126,54 +140,53 @@ class SplashController extends GetxController { |
|
|
|
_loadingProgress.value += stepValue; |
|
|
|
} else { |
|
|
|
timer.cancel(); |
|
|
|
_isAnimationCompleted.value = true; |
|
|
|
_checkNavigateCondition(); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// 异步加载应用资源 |
|
|
|
Future<void> _loadAppResources() async { |
|
|
|
/// 异步验证Token和加载应用配置 |
|
|
|
Future<void> _validateTokenAsync() async { |
|
|
|
try { |
|
|
|
// 模拟资源加载(如API配置、本地缓存等) |
|
|
|
await Future.delayed(const Duration(milliseconds: 1500)); |
|
|
|
|
|
|
|
// 实际资源加载代码 |
|
|
|
// await _loadImages(); |
|
|
|
// await _initDatabase(); |
|
|
|
bool isLoggedIn = false; |
|
|
|
final token = _storage.read('logintoken'); |
|
|
|
Logger.d(_tag, 'Token验证开始: $token'); |
|
|
|
|
|
|
|
if (token != null && token.isNotEmpty) { |
|
|
|
isLoggedIn = await getLoginToken(); |
|
|
|
if (isLoggedIn) { |
|
|
|
await getAppConfig(); |
|
|
|
_isAppConfigLoaded.value = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_isResourceLoaded.value = true; |
|
|
|
_checkNavigateCondition(); |
|
|
|
_isLoggedIn.value = isLoggedIn; |
|
|
|
Logger.d(_tag, 'Token验证完成,登录状态: $isLoggedIn'); |
|
|
|
} catch (e) { |
|
|
|
Get.snackbar('错误', '资源加载失败,请检查网络连接'); |
|
|
|
_isResourceLoaded.value = true; // 允许用户继续使用 |
|
|
|
Logger.e(_tag, '资源加载错误: $e'); |
|
|
|
Logger.e(_tag, 'Token验证失败: $e'); |
|
|
|
_isLoggedIn.value = false; |
|
|
|
} finally { |
|
|
|
_isTokenValidated.value = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 检查跳转条件 |
|
|
|
void _checkNavigateCondition() async { |
|
|
|
if (_loadingProgress.value >= 1.0 && _isResourceLoaded.value) { |
|
|
|
// 关键改动:先执行页面跳转,并用 await 等待它完成 |
|
|
|
await _navigateToLogin(); |
|
|
|
|
|
|
|
// 页面跳转完全结束后,再安心地安排版本检查 |
|
|
|
_scheduleVersionCheck(); |
|
|
|
// 检查跳转条件:所有任务都完成后才跳转 |
|
|
|
void _checkNavigateCondition() { |
|
|
|
if (_isAnimationCompleted.value && |
|
|
|
_isTrackingPermissionRequested.value && |
|
|
|
_isVersionCheckCompleted.value && |
|
|
|
_isTokenValidated.value) { |
|
|
|
_navigateToTargetPage(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取登录Token并跳转 |
|
|
|
Future<void> _navigateToLogin() async { |
|
|
|
bool isLoggedIn = false; |
|
|
|
final token = _storage.read('logintoken'); |
|
|
|
Logger.d(_tag, 'getLoginToken:进来了${token}'); |
|
|
|
if (token != null && token.isNotEmpty) { |
|
|
|
isLoggedIn = await getLoginToken(); |
|
|
|
} |
|
|
|
if (isLoggedIn) { |
|
|
|
await getAppConfig(); |
|
|
|
// 根据登录状态跳转到目标页面 |
|
|
|
void _navigateToTargetPage() { |
|
|
|
if (_isLoggedIn.value && _isAppConfigLoaded.value) { |
|
|
|
Get.offAllNamed(Routes.pairing); |
|
|
|
} else { |
|
|
|
Get.offAllNamed(Routes.login); // 跳转到登录页 |
|
|
|
Get.offAllNamed(Routes.login); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -227,7 +240,7 @@ class SplashController extends GetxController { |
|
|
|
|
|
|
|
/// 混合检查机制:判断是否需要进行版本检查 |
|
|
|
/// 检查条件:1. 会话内未检查过 OR 2. 距离上次检查超过24小时 |
|
|
|
void _checkVersionUpdateIfNeeded() { |
|
|
|
Future<void> _checkVersionUpdateIfNeeded() async { |
|
|
|
try { |
|
|
|
final now = DateTime.now(); |
|
|
|
final lastCheckTimestamp = _storage.read(_lastVersionCheckKey); |
|
|
|
@ -259,7 +272,7 @@ class SplashController extends GetxController { |
|
|
|
|
|
|
|
if (shouldCheck) { |
|
|
|
Logger.i(_tag, '开始版本检查 - 原因: $reason'); |
|
|
|
_performVersionCheck(); |
|
|
|
await _performVersionCheck(); |
|
|
|
} else { |
|
|
|
Logger.i(_tag, '跳过版本检查 - 会话内已检查且未超过时间间隔'); |
|
|
|
} |
|
|
|
@ -278,9 +291,14 @@ class SplashController extends GetxController { |
|
|
|
Logger.i(_tag, '发现新版本: ${updateInfo.latestVersion}'); |
|
|
|
// 在主线程中显示更新对话框 |
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
|
|
VersionUpdateDialog.show(updateInfo); |
|
|
|
VersionUpdateDialog.show(updateInfo).then((_) { |
|
|
|
// 无论用户选择更新还是稍后提醒,都标记版本检查完成 |
|
|
|
_isVersionCheckCompleted.value = true; |
|
|
|
_checkNavigateCondition(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
_isVersionCheckCompleted.value = true; |
|
|
|
Logger.i(_tag, '当前已是最新版本: ${updateInfo.currentVersion}'); |
|
|
|
} |
|
|
|
|
|
|
|
@ -288,12 +306,16 @@ class SplashController extends GetxController { |
|
|
|
_updateVersionCheckRecord(); |
|
|
|
}).catchError((e) { |
|
|
|
Logger.e(_tag, '版本检查失败: $e'); |
|
|
|
// 即使检查失败,也更新记录,避免频繁重试 |
|
|
|
// 即使检查失败,也标记版本检查完成,让流程继续 |
|
|
|
_isVersionCheckCompleted.value = true; |
|
|
|
// 更新检查记录,避免频繁重试 |
|
|
|
_updateVersionCheckRecord(); |
|
|
|
}); |
|
|
|
} catch (e) { |
|
|
|
Logger.e(_tag, '版本检查失败: $e'); |
|
|
|
// 即使检查失败,也更新记录,避免频繁重试 |
|
|
|
// 即使检查失败,也标记版本检查完成,让流程继续 |
|
|
|
_isVersionCheckCompleted.value = true; |
|
|
|
// 更新检查记录,避免频繁重试 |
|
|
|
_updateVersionCheckRecord(); |
|
|
|
} |
|
|
|
} |
|
|
|
|