用户开始录制,拒绝录音录像权限,增加弹窗引导用户前往系统页设置

This commit is contained in:
2026-06-05 16:11:54 +08:00
parent 4d83f38960
commit 54738d53f9
2 changed files with 69 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.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/app_platform_info.dart';
import 'package:recording_tool/core/platform/device_health_checker.dart'; import 'package:recording_tool/core/platform/device_health_checker.dart';
import 'package:recording_tool/features/dialog/dialog-record.dart'; import 'package:recording_tool/features/dialog/dialog-record.dart';
@@ -118,13 +119,43 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
); );
} }
/// 点击开始录制:校验剪贴板与健康状态 /// 根据缺失权限生成弹窗文案。
String _recordingPermissionDialogTitle(RecordingRequiredPermissions result) {
if (!result.cameraGranted && !result.microphoneGranted) {
return '录制需要开启相机和录音权限,请在系统设置中授权后重试';
}
if (!result.cameraGranted) {
return '录制需要开启相机权限,请在系统设置中授权后重试';
}
return '录制需要开启录音权限,请在系统设置中授权后重试';
}
/// 开始录制前检测相机、录音权限,未授予则弹窗并跳转系统设置。
Future<bool> _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<void> _onStartRecording() async { Future<void> _onStartRecording() async {
final recordingInfo = ref.read(recordingViewModelProvider); final recordingInfo = ref.read(recordingViewModelProvider);
if (!recordingInfo.hasClipboardFilename) { if (!recordingInfo.hasClipboardFilename) {
await _showNoPlayerInfoDialog(); await _showNoPlayerInfoDialog();
return; return;
} }
if (!await _ensureRecordingPermissions()) return;
if (!mounted) return;
await _checkAndShowDeviceHealthAlerts(); await _checkAndShowDeviceHealthAlerts();
if (!mounted) return; if (!mounted) return;
await ref.read(recordingViewModelProvider.notifier).startRecording(); await ref.read(recordingViewModelProvider.notifier).startRecording();

View File

@@ -31,6 +31,19 @@ enum ClipboardReadResult {
invalid, invalid,
} }
/// 开始录制所需的相机/麦克风权限检测结果。
class RecordingRequiredPermissions {
const RecordingRequiredPermissions({
required this.cameraGranted,
required this.microphoneGranted,
});
final bool cameraGranted;
final bool microphoneGranted;
bool get allGranted => cameraGranted && microphoneGranted;
}
/// 录制页 ViewModel剪贴板、权限、相机预览与录制流程。 /// 录制页 ViewModel剪贴板、权限、相机预览与录制流程。
class RecordingViewModel extends Notifier<RecordingModel> { class RecordingViewModel extends Notifier<RecordingModel> {
static final _defaultClipboard = ClipboardRecordingModel( static final _defaultClipboard = ClipboardRecordingModel(
@@ -253,6 +266,30 @@ class RecordingViewModel extends Notifier<RecordingModel> {
return _galleryPermissions().isEmpty; return _galleryPermissions().isEmpty;
} }
/// 检测并尝试申请相机、麦克风权限,同步更新 session 中的 isMicrophoneGranted。
Future<RecordingRequiredPermissions> 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<void> startRecording({bool enableDoNotDisturb = true}) async { Future<void> startRecording({bool enableDoNotDisturb = true}) async {
final session = state.session; final session = state.session;