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.
373 lines
11 KiB
373 lines
11 KiB
import 'package:get/get.dart';
|
|
import 'dart:async';
|
|
import '../../../routes/app_routes.dart';
|
|
import '../../../core/utils/logger.dart';
|
|
import 'package:flutter/material.dart';
|
|
import '../../../data/services/bluetooth_manager.dart';
|
|
import '../../../data/services/ble_manager.dart';
|
|
import '../../../data/services/version_update_service.dart';
|
|
import '../../common/widgets/version_update_dialog.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
|
|
/// 连接状态枚举
|
|
enum ConnectionStatus {
|
|
disconnected, // 未连接
|
|
connecting, // 连接中
|
|
connected, // 已连接
|
|
failed // 连接失败
|
|
}
|
|
|
|
/// 主页控制器,管理设备连接状态和信息展示
|
|
class HomeController extends GetxController {
|
|
static const String _tag = 'HomeCtrl';
|
|
static HomeController get to => Get.find();
|
|
|
|
final _isFullyConnected = false.obs;
|
|
String? _lastConnectedDeviceName;
|
|
|
|
bool get isFullyConnected => _isFullyConnected.value;
|
|
RxBool get isFullyConnectedRx => _isFullyConnected;
|
|
BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice;
|
|
Rx<DeviceInfo?> get deviceInfo => _bleManager.deviceInfo;
|
|
|
|
final leftEarBattery = 0.obs;
|
|
final rightEarBattery = 0.obs;
|
|
final caseBattery = 0.obs;
|
|
final leftEarCharging = false.obs;
|
|
final rightEarCharging = false.obs;
|
|
final caseCharging = false.obs;
|
|
final firmwareVersion = ''.obs;
|
|
void updateDeviceInfo(DeviceInfo? newDeviceInfo) {
|
|
leftEarBattery.value = newDeviceInfo?.leftEarBattery ?? 0;
|
|
rightEarBattery.value = newDeviceInfo?.rightEarBattery ?? 0;
|
|
caseBattery.value = newDeviceInfo?.caseBattery ?? 0;
|
|
leftEarCharging.value = newDeviceInfo?.leftEarCharging ?? false;
|
|
rightEarCharging.value = newDeviceInfo?.rightEarCharging ?? false;
|
|
caseCharging.value = newDeviceInfo?.caseCharging ?? false;
|
|
firmwareVersion.value = newDeviceInfo?.firmwareVersion ?? '';
|
|
}
|
|
|
|
Worker? _deviceInfoWorker;
|
|
Worker? _classicBluetoothWorker;
|
|
Worker? _bleConnectionWorker;
|
|
|
|
final _bluetoothManager = Get.find<BluetoothManager>();
|
|
final _bleManager = Get.find<BleManager>();
|
|
final _storage = GetStorage();
|
|
|
|
// 会话级别的版本检查标记(静态变量,整个应用会话期间保持)
|
|
static bool _hasCheckedVersionInSession = false;
|
|
|
|
// 版本检查相关常量
|
|
static const String _lastVersionCheckKey = 'last_version_check_timestamp';
|
|
static const int _versionCheckIntervalHours = 24; // 24小时间隔
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_initWithCachedInfo();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_deviceInfoWorker?.dispose();
|
|
_classicBluetoothWorker?.dispose();
|
|
_bleConnectionWorker?.dispose();
|
|
super.onClose();
|
|
}
|
|
|
|
void _initWithCachedInfo() {
|
|
_setupStateListeners();
|
|
_checkConnectionStatus();
|
|
|
|
// 延迟检查版本更新,避免影响页面加载
|
|
// 混合检查机制:会话检查 + 24小时间隔检查
|
|
Future.delayed(const Duration(seconds: 2), () {
|
|
_checkVersionUpdateIfNeeded();
|
|
});
|
|
}
|
|
|
|
void _setupStateListeners() {
|
|
_deviceInfoWorker = ever(_bleManager.deviceInfo, (DeviceInfo? info) {
|
|
if (info != null) {
|
|
updateDeviceInfo(info);
|
|
}
|
|
update();
|
|
});
|
|
|
|
_classicBluetoothWorker = ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) {
|
|
if (device != null) {
|
|
_lastConnectedDeviceName = device.name;
|
|
}
|
|
_checkConnectionStatus();
|
|
});
|
|
|
|
_bleConnectionWorker = ever(_bleManager.connectionStateRx, (int state) {
|
|
_checkConnectionStatus();
|
|
});
|
|
}
|
|
|
|
Future<void> refreshDeviceConnection() async {
|
|
await _checkConnectionStatus();
|
|
}
|
|
|
|
void rescanDevices() {
|
|
Get.toNamed(Routes.pairing);
|
|
}
|
|
|
|
void openTranslation() {
|
|
Get.toNamed(Routes.translation);
|
|
}
|
|
|
|
void exploreAIFeatures() {
|
|
Get.toNamed(Routes.agent);
|
|
}
|
|
|
|
Future<void> _checkConnectionStatus() async {
|
|
try {
|
|
final currentBleState = await _bleManager.getConnectionState();
|
|
|
|
bool hasClassicBluetooth = currentDevice != null;
|
|
bool hasBleConnection = currentBleState == 2;
|
|
bool wasConnected = _isFullyConnected.value;
|
|
bool isNowConnected = hasClassicBluetooth && hasBleConnection;
|
|
|
|
if (isNowConnected && currentDevice == null) {
|
|
isNowConnected = false;
|
|
}
|
|
|
|
if (_isFullyConnected.value != isNowConnected) {
|
|
_isFullyConnected.value = isNowConnected;
|
|
_isFullyConnected.refresh();
|
|
update();
|
|
|
|
if (!wasConnected && isNowConnected) {
|
|
await _bleManager.fetchDeviceInfo();
|
|
}
|
|
}
|
|
|
|
update();
|
|
} catch (e) {
|
|
Logger.e(_tag, '${'checkConnectionStatusFailed'.tr}: $e');
|
|
if (_isFullyConnected.value) {
|
|
_isFullyConnected.value = false;
|
|
_isFullyConnected.refresh();
|
|
update();
|
|
}
|
|
}
|
|
}
|
|
|
|
Color getConnectionStatusColor() {
|
|
return _isFullyConnected.value ? Colors.green : Colors.grey;
|
|
}
|
|
|
|
String getConnectionStatusText() {
|
|
return _isFullyConnected.value ? 'connected'.tr : 'unconnected'.tr;
|
|
}
|
|
|
|
bool get shouldShowDeviceInfo => currentDevice != null;
|
|
|
|
String getDeviceName() {
|
|
if (_isFullyConnected.value && currentDevice == null) {
|
|
_checkConnectionStatus();
|
|
}
|
|
|
|
final deviceName = currentDevice?.name;
|
|
if (deviceName != null && deviceName.isNotEmpty) {
|
|
return deviceName;
|
|
}
|
|
|
|
final bleDeviceName = deviceInfo.value?.name;
|
|
if (bleDeviceName != null && bleDeviceName.isNotEmpty) {
|
|
return bleDeviceName;
|
|
}
|
|
|
|
if (!_isFullyConnected.value && _lastConnectedDeviceName != null && _lastConnectedDeviceName!.isNotEmpty) {
|
|
return _lastConnectedDeviceName!;
|
|
}
|
|
|
|
return 'unknownEquipment'.tr;
|
|
}
|
|
|
|
String getSignalStrength() {
|
|
if (!_isFullyConnected.value) {
|
|
return 'zeroSignal'.tr;
|
|
}
|
|
|
|
final random = DateTime.now().millisecondsSinceEpoch % 3;
|
|
switch (random) {
|
|
case 0:
|
|
return 'good'.tr;
|
|
case 1:
|
|
return 'fine'.tr;
|
|
default:
|
|
return 'excellent'.tr;
|
|
}
|
|
}
|
|
|
|
String getDeviceId() {
|
|
return deviceInfo.value?.address ?? currentDevice?.address ?? 'unknown'.tr;
|
|
}
|
|
|
|
String getBatteryText() {
|
|
return deviceInfo.value?.batteryLevel != null &&
|
|
deviceInfo.value!.batteryLevel > 0
|
|
? '${deviceInfo.value?.batteryLevel}%'
|
|
: 'unknown'.tr;
|
|
}
|
|
|
|
String getEarbudsText() {
|
|
if (deviceInfo.value == null ||
|
|
(deviceInfo.value!.leftEarBattery <= 0 &&
|
|
deviceInfo.value!.rightEarBattery <= 0 ||
|
|
deviceInfo.value!.caseBattery > 100)) {
|
|
return 'unknown'.tr;
|
|
}
|
|
|
|
String leftStatus = deviceInfo.value!.leftEarBattery > 0
|
|
? '${deviceInfo.value!.leftEarBattery}%'
|
|
: 'N/A';
|
|
String rightStatus = deviceInfo.value!.rightEarBattery > 0
|
|
? '${deviceInfo.value!.rightEarBattery}%'
|
|
: 'N/A';
|
|
|
|
leftStatus =
|
|
deviceInfo.value!.leftEarCharging ? '$leftStatus ⚡' : leftStatus;
|
|
rightStatus =
|
|
deviceInfo.value!.rightEarCharging ? '$rightStatus ⚡' : rightStatus;
|
|
|
|
return '${'leftEar'.tr}: $leftStatus / ${'rightEar'.tr}: $rightStatus'; // 左耳: xxx / 右耳: xxx
|
|
}
|
|
|
|
String getCaseText() {
|
|
if (deviceInfo.value == null ||
|
|
deviceInfo.value!.caseBattery <= 0 ||
|
|
deviceInfo.value!.caseBattery > 100) {
|
|
return 'unknown'.tr;
|
|
}
|
|
|
|
String caseStatus = '${deviceInfo.value!.caseBattery}%';
|
|
if (deviceInfo.value!.caseCharging) {
|
|
caseStatus = '$caseStatus ⚡';
|
|
}
|
|
|
|
return '${'chargingCase'.tr}: $caseStatus'; // 充电仓: xxx
|
|
}
|
|
|
|
String getChargingText() {
|
|
if (deviceInfo.value == null) {
|
|
return 'unknown'.tr;
|
|
}
|
|
|
|
bool isCharging = deviceInfo.value!.leftEarCharging ||
|
|
deviceInfo.value!.rightEarCharging ||
|
|
deviceInfo.value!.caseCharging;
|
|
|
|
return isCharging ? 'charging'.tr : 'notCharging'.tr;
|
|
}
|
|
|
|
String getVersionText() {
|
|
return deviceInfo.value?.firmwareVersion.isNotEmpty == true
|
|
? deviceInfo.value!.firmwareVersion
|
|
: 'unknown'.tr;
|
|
}
|
|
|
|
/// 混合检查机制:判断是否需要进行版本检查
|
|
/// 检查条件: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;
|
|
final updateInfo = await versionService.checkForUpdate();
|
|
|
|
if (updateInfo.hasUpdate) {
|
|
Logger.i(_tag, '发现新版本: ${updateInfo.latestVersion}');
|
|
// 显示更新对话框
|
|
await VersionUpdateDialog.show(updateInfo);
|
|
} else {
|
|
Logger.i(_tag, '当前已是最新版本: ${updateInfo.currentVersion}');
|
|
}
|
|
|
|
// 更新检查记录
|
|
_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('HomeCtrl', '版本检查会话标记已重置');
|
|
}
|
|
|
|
/// 完全重置版本检查状态(包括时间戳)
|
|
/// 用于测试或特殊情况下强制重新检查
|
|
void forceResetVersionCheck() {
|
|
try {
|
|
_hasCheckedVersionInSession = false;
|
|
_storage.remove(_lastVersionCheckKey);
|
|
Logger.i(_tag, '版本检查状态已完全重置');
|
|
} catch (e) {
|
|
Logger.e(_tag, '重置版本检查状态失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|