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.
 
 
 
 
 
 

183 lines
6.2 KiB

import 'package:device_plugin_interface/device_plugin_interface.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../core/utils/logger.dart';
import '../../core/utils/version_compare.dart';
import '../../devices/device_hub.dart';
import '../../routes/app_routes.dart';
import '../models/appconfig.dart';
import '../models/appconfig_model.dart';
/// 连上设备后的固件升级检查(一次会话只提醒一次)——厂商无关。
///
/// 当前版本来自在线会话的 [DeviceInfo.firmwareVersion],最新版本来自
/// `user_getappconfig` 下发的产品配置(按设备名匹配)。
class OtaCheckService extends GetxService {
static const String _tag = 'OtaCheckService';
static bool _hasCheckedInCurrentSession = false;
static bool _isCheckingInProgress = false;
static OtaCheckService get to => Get.find<OtaCheckService>();
/// 重置检查状态(仅在应用重启时调用)
static void resetCheckStatus() {
_hasCheckedInCurrentSession = false;
_isCheckingInProgress = false;
}
bool shouldCheckOta() => !_hasCheckedInCurrentSession && !_isCheckingInProgress;
void _markAsChecked() {
_hasCheckedInCurrentSession = true;
_isCheckingInProgress = false;
}
bool _isCurrentlyOnHomePage() {
final currentRoute = Get.currentRoute;
return currentRoute == Routes.home || currentRoute == Routes.initial;
}
DBProduct? _findProduct(String name) {
if (!AppConfig.isInitialized()) return null;
final n = name.trim().toUpperCase();
if (n.isEmpty) return null;
for (final p in AppConfig.config.products) {
final pn = p.devicename.trim().toUpperCase();
if (pn.isEmpty) continue;
if (pn.contains(n) || n.contains(pn)) return p;
}
return null;
}
/// 执行一次检查。[isFullyConnected] 为 false 直接跳过。返回是否真的检查了。
Future<bool> performOtaCheck(bool isFullyConnected) async {
bool isForceUpdate = false;
try {
if (!shouldCheckOta()) return false;
if (!isFullyConnected) return false;
if (!_isCurrentlyOnHomePage()) return false;
final session = DeviceHub.to.sessionWith(DeviceCapability.ota);
if (session == null) return false;
_isCheckingInProgress = true;
final currentVersion = session.info.firmwareVersion ?? '';
final product = _findProduct(session.info.name);
final latestVersion = product?.version?.split('+').first.trim() ?? '';
Logger.d(_tag, '当前版本: $currentVersion, 最新版本: $latestVersion');
final hasUpdate = latestVersion.isNotEmpty &&
(currentVersion.isEmpty ||
VersionCompare.isNewer(latestVersion, currentVersion));
if (hasUpdate) {
isForceUpdate = product?.isforceupdate ?? false;
if (_isCurrentlyOnHomePage()) {
_showOtaUpgradeDialog(latestVersion);
}
} else {
Logger.d(_tag, '当前已是最新版本');
}
if (!isForceUpdate) _markAsChecked();
_isCheckingInProgress = false;
return true;
} catch (e) {
Logger.e(_tag, '检查OTA升级失败: $e');
if (!isForceUpdate) _markAsChecked();
_isCheckingInProgress = false;
return false;
}
}
void _showOtaUpgradeDialog(String latestVersion) {
if (!_isCurrentlyOnHomePage()) return;
final isDarkMode = Get.isDarkMode;
Get.dialog(
AlertDialog(
title: Row(
children: [
Icon(
Icons.headset,
color: isDarkMode ? const Color(0xFF0066CC) : Colors.blue[600],
size: 24.sp,
),
SizedBox(width: 8.w),
Flexible(
child: Text(
'ota_newVersionFound'.tr,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.blue[700],
fontWeight: FontWeight.w600,
fontSize: 20.sp,
),
softWrap: true,
maxLines: 2,
),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'latestFirmwareVersion'.tr}: $latestVersion',
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
fontWeight: FontWeight.w500,
fontSize: 16.sp,
),
),
],
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'talkLater'.tr,
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
),
),
ElevatedButton(
onPressed: () {
Get.back();
Get.toNamed(Routes.otaUpgrade);
},
style: ElevatedButton.styleFrom(
backgroundColor:
isDarkMode ? const Color(0xFF0066CC) : Colors.blue[600],
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.sp),
),
side: isDarkMode
? BorderSide(color: Colors.grey[800]!, width: 1)
: BorderSide.none,
),
child: Text('ota_updateNow'.tr, style: TextStyle(fontSize: 14.sp)),
),
],
),
],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.sp)),
backgroundColor: isDarkMode ? const Color(0xFF1A1A1A) : Colors.white,
),
barrierDismissible: true,
);
}
Map<String, dynamic> getCheckStatus() => {
'hasCheckedInCurrentSession': _hasCheckedInCurrentSession,
'isCheckingInProgress': _isCheckingInProgress,
'currentRoute': Get.currentRoute,
'isOnHomePage': _isCurrentlyOnHomePage(),
};
}