diff --git a/lib/features/recording/view-model/view_model_recording.dart b/lib/features/recording/view-model/view_model_recording.dart index 4ce5608..d42faf0 100644 --- a/lib/features/recording/view-model/view_model_recording.dart +++ b/lib/features/recording/view-model/view_model_recording.dart @@ -13,6 +13,7 @@ import 'package:recording_tool/features/recording/model/model_recording_session. import 'package:recording_tool/features/recording/platform/recording_platform.dart'; import 'package:recording_tool/features/recording/utils/recording_display_name.dart'; +/// 录制页状态 Provider。 final recordingViewModelProvider = NotifierProvider( RecordingViewModel.new, @@ -30,6 +31,7 @@ enum ClipboardReadResult { invalid, } +/// 录制页 ViewModel:剪贴板、权限、相机预览与录制流程。 class RecordingViewModel extends Notifier { static final _defaultClipboard = ClipboardRecordingModel( title: '', @@ -38,19 +40,21 @@ class RecordingViewModel extends Notifier { StreamSubscription? _statusSubscription; + /// 初始化状态并注册销毁回调。 @override RecordingModel build() { ref.onDispose(_dispose); return RecordingModel(clipboardRecordingModel: _defaultClipboard); } + /// 局部更新 session 子状态。 void _updateSession( RecordingSessionState Function(RecordingSessionState session) update, ) { state = state.copyWith(session: update(state.session)); } - /// 从剪切板获取小程序复制的录制信息。 + /// 读取并解析剪贴板中的小程序录制信息。 Future getClipboardContent() async { try { final clipboardData = await Clipboard.getData(Clipboard.kTextPlain); @@ -94,10 +98,12 @@ class RecordingViewModel extends Notifier { } } + /// 清空剪贴板赛事信息(供 UI 调用)。 void resetClipboardInfo() { _resetClipboardInfo(); } + /// 重置剪贴板赛事信息为默认空值。 void _resetClipboardInfo() { state = state.copyWith( clipboardRecordingModel: _defaultClipboard, @@ -105,6 +111,7 @@ class RecordingViewModel extends Notifier { ); } + /// 申请权限、检查系统设置并初始化相机预览。 Future prepareSession() async { if (!RecordingPlatform.isSupported) { _updateSession((s) => s.copyWith(errorMessage: '当前设备不支持录制')); @@ -179,6 +186,7 @@ class RecordingViewModel extends Notifier { } } + /// 初始化相机预览,PlatformView 未就绪时自动重试。 Future _initializePreviewWithRetry() async { const maxAttempts = 8; for (var attempt = 0; attempt < maxAttempts; attempt++) { @@ -196,7 +204,7 @@ class RecordingViewModel extends Notifier { throw StateError('initializePreview retry exhausted'); } - /// 停止录制后重新绑定相机预览(重置 isPreviewReady 以显示加载遮罩)。 + /// 停止录制后重新绑定相机预览,并显示加载遮罩。 Future restorePreview() async { if (!RecordingPlatform.isSupported) return; @@ -222,6 +230,7 @@ class RecordingViewModel extends Notifier { } } + /// 当前平台所需的相册/视频保存权限列表。 List _galleryPermissions() { if (Platform.isIOS) { return [Permission.photosAddOnly, Permission.photos]; @@ -232,6 +241,7 @@ class RecordingViewModel extends Notifier { return const []; } + /// 判断相册相关权限是否至少有一项已授予。 bool _isGalleryPermissionGranted( Map permissions, ) { @@ -243,6 +253,7 @@ class RecordingViewModel extends Notifier { return _galleryPermissions().isEmpty; } + /// 开始录制,可选开启勿扰模式。 Future startRecording({bool enableDoNotDisturb = true}) async { final session = state.session; if (!session.isPreviewReady || @@ -282,6 +293,7 @@ class RecordingViewModel extends Notifier { } } + /// 停止录制、保存到相册,并恢复相机预览。 Future stopRecording() async { if (!state.session.isRecording) return; @@ -311,30 +323,37 @@ class RecordingViewModel extends Notifier { } } + /// 切换录制中触屏锁定状态。 void setTouchLocked(bool locked) { _updateSession((s) => s.copyWith(isTouchLocked: locked)); } + /// 清除上次保存成功的录制结果标记。 void clearSavedRecordingResult() { _updateSession((s) => s.copyWith(clearLastSaved: true)); } + /// 跳转系统勿扰/通知策略设置页。 Future openDndSettings() => RecordingPlatform.openNotificationPolicySettings(); + /// 重新检测勿扰模式权限并更新状态。 Future refreshDndAccess() async { final hasDnd = await RecordingPlatform.hasNotificationPolicyAccess(); _updateSession((s) => s.copyWith(hasDndAccess: hasDnd)); } + /// 跳转电池优化白名单设置页。 Future openBatterySettings() => RecordingPlatform.openBatteryOptimizationSettings(); + /// 重新检测是否已忽略电池优化并更新状态。 Future refreshBatteryOptimization() async { final ignored = await RecordingPlatform.isIgnoringBatteryOptimizations(); _updateSession((s) => s.copyWith(isBatteryOptimizedIgnored: ignored)); } + /// 退出录制页时释放相机、勿扰和状态订阅。 Future teardown() async { await RecordingPlatform.setImmersiveMode(enabled: false); await RecordingPlatform.disableDoNotDisturb(); @@ -344,6 +363,7 @@ class RecordingViewModel extends Notifier { state = state.copyWith(session: const RecordingSessionState()); } + /// 订阅原生层录制状态流并同步到 session。 Future _listenStatus() async { await _statusSubscription?.cancel(); _statusSubscription = RecordingPlatform.statusStream().listen((status) { @@ -351,6 +371,7 @@ class RecordingViewModel extends Notifier { }); } + /// Provider 销毁时取消状态流订阅。 Future _dispose() async { await _statusSubscription?.cancel(); }