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.

335 lines
10 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 _isResourceLoaded = false.obs;
bool get isResourceLoaded => _isResourceLoaded.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();
}
});
// 异步处理用户追踪授权,不阻塞主流程
_requestTrackingPermissionAsync();
}
/// 异步请求追踪权限,不阻塞启动流程
void _requestTrackingPermissionAsync() {
Future.delayed(const Duration(milliseconds: 500), () async {
try {
await AppTrackingTransparency.requestTrackingAuthorization();
Logger.d(_tag, '用户追踪权限请求完成');
} catch (e) {
Logger.e(_tag, '用户追踪权限请求失败: $e');
}
});
}
void _startNormalFlow() {
_startLoadingAnimation();
_loadAppResources();
}
/// 智能调度版本检查,在页面跳转后执行,完全不影响启动流程
void _scheduleVersionCheck() {
// 在独立的微任务中执行,避免阻塞主线程
scheduleMicrotask(() async {
try {
Logger.d(_tag, '开始执行版本检查(后台任务)');
_checkVersionUpdateIfNeeded();
} catch (e) {
Logger.e(_tag, '版本检查调度失败: $e');
}
});
}
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, // 禁止通过返回键关闭弹窗
);
}
// 进度条动画(持续2秒)
void _startLoadingAnimation() {
const totalDuration = 1500; // 动画总时长增加到1.5秒
const steps = totalDuration ~/ 30; // 30的间隔,更平滑的动画
const stepValue = 1.0 / steps;
Timer.periodic(const Duration(milliseconds: 30), (timer) {
if (_loadingProgress.value < 1.0) {
_loadingProgress.value += stepValue;
} else {
timer.cancel();
_checkNavigateCondition();
}
});
}
// 异步加载应用资源
Future<void> _loadAppResources() async {
try {
// 模拟资源加载(如API配置、本地缓存等)
await Future.delayed(const Duration(milliseconds: 1500));
// 实际资源加载代码
// await _loadImages();
// await _initDatabase();
_isResourceLoaded.value = true;
_checkNavigateCondition();
} catch (e) {
Get.snackbar('错误', '资源加载失败,请检查网络连接');
_isResourceLoaded.value = true; // 允许用户继续使用
Logger.e(_tag, '资源加载错误: $e');
}
}
// 检查跳转条件
void _checkNavigateCondition() async {
if (_loadingProgress.value >= 1.0 && _isResourceLoaded.value) {
// 关键改动:先执行页面跳转,并用 await 等待它完成
await _navigateToLogin();
// 页面跳转完全结束后,再安心地安排版本检查
_scheduleVersionCheck();
}
}
// 获取登录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();
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小时
void _checkVersionUpdateIfNeeded() {
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');
_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);
});
} else {
Logger.i(_tag, '当前已是最新版本: ${updateInfo.currentVersion}');
}
// 更新检查记录
_updateVersionCheckRecord();
}).catchError((e) {
Logger.e(_tag, '版本检查失败: $e');
// 即使检查失败,也更新记录,避免频繁重试
_updateVersionCheckRecord();
});
} catch (e) {
Logger.e(_tag, '版本检查失败: $e');
// 即使检查失败,也更新记录,避免频繁重试
_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');
}
}
}