import 'package:flutter/widgets.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:pj_supermarket_cloud/common/services/face_recognition_session_service.dart'; /// 同步调度与顶栏共用的空闲状态快照,避免界面自行拼装支付、弹窗和重型任务规则。 class SyncIdleSnapshot { /// 创建一次不可变的终端空闲判断快照。 const SyncIdleSnapshot({ required this.foreground, required this.safeWindow, required this.idle, required this.inactiveFor, required this.reason, }); /// 应用当前是否处于 resumed 前台状态。 final bool foreground; /// 当前是否避开了支付遮罩、人脸会话、业务弹窗和其他重型任务。 final bool safeWindow; /// 当前是否同时满足安全窗口和连续 60 秒无人操作。 final bool idle; /// 距离最近一次指针操作或应用恢复前台经过的时长。 final Duration inactiveFor; /// 当前未达到空闲状态的首要中文原因;空闲时为空字符串。 final String reason; /// 返回顶栏使用的简短空闲状态文案。 String get label => idle ? '空闲' : '忙碌'; } /// 统一记录终端用户活动和重型后台任务,用于选择不会干扰支付、人脸和弹框的安全执行窗口。 class SyncIdleTool { /// 隐藏构造器,空闲判断必须通过全局单例维护同一份活动时间和任务计数。 SyncIdleTool._(); /// 应用级空闲检测单例。 static final SyncIdleTool instance = SyncIdleTool._(); /// 自动空闲任务要求的最短连续无人操作时间。 static const Duration idleDuration = Duration(seconds: 60); /// 最近一次用户指针操作或恢复前台的本地时间。 DateTime _lastActivityAt = DateTime.now(); /// 应用是否处于可安全执行终端任务的前台生命周期。 bool _foreground = true; /// 当前并行执行的重型同步任务数量,用计数支持嵌套开始和结束。 int _heavyTaskCount = 0; /// 记录一次终端触控活动并重新开始 60 秒空闲计时。 void recordUserActivity() { _lastActivityAt = DateTime.now(); } /// 根据应用生命周期更新前台状态,恢复前台时视为一次用户活动。 void updateLifecycle(AppLifecycleState state) { _foreground = state == AppLifecycleState.resumed; if (_foreground) recordUserActivity(); } /// 标记一个重型任务开始,阻止其他需要安全窗口的任务并发执行。 void beginHeavyTask() => _heavyTaskCount++; /// 标记一个重型任务结束;计数最低保持为零以容忍重复清理。 void endHeavyTask() { if (_heavyTaskCount > 0) _heavyTaskCount--; } /// 手动任务和后台拉取只等待业务安全窗口,不额外要求 60 秒无人操作。 SyncIdleSnapshot get snapshot { final faceActive = Get.isRegistered() && Get.find().isActive; final route = Get.context == null ? null : ModalRoute.of(Get.context!); final dialogOpen = Get.isDialogOpen == true || Get.isBottomSheetOpen == true || route?.isCurrent == false; final inactiveFor = DateTime.now().difference(_lastActivityAt); final reason = !_foreground ? '应用不在前台' : EasyLoading.isShow ? '前台操作进行中' : faceActive ? '人脸识别进行中' : dialogOpen ? '业务弹窗处理中' : _heavyTaskCount > 0 ? '后台同步进行中' : inactiveFor < idleDuration ? '最近有终端操作' : ''; final safeWindow = _foreground && !EasyLoading.isShow && !faceActive && !dialogOpen && _heavyTaskCount == 0; return SyncIdleSnapshot( foreground: _foreground, safeWindow: safeWindow, idle: safeWindow && inactiveFor >= idleDuration, inactiveFor: inactiveFor, reason: reason, ); } /// 当前是否满足手动执行和 MQTT 拉取所需的业务安全窗口。 bool get isSafeWindow => snapshot.safeWindow; /// 当前是否满足自动空闲任务的完整空闲条件。 bool get isIdle => snapshot.idle; }