import 'package:flutter/services.dart'; /// 当前 Android 设备的运行环境。 class AppRuntimeEnvironment { final bool isEmulator; final int displayCount; const AppRuntimeEnvironment({ required this.isEmulator, required this.displayCount, }); factory AppRuntimeEnvironment.fromMap(Map? data) { return AppRuntimeEnvironment( isEmulator: data?['isEmulator'] == true, displayCount: (data?['displayCount'] as num?)?.toInt() ?? 1, ); } } /// Android 专用设备模式的实时状态。 class AppKioskStatus { final bool deviceAdminActive; final bool deviceOwner; final bool available; final bool active; final bool lockTaskPermitted; final bool autoEnterEnabled; final bool bootAutoStartSupported; const AppKioskStatus({ required this.deviceAdminActive, required this.deviceOwner, required this.available, required this.active, required this.lockTaskPermitted, required this.autoEnterEnabled, required this.bootAutoStartSupported, }); const AppKioskStatus.unavailable() : deviceAdminActive = false, deviceOwner = false, available = false, active = false, lockTaskPermitted = false, autoEnterEnabled = false, bootAutoStartSupported = false; factory AppKioskStatus.fromMap(Map? data) { return AppKioskStatus( deviceAdminActive: data?['deviceAdminActive'] == true, deviceOwner: data?['deviceOwner'] == true, available: data?['available'] == true, active: data?['active'] == true, lockTaskPermitted: data?['lockTaskPermitted'] == true, autoEnterEnabled: data?['autoEnterEnabled'] == true, bootAutoStartSupported: data?['bootAutoStartSupported'] == true, ); } bool get canExitToAndroidHome => available && active; String get statusLabel { if (!available) return '暂不可实现'; if (active && !autoEnterEnabled) return 'Kiosk 运行中 · 自动开启已关闭'; if (active) return '可以实现 · Kiosk 运行中'; if (!autoEnterEnabled) return '可以实现 · 自动开启已关闭'; if (lockTaskPermitted) return '可以实现 · 正在进入'; return '可以实现 · 等待策略生效'; } String get description { if (available && active && !autoEnterEnabled) { return '当前仍在 Kiosk;管理员退出后,再次打开或回到 App 将不再自动锁定。'; } if (available && !autoEnterEnabled) { return '设备具备 Device Owner 权限;开启右侧配置后,每次打开或回到 App 都会自动进入 Kiosk。'; } if (available) { return '已配置 Device Owner;每次打开或回到 App 都会自动进入 Kiosk,并隐藏主页、最近任务和系统栏。'; } if (deviceAdminActive) { return '当前仅激活了设备管理员,仍不能可靠禁用主页和最近任务;需要配置为 Device Owner。'; } return '未配置 Device Owner,普通全屏不能保证无法退出;需在恢复出厂或未初始化的设备上部署。'; } } class AppNetworkSnapshot { final bool connected; final String type; final int receivedBytes; final int transmittedBytes; const AppNetworkSnapshot({ required this.connected, required this.type, required this.receivedBytes, required this.transmittedBytes, }); factory AppNetworkSnapshot.fromMap(Map? data) { return AppNetworkSnapshot( connected: data?['connected'] == true, type: data?['type']?.toString() ?? 'offline', receivedBytes: (data?['rxBytes'] as num?)?.toInt() ?? 0, transmittedBytes: (data?['txBytes'] as num?)?.toInt() ?? 0, ); } } /// 统一管理虚拟机识别和硬件手动输入模式,避免各业务页面自行猜测设备能力。 abstract class AppRuntimeTool { static const MethodChannel _channel = MethodChannel( 'biz_xfj/runtime_environment', ); static AppRuntimeEnvironment _environment = const AppRuntimeEnvironment( isEmulator: false, displayCount: 1, ); static bool _initialized = false; static bool _manualHardwareInput = false; static AppKioskStatus _kioskStatus = const AppKioskStatus.unavailable(); static bool get isEmulator => _environment.isEmulator; static int get displayCount => _environment.displayCount; /// 虚拟机始终使用手动输入;真机可在设置中主动开启手动输入模式。 static bool get useManualHardwareInput => isEmulator || _manualHardwareInput; static bool get canUsePhysicalHardware => !useManualHardwareInput; static bool get hasMultipleDisplays => displayCount >= 2; static AppKioskStatus get kioskStatus => _kioskStatus; static Future initializePlatform() async { if (_initialized) return; try { final data = await _channel.invokeMapMethod( 'getRuntimeEnvironment', ); _environment = AppRuntimeEnvironment.fromMap(data); _kioskStatus = await refreshKioskStatus(); } on PlatformException { // 原生通道异常时按单屏处理,避免错误地启动副屏。 _environment = const AppRuntimeEnvironment( isEmulator: false, displayCount: 1, ); _kioskStatus = const AppKioskStatus.unavailable(); } _initialized = true; } static Future refreshKioskStatus() async { try { final data = await _channel.invokeMapMethod( 'getKioskStatus', ); _kioskStatus = AppKioskStatus.fromMap(data); } on PlatformException { _kioskStatus = const AppKioskStatus.unavailable(); } return _kioskStatus; } /// 配置每次进入 App 时是否自动恢复 Kiosk,配置由 Android 原生层持久化。 static Future setKioskAutoEnterEnabled(bool enabled) async { try { final data = await _channel.invokeMapMethod( 'setKioskAutoEnterEnabled', {'enabled': enabled}, ); _kioskStatus = AppKioskStatus.fromMap(data); } on PlatformException { // 保留原状态,设置页会据此判断本次配置没有生效。 } return _kioskStatus; } /// 只有原生层确认 Device Owner/Kiosk 可用时才允许退出到系统桌面。 static Future exitKioskToAndroidHome() async { if (!_kioskStatus.canExitToAndroidHome) return false; try { return await _channel.invokeMethod('exitKioskToHome') ?? false; } on PlatformException { return false; } } static Future getNetworkSnapshot() async { try { final data = await _channel.invokeMapMethod( 'getNetworkSnapshot', ); return AppNetworkSnapshot.fromMap(data); } on PlatformException { return const AppNetworkSnapshot( connected: false, type: 'offline', receivedBytes: 0, transmittedBytes: 0, ); } } static void applyManualHardwareInput(bool enabled) { _manualHardwareInput = enabled; } }