From 54738d53f9b1edde5f3fed1cd5e05168402d2072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E9=94=8B?= <2535831261@qq.com> Date: Fri, 5 Jun 2026 16:11:54 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=BC=80=E5=A7=8B=E5=BD=95?= =?UTF-8?q?=E5=88=B6=EF=BC=8C=E6=8B=92=E7=BB=9D=E5=BD=95=E9=9F=B3=E5=BD=95?= =?UTF-8?q?=E5=83=8F=E6=9D=83=E9=99=90=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E5=BC=95=E5=AF=BC=E7=94=A8=E6=88=B7=E5=89=8D=E5=BE=80?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E9=A1=B5=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/features/recording/pages/page_record.dart | 33 ++++++++++++++++- .../view-model/view_model_recording.dart | 37 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/lib/features/recording/pages/page_record.dart b/lib/features/recording/pages/page_record.dart index c5e4916..c438ffa 100644 --- a/lib/features/recording/pages/page_record.dart +++ b/lib/features/recording/pages/page_record.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:recording_tool/core/platform/app_platform_info.dart'; import 'package:recording_tool/core/platform/device_health_checker.dart'; import 'package:recording_tool/features/dialog/dialog-record.dart'; @@ -118,13 +119,43 @@ class _RecordingPageState extends ConsumerState { ); } - /// 点击开始录制:校验剪贴板与健康状态 + /// 根据缺失权限生成弹窗文案。 + String _recordingPermissionDialogTitle(RecordingRequiredPermissions result) { + if (!result.cameraGranted && !result.microphoneGranted) { + return '录制需要开启相机和录音权限,请在系统设置中授权后重试'; + } + if (!result.cameraGranted) { + return '录制需要开启相机权限,请在系统设置中授权后重试'; + } + return '录制需要开启录音权限,请在系统设置中授权后重试'; + } + + /// 开始录制前检测相机、录音权限,未授予则弹窗并跳转系统设置。 + Future _ensureRecordingPermissions() async { + final result = await ref + .read(recordingViewModelProvider.notifier) + .ensureCameraAndMicrophonePermissions(); + if (result.allGranted) return true; + if (!mounted) return false; + + await RecordDialog.showSingle( + context, + title: _recordingPermissionDialogTitle(result), + buttonText: '确定', + onPressed: openAppSettings, + ); + return false; + } + + /// 点击开始录制:校验剪贴板、权限与健康状态 Future _onStartRecording() async { final recordingInfo = ref.read(recordingViewModelProvider); if (!recordingInfo.hasClipboardFilename) { await _showNoPlayerInfoDialog(); return; } + if (!await _ensureRecordingPermissions()) return; + if (!mounted) return; await _checkAndShowDeviceHealthAlerts(); if (!mounted) return; await ref.read(recordingViewModelProvider.notifier).startRecording(); diff --git a/lib/features/recording/view-model/view_model_recording.dart b/lib/features/recording/view-model/view_model_recording.dart index d42faf0..01429c3 100644 --- a/lib/features/recording/view-model/view_model_recording.dart +++ b/lib/features/recording/view-model/view_model_recording.dart @@ -31,6 +31,19 @@ enum ClipboardReadResult { invalid, } +/// 开始录制所需的相机/麦克风权限检测结果。 +class RecordingRequiredPermissions { + const RecordingRequiredPermissions({ + required this.cameraGranted, + required this.microphoneGranted, + }); + + final bool cameraGranted; + final bool microphoneGranted; + + bool get allGranted => cameraGranted && microphoneGranted; +} + /// 录制页 ViewModel:剪贴板、权限、相机预览与录制流程。 class RecordingViewModel extends Notifier { static final _defaultClipboard = ClipboardRecordingModel( @@ -253,6 +266,30 @@ class RecordingViewModel extends Notifier { return _galleryPermissions().isEmpty; } + /// 检测并尝试申请相机、麦克风权限,同步更新 session 中的 isMicrophoneGranted。 + Future ensureCameraAndMicrophonePermissions() async { + final permissions = await PermissionService.requestMissing([ + Permission.camera, + Permission.microphone, + ]); + + final cameraGranted = _isPermissionGranted(permissions[Permission.camera]); + final microphoneGranted = _isPermissionGranted( + permissions[Permission.microphone], + ); + + _updateSession((s) => s.copyWith(isMicrophoneGranted: microphoneGranted)); + + return RecordingRequiredPermissions( + cameraGranted: cameraGranted, + microphoneGranted: microphoneGranted, + ); + } + + bool _isPermissionGranted(PermissionStatus? status) { + return status?.isGranted == true || status?.isLimited == true; + } + /// 开始录制,可选开启勿扰模式。 Future startRecording({bool enableDoNotDisturb = true}) async { final session = state.session;