From 9b918a90df787c8c1f5ee6401f05f20b31162220 Mon Sep 17 00:00:00 2001 From: Songzex <2402265378@qq.com> Date: Wed, 29 Apr 2026 14:29:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E6=AC=A1=E6=8F=90=E4=BA=A4=E8=A1=A5?= =?UTF-8?q?=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/localization/app_localizations.dart | 64 +++++++ .../bloc/permission_request_bloc.dart | 158 ++++++++++++++++++ .../bloc/permission_request_event.dart | 34 ++++ .../bloc/permission_request_state.dart | 29 ++++ 4 files changed, 285 insertions(+) create mode 100644 lib/core/localization/app_localizations.dart create mode 100644 lib/features/home/presentation/bloc/permission_request_bloc.dart create mode 100644 lib/features/home/presentation/bloc/permission_request_event.dart create mode 100644 lib/features/home/presentation/bloc/permission_request_state.dart diff --git a/lib/core/localization/app_localizations.dart b/lib/core/localization/app_localizations.dart new file mode 100644 index 00000000..e50d4ed7 --- /dev/null +++ b/lib/core/localization/app_localizations.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; + +class AppLocalizations { + final Locale locale; + Map _localizedStrings = {}; + + AppLocalizations(this.locale); + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; + } + + static const LocalizationsDelegate delegate = _AppLocalizationsDelegate(); + + Future load() async { + try { + String jsonString = await rootBundle.loadString( + 'assets/languages/${locale.languageCode}-${locale.countryCode}.json', + ); + Map jsonMap = json.decode(jsonString); + _localizedStrings = jsonMap; + return true; + } catch (e) { + debugPrint('❌ [AppLocalizations] 加载语言文件失败: $e'); + return false; + } + } + + String translate(String key) { + final keys = key.split('.'); + dynamic value = _localizedStrings; + + for (final k in keys) { + if (value is Map) { + value = value[k]; + } else { + return key; + } + } + + return value?.toString() ?? key; + } +} + +class _AppLocalizationsDelegate extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + bool isSupported(Locale locale) { + return ['zh', 'en'].contains(locale.languageCode); + } + + @override + Future load(Locale locale) async { + final localizations = AppLocalizations(locale); + await localizations.load(); + return localizations; + } + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} diff --git a/lib/features/home/presentation/bloc/permission_request_bloc.dart b/lib/features/home/presentation/bloc/permission_request_bloc.dart new file mode 100644 index 00000000..accd44bd --- /dev/null +++ b/lib/features/home/presentation/bloc/permission_request_bloc.dart @@ -0,0 +1,158 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:get_it/get_it.dart'; + +import '../../../../core/logging/i_logger_service.dart'; +import '../../../../core/network/net_message_dispatcher.dart'; +import '../../../remote_control/domain/repositories/remote_control_repository.dart'; +import 'permission_request_event.dart'; +import 'permission_request_state.dart'; + +class PermissionRequestBloc extends Bloc { + final NetMessageDispatcher _dispatcher; + final RemoteControlRepository _repository; + final ILoggerService _logger = GetIt.I(); + + // 🔥 保存订阅引用,用于管理生命周期 + StreamSubscription? _permissionSubscription; + + PermissionRequestBloc(this._dispatcher, this._repository) : super(const PermissionRequestInitial()) { + // 🔥 核心:直接在构造函数中建立 0x12 监听(通过 NetMessageDispatcher) + _initPermissionListener(); + + // 事件处理 + on(_handleRequestReceived); + on(_handleDialogDismissed); + } + + // 🔥 通过 NetMessageDispatcher 监听 TCP 0x12 指令,解析权限请求 + void _initPermissionListener() { + debugPrint('🔗 [PermissionRequestBloc] 初始化 0x12 权限监听器(通过 NetMessageDispatcher)'); + _logger.logWithLevel('🔗 [PermissionRequestBloc] 初始化 0x12 权限监听器', shouldLog: true); + + _permissionSubscription = _dispatcher.onCommand(0x12).listen((packet) { + + debugPrint('🔍 [PermissionRequestBloc] ✅✅✅ 0x12 包被监听到了!'); + _logger.logWithLevel('>>> [PermissionRequestBloc] ✅✅✅ 0x12 包被监听到了!', shouldLog: true); + + try { + _logger.logWithLevel('>>> [PermissionRequestBloc] ✅ 收到 0x12 原始包', shouldLog: true); + + // 🔥 关键:手动去掉最后 2 个 CRC 字节 + String jsonString; + if (packet.payload.length > 2) { + jsonString = utf8.decode(packet.payload.sublist(0, packet.payload.length - 2)); + } else { + jsonString = utf8.decode(packet.payload); + } + + debugPrint('>>> [PermissionRequestBloc] 收到 0x12: $jsonString'); + _logger.logWithLevel('>>> [PermissionRequestBloc] 收到 0x12: $jsonString', shouldLog: true); + + final jsonMap = jsonDecode(jsonString); + final requestType = jsonMap['request']; + final platform = jsonMap['platform']; + final respondData = jsonMap['respond']; + + _logger.logWithLevel('>>> [PermissionRequestBloc] 解析结果: requestType=$requestType, platform=$platform', shouldLog: true); + + // 情况 1: 响应格式(忽略) + if (respondData != null && respondData is Map) { + debugPrint('>>> [PermissionRequestBloc] 📊 收到响应格式,忽略'); + _logger.logWithLevel('>>> [PermissionRequestBloc] 📊 收到响应格式,忽略', shouldLog: true); + return; + } + + // 情况 2: 请求格式 - switch_control + if (requestType == 'switch_control') { + // 🔥 关键判断:只有当是其他平台(web)请求时才弹窗 + if (platform != null && platform.toString().toLowerCase() != 'app') { + debugPrint('>>> [PermissionRequestBloc] 🚨 $platform 端请求控制权'); + _logger.logWithLevel('>>> [PermissionRequestBloc] 🚨 $platform 端请求控制权', shouldLog: true); + + if (!isClosed) { + _logger.logWithLevel('>>> [PermissionRequestBloc] 📤 准备 add PermissionRequestReceived 事件', shouldLog: true); + add(PermissionRequestReceived( + platform: platform.toString(), + requestType: requestType, + )); + } else { + _logger.logWithLevel('>>> [PermissionRequestBloc] ⚠️ Bloc 已关闭,无法添加事件', shouldLog: true); + } + } else { + debugPrint('>>> [PermissionRequestBloc] ℹ️ APP 自己的请求回显,忽略'); + _logger.logWithLevel('>>> [PermissionRequestBloc] ℹ️ APP 自己的请求回显,忽略', shouldLog: true); + } + } + // 情况 3: 异地登录通知 + else if (requestType == 'have_logged_in') { + debugPrint('>>> [PermissionRequestBloc] ⚠️ 检测到异地登录'); + _logger.logWithLevel('>>> [PermissionRequestBloc] ⚠️ 检测到异地登录', shouldLog: true); + + if (!isClosed) { + _logger.logWithLevel('>>> [PermissionRequestBloc] 📤 准备 add PermissionRequestReceived 事件(异地登录)', shouldLog: true); + add(PermissionRequestReceived( + platform: 'unknown', + requestType: requestType, + )); + } + } + } catch (e) { + debugPrint('>>> [PermissionRequestBloc] ❌ 解析失败:$e'); + _logger.logWithLevel('>>> [PermissionRequestBloc] ❌ 解析失败:$e', shouldLog: true); + } + }); + + debugPrint('✅ [PermissionRequestBloc] 0x12 监听器已建立完成'); + _logger.logWithLevel('✅ [PermissionRequestBloc] 0x12 监听器已建立完成', shouldLog: true); + } + + Future _handleRequestReceived( + PermissionRequestReceived event, + Emitter emit, + ) async { + debugPrint('📤 [PermissionRequestBloc] emit DialogVisible - platform=${event.platform}'); + _logger.logWithLevel('📤 [PermissionRequestBloc] emit DialogVisible - platform=${event.platform}', shouldLog: true); + + emit(PermissionRequestDialogVisible( + platform: event.platform, + requestType: event.requestType, + )); + + _logger.logWithLevel('📤 [PermissionRequestBloc] ✅ 已 emit DialogVisible 状态', shouldLog: true); + } + + Future _handleDialogDismissed( + PermissionDialogDismissed event, + Emitter emit, + ) async { + debugPrint('📤 [PermissionRequestBloc] emit DialogHidden'); + _logger.logWithLevel('📤 [PermissionRequestBloc] emit DialogHidden'); + + // 🔥 发送响应到机器人(和远程遥控页面一样) + if (event.deviceId.isNotEmpty) { + debugPrint('📤 [PermissionRequestBloc] 发送权限响应: agree=${event.agree}, deviceId=${event.deviceId}'); + _repository.respondPermission(event.agree, event.deviceId); + } + + emit(const PermissionRequestDialogHidden()); + } + + @override + Future close() { + debugPrint('🚫 [PermissionRequestBloc] 关闭,取消订阅'); + _logger.logWithLevel('🚫 [PermissionRequestBloc] 关闭', shouldLog: true); + _permissionSubscription?.cancel(); + return super.close(); + } + + /// 🔥 公开方法:重新初始化监听器(TCP 重连后调用) + void reinitListener() { + debugPrint('🔄 [PermissionRequestBloc] 重新初始化监听器'); + _logger.logWithLevel('🔄 [PermissionRequestBloc] 重新初始化监听器', shouldLog: true); + _permissionSubscription?.cancel(); + _initPermissionListener(); + } +} diff --git a/lib/features/home/presentation/bloc/permission_request_event.dart b/lib/features/home/presentation/bloc/permission_request_event.dart new file mode 100644 index 00000000..87bf692a --- /dev/null +++ b/lib/features/home/presentation/bloc/permission_request_event.dart @@ -0,0 +1,34 @@ +import 'package:equatable/equatable.dart'; + +abstract class PermissionRequestEvent extends Equatable { + const PermissionRequestEvent(); + + @override + List get props => []; +} + +class PermissionRequestReceived extends PermissionRequestEvent { + final String platform; + final String requestType; + + const PermissionRequestReceived({ + required this.platform, + required this.requestType, + }); + + @override + List get props => [platform, requestType]; +} + +class PermissionDialogDismissed extends PermissionRequestEvent { + final bool agree; + final String deviceId; + + const PermissionDialogDismissed({ + required this.agree, + required this.deviceId, + }); + + @override + List get props => [agree, deviceId]; +} diff --git a/lib/features/home/presentation/bloc/permission_request_state.dart b/lib/features/home/presentation/bloc/permission_request_state.dart new file mode 100644 index 00000000..d7a56489 --- /dev/null +++ b/lib/features/home/presentation/bloc/permission_request_state.dart @@ -0,0 +1,29 @@ +import 'package:equatable/equatable.dart'; + +abstract class PermissionRequestState extends Equatable { + const PermissionRequestState(); + + @override + List get props => []; +} + +class PermissionRequestInitial extends PermissionRequestState { + const PermissionRequestInitial(); +} + +class PermissionRequestDialogVisible extends PermissionRequestState { + final String platform; + final String requestType; + + const PermissionRequestDialogVisible({ + required this.platform, + required this.requestType, + }); + + @override + List get props => [platform, requestType]; +} + +class PermissionRequestDialogHidden extends PermissionRequestState { + const PermissionRequestDialogHidden(); +}