上次提交补充

This commit is contained in:
2026-04-29 14:29:49 +08:00
parent 19add5ad51
commit 9b918a90df
4 changed files with 285 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
class AppLocalizations {
final Locale locale;
Map<String, dynamic> _localizedStrings = {};
AppLocalizations(this.locale);
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
}
static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();
Future<bool> load() async {
try {
String jsonString = await rootBundle.loadString(
'assets/languages/${locale.languageCode}-${locale.countryCode}.json',
);
Map<String, dynamic> 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<String, dynamic>) {
value = value[k];
} else {
return key;
}
}
return value?.toString() ?? key;
}
}
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) {
return ['zh', 'en'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) async {
final localizations = AppLocalizations(locale);
await localizations.load();
return localizations;
}
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}

View File

@@ -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<PermissionRequestEvent, PermissionRequestState> {
final NetMessageDispatcher _dispatcher;
final RemoteControlRepository _repository;
final ILoggerService _logger = GetIt.I<ILoggerService>();
// 🔥 保存订阅引用,用于管理生命周期
StreamSubscription? _permissionSubscription;
PermissionRequestBloc(this._dispatcher, this._repository) : super(const PermissionRequestInitial()) {
// 🔥 核心:直接在构造函数中建立 0x12 监听(通过 NetMessageDispatcher)
_initPermissionListener();
// 事件处理
on<PermissionRequestReceived>(_handleRequestReceived);
on<PermissionDialogDismissed>(_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<void> _handleRequestReceived(
PermissionRequestReceived event,
Emitter<PermissionRequestState> 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<void> _handleDialogDismissed(
PermissionDialogDismissed event,
Emitter<PermissionRequestState> 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<void> 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();
}
}

View File

@@ -0,0 +1,34 @@
import 'package:equatable/equatable.dart';
abstract class PermissionRequestEvent extends Equatable {
const PermissionRequestEvent();
@override
List<Object?> get props => [];
}
class PermissionRequestReceived extends PermissionRequestEvent {
final String platform;
final String requestType;
const PermissionRequestReceived({
required this.platform,
required this.requestType,
});
@override
List<Object?> get props => [platform, requestType];
}
class PermissionDialogDismissed extends PermissionRequestEvent {
final bool agree;
final String deviceId;
const PermissionDialogDismissed({
required this.agree,
required this.deviceId,
});
@override
List<Object?> get props => [agree, deviceId];
}

View File

@@ -0,0 +1,29 @@
import 'package:equatable/equatable.dart';
abstract class PermissionRequestState extends Equatable {
const PermissionRequestState();
@override
List<Object?> 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<Object?> get props => [platform, requestType];
}
class PermissionRequestDialogHidden extends PermissionRequestState {
const PermissionRequestDialogHidden();
}