You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
11 KiB
357 lines
11 KiB
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
|
|
|
|
import '../../../data/models/appconfig.dart';
|
|
import '../../../data/models/appconfig_model.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
import '../../../data/models/user_Info.dart';
|
|
import '../../../routes/app_routes.dart';
|
|
import '../../../core/themes/theme_service.dart';
|
|
import '../../../data/services/network/api.dart';
|
|
import '../../../data/services/version_update_service.dart';
|
|
import '../../../modules/common/widgets/version_update_dialog.dart';
|
|
import '../../../modules/common/widgets/privacy_policy_dialog.dart';
|
|
import '../../../core/utils/logger.dart';
|
|
|
|
class SplashController extends GetxController {
|
|
final GetStorage _storage = GetStorage();
|
|
static const String _tag = 'SplashCtrl';
|
|
|
|
// 进度条动画控制
|
|
final _loadingProgress = 0.0.obs;
|
|
double get loadingProgress => _loadingProgress.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; //是否同意隐私政策
|
|
|
|
// 版本号
|
|
//final String version = 'V1.0.0';
|
|
|
|
// 主题服务
|
|
final ThemeService _themeService = ThemeService();
|
|
|
|
// 会话级别的版本检查标记(静态变量,整个应用会话期间保持)
|
|
static bool _hasCheckedVersionInSession = false;
|
|
|
|
// 版本检查相关常量
|
|
static const String _lastVersionCheckKey = 'last_version_check_timestamp';
|
|
static const int _versionCheckIntervalHours = 24; // 24小时间隔
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
|
|
// 首先检查隐私政策状态
|
|
final hasAcceptedPrivacy = _storage.read('hasAcceptedPrivacy') ?? false;
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (!hasAcceptedPrivacy) {
|
|
// 优先显示隐私政策弹窗,暂停其他初始化
|
|
_showPrivacyDialog();
|
|
} else {
|
|
// 已同意隐私政策,正常启动流程
|
|
_startNormalFlow();
|
|
}
|
|
});
|
|
|
|
// 注意:追踪权限请求将在用户同意隐私政策后执行
|
|
}
|
|
|
|
/// 异步请求追踪权限
|
|
Future<void> _requestTrackingPermissionAsync() async {
|
|
try {
|
|
await AppTrackingTransparency.requestTrackingAuthorization();
|
|
Logger.d(_tag, '用户追踪权限请求完成');
|
|
} catch (e) {
|
|
Logger.e(_tag, '用户追踪权限请求失败: $e');
|
|
} finally {
|
|
_isTrackingPermissionRequested.value = true;
|
|
}
|
|
}
|
|
|
|
void _startNormalFlow() {
|
|
_startLoadingAnimation();
|
|
_startParallelTasks();
|
|
}
|
|
|
|
/// 启动并行任务:在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() {
|
|
_storage.write('hasAcceptedPrivacy', true);
|
|
Get.back(); // 关闭弹窗
|
|
_startNormalFlow(); // 开始正常流程(已包含版本检查)
|
|
}
|
|
|
|
void _onPrivacyRejected() {
|
|
exit(0); // 退出应用
|
|
}
|
|
|
|
// 显示隐私政策弹窗
|
|
void _showPrivacyDialog() {
|
|
PrivacyPolicyDialog.show(
|
|
onAccept: () {
|
|
_onPrivacyAccepted();
|
|
_hasAcceptedPrivacy.value = true;
|
|
},
|
|
onReject: _onPrivacyRejected,
|
|
canDismiss: false, // 禁止通过返回键关闭弹窗
|
|
);
|
|
}
|
|
|
|
// 进度条动画(持续1.5秒)
|
|
void _startLoadingAnimation() {
|
|
const totalDuration = 1500; // 动画总时长1.5秒
|
|
const steps = totalDuration ~/ 30; // 30ms间隔,更平滑的动画
|
|
const stepValue = 1.0 / steps;
|
|
|
|
Timer.periodic(const Duration(milliseconds: 30), (timer) {
|
|
if (_loadingProgress.value < 1.0) {
|
|
_loadingProgress.value += stepValue;
|
|
} else {
|
|
timer.cancel();
|
|
_isAnimationCompleted.value = true;
|
|
_checkNavigateCondition();
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 异步验证Token和加载应用配置
|
|
Future<void> _validateTokenAsync() async {
|
|
try {
|
|
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;
|
|
}
|
|
}
|
|
|
|
_isLoggedIn.value = isLoggedIn;
|
|
Logger.d(_tag, 'Token验证完成,登录状态: $isLoggedIn');
|
|
} catch (e) {
|
|
Logger.e(_tag, 'Token验证失败: $e');
|
|
_isLoggedIn.value = false;
|
|
} finally {
|
|
_isTokenValidated.value = true;
|
|
}
|
|
}
|
|
|
|
// 检查跳转条件:所有任务都完成后才跳转
|
|
void _checkNavigateCondition() {
|
|
if (_isAnimationCompleted.value &&
|
|
_isTrackingPermissionRequested.value &&
|
|
_isVersionCheckCompleted.value &&
|
|
_isTokenValidated.value) {
|
|
_navigateToTargetPage();
|
|
}
|
|
}
|
|
|
|
// 根据登录状态跳转到目标页面
|
|
void _navigateToTargetPage() {
|
|
if (_isLoggedIn.value && _isAppConfigLoaded.value) {
|
|
Get.offAllNamed(Routes.pairing);
|
|
} else {
|
|
Get.offAllNamed(Routes.login);
|
|
}
|
|
}
|
|
|
|
Future<bool> getLoginToken() async {
|
|
try {
|
|
Logger.d(_tag, 'getLoginToken:进来了');
|
|
//if (User.getname() != "") {
|
|
final response = await Api.tokenlogin();
|
|
Logger.d(_tag, 'getLoginToken: $response');
|
|
if (response == null) return false;
|
|
|
|
if (response['user'] != '') {
|
|
Logger.d(_tag, 'getLoginToken:进来了');
|
|
// 根据API设计调整
|
|
final user = User.fromJson(response);
|
|
|
|
// 存储用户信息(示例)
|
|
await _storage.write('user_info', user.toJson());
|
|
return true;
|
|
}
|
|
//return true;
|
|
//}
|
|
return false;
|
|
} catch (e) {
|
|
Logger.e(_tag, 'Token获取失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> getAppConfig() async {
|
|
try {
|
|
Logger.d(_tag, 'getAppConfig:进来了');
|
|
//if (User.getname() != "") {
|
|
final response = await Api.getappconfig();
|
|
Logger.d(_tag, 'getAppConfig: $response');
|
|
if (response == null) return false;
|
|
// 根据API设计调整
|
|
final resp = UserGetAppConfigResp.fromJson(response);
|
|
Logger.d(_tag, 'getAppConfig:进来了');
|
|
// 存储用户信息(示例)
|
|
// _storage.write('ENV', resp.env);
|
|
// _storage.write('AGENTS', resp.agents);
|
|
// _storage.write('MCPS', resp.mcps);
|
|
AppConfig.initialize(resp);
|
|
return true;
|
|
} catch (e) {
|
|
Logger.e(_tag, 'getAppConfig 取失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 混合检查机制:判断是否需要进行版本检查
|
|
/// 检查条件:1. 会话内未检查过 OR 2. 距离上次检查超过24小时
|
|
Future<void> _checkVersionUpdateIfNeeded() async {
|
|
try {
|
|
final now = DateTime.now();
|
|
final lastCheckTimestamp = _storage.read(_lastVersionCheckKey);
|
|
|
|
bool shouldCheck = false;
|
|
String reason = '';
|
|
|
|
// 条件1:会话内未检查过
|
|
if (!_hasCheckedVersionInSession) {
|
|
shouldCheck = true;
|
|
reason = '会话内首次检查';
|
|
}
|
|
// 条件2:距离上次检查超过24小时
|
|
else if (lastCheckTimestamp != null) {
|
|
final lastCheckTime =
|
|
DateTime.fromMillisecondsSinceEpoch(lastCheckTimestamp);
|
|
final timeDifference = now.difference(lastCheckTime);
|
|
|
|
if (timeDifference.inHours >= _versionCheckIntervalHours) {
|
|
shouldCheck = true;
|
|
reason = '距离上次检查已超过${_versionCheckIntervalHours}小时';
|
|
}
|
|
}
|
|
// 条件3:从未检查过(首次使用应用)
|
|
else {
|
|
shouldCheck = true;
|
|
reason = '首次使用应用';
|
|
}
|
|
|
|
if (shouldCheck) {
|
|
Logger.i(_tag, '开始版本检查 - 原因: $reason');
|
|
await _performVersionCheck();
|
|
} else {
|
|
Logger.i(_tag, '跳过版本检查 - 会话内已检查且未超过时间间隔');
|
|
}
|
|
} catch (e) {
|
|
Logger.e(_tag, '版本检查判断失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 执行版本检查(在后台进行,不阻塞启动流程)
|
|
Future<void> _performVersionCheck() async {
|
|
try {
|
|
final versionService = VersionUpdateService.to;
|
|
// 异步执行,不等待结果
|
|
versionService.checkForUpdate().then((updateInfo) {
|
|
if (updateInfo.hasUpdate) {
|
|
Logger.i(_tag, '发现新版本: ${updateInfo.latestVersion}');
|
|
// 在主线程中显示更新对话框
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
VersionUpdateDialog.show(updateInfo).then((_) {
|
|
// 无论用户选择更新还是稍后提醒,都标记版本检查完成
|
|
_isVersionCheckCompleted.value = true;
|
|
_checkNavigateCondition();
|
|
});
|
|
});
|
|
} else {
|
|
_isVersionCheckCompleted.value = true;
|
|
Logger.i(_tag, '当前已是最新版本: ${updateInfo.currentVersion}');
|
|
}
|
|
|
|
// 更新检查记录
|
|
_updateVersionCheckRecord();
|
|
}).catchError((e) {
|
|
Logger.e(_tag, '版本检查失败: $e');
|
|
// 即使检查失败,也标记版本检查完成,让流程继续
|
|
_isVersionCheckCompleted.value = true;
|
|
// 更新检查记录,避免频繁重试
|
|
_updateVersionCheckRecord();
|
|
});
|
|
} catch (e) {
|
|
Logger.e(_tag, '版本检查失败: $e');
|
|
// 即使检查失败,也标记版本检查完成,让流程继续
|
|
_isVersionCheckCompleted.value = true;
|
|
// 更新检查记录,避免频繁重试
|
|
_updateVersionCheckRecord();
|
|
}
|
|
}
|
|
|
|
/// 更新版本检查记录
|
|
void _updateVersionCheckRecord() {
|
|
try {
|
|
// 更新时间戳
|
|
final now = DateTime.now();
|
|
_storage.write(_lastVersionCheckKey, now.millisecondsSinceEpoch);
|
|
|
|
// 标记本次会话已检查过版本
|
|
_hasCheckedVersionInSession = true;
|
|
|
|
Logger.i(_tag, '版本检查记录已更新: ${now.toIso8601String()}');
|
|
} catch (e) {
|
|
Logger.e(_tag, '更新版本检查记录失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 重置版本检查标记(可用于强制重新检查)
|
|
/// 注意:这会清除会话标记,但不会清除时间戳记录
|
|
static void resetVersionCheckFlag() {
|
|
_hasCheckedVersionInSession = false;
|
|
Logger.i('SplashCtrl', '版本检查会话标记已重置');
|
|
}
|
|
|
|
/// 完全重置版本检查状态(包括时间戳)
|
|
/// 用于测试或特殊情况下强制重新检查
|
|
void forceResetVersionCheck() {
|
|
try {
|
|
_hasCheckedVersionInSession = false;
|
|
_storage.remove(_lastVersionCheckKey);
|
|
Logger.i(_tag, '版本检查状态已完全重置');
|
|
} catch (e) {
|
|
Logger.e(_tag, '重置版本检查状态失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|