From 6051f850830989a186dfe8ee0502297f93fdc399 Mon Sep 17 00:00:00 2001 From: wuqifeng <540416539@qq.com> Date: Sun, 9 Jul 2023 00:27:04 +0800 Subject: [PATCH] feat:带隐私合规权限申请描述的权限申请页封装(独立的透明页) --- lib/pages/reading/bloc/reading_bloc.dart | 29 ++++++++++++++++++++--------- lib/pages/reading/permissionRequestPage.dart | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pages/reading/reading_page.dart | 2 +- 3 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 lib/pages/reading/permissionRequestPage.dart diff --git a/lib/pages/reading/bloc/reading_bloc.dart b/lib/pages/reading/bloc/reading_bloc.dart index 2aa4894..afcd10b 100644 --- a/lib/pages/reading/bloc/reading_bloc.dart +++ b/lib/pages/reading/bloc/reading_bloc.dart @@ -4,15 +4,16 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart'; import '../../../common/request/dao/listen_dao.dart'; import '../../../common/request/exception.dart'; import '../../../models/course_process_entity.dart'; import '../../../utils/loading.dart'; -import 'dart:convert'; import '../../../utils/log_util.dart'; +import '../permissionRequestPage.dart'; part 'reading_event.dart'; part 'reading_state.dart'; @@ -74,7 +75,9 @@ class ReadingPageBloc extends Bloc { late AudioPlayer audioPlayer; - ReadingPageBloc(this.pageController, this.courseLessonId) + final BuildContext context; + + ReadingPageBloc(this.context, this.pageController, this.courseLessonId) : super(ReadingPageInitial()) { on(_pageControllerChange); on(_playModeChange); @@ -112,7 +115,8 @@ class ReadingPageBloc extends Bloc { const MethodChannel('wow_english/sing_sound_method_channel'); methodChannel.invokeMethod('initVoiceSdk', {}); //初始化评测 methodChannel.setMethodCallHandler((call) async { - Log.d("setMethodCallHandler method=${call.method} arguments=${call.arguments}"); + Log.d( + "setMethodCallHandler method=${call.method} arguments=${call.arguments}"); if (call.method == 'voiceResult') { //评测结果 add(XSVoiceResultEvent(call.arguments)); @@ -209,7 +213,8 @@ class ReadingPageBloc extends Bloc { if (_isRecordAudioPlaying) { _isRecordAudioPlaying = false; } - Log.d("_playOriginalAudio _isRecordAudioPlaying=$_isRecordAudioPlaying _isOriginAudioPlaying=$_isOriginAudioPlaying url=$audioUrl"); + Log.d( + "_playOriginalAudio _isRecordAudioPlaying=$_isRecordAudioPlaying _isOriginAudioPlaying=$_isOriginAudioPlaying url=$audioUrl"); if (_isOriginAudioPlaying) { _isOriginAudioPlaying = false; await audioPlayer.stop(); @@ -230,7 +235,8 @@ class ReadingPageBloc extends Bloc { if (_isOriginAudioPlaying) { _isOriginAudioPlaying = false; } - Log.d("_playRecordAudioInner _isRecordAudioPlaying=$_isRecordAudioPlaying url=${currentPageData()?.recordUrl}"); + Log.d( + "_playRecordAudioInner _isRecordAudioPlaying=$_isRecordAudioPlaying url=${currentPageData()?.recordUrl}"); if (_isRecordAudioPlaying) { _isRecordAudioPlaying = false; await audioPlayer.stop(); @@ -282,11 +288,16 @@ class ReadingPageBloc extends Bloc { } void startRecord(String content) async { - if (_isRecording == true) { - return; + // 调用封装好的权限检查和请求方法 + bool result = await permissionCheckAndRequest( + context, + Permission.microphone, + "录音" + ); + if (result) { + methodChannel.invokeMethod( + 'startVoice', {'word': content, 'type': '0', 'userId': '1'}); } - methodChannel.invokeMethod( - 'startVoice', {'word': content, 'type': '0', 'userId': '1'}); } void _voiceXsResult( diff --git a/lib/pages/reading/permissionRequestPage.dart b/lib/pages/reading/permissionRequestPage.dart new file mode 100644 index 0000000..cffeb24 --- /dev/null +++ b/lib/pages/reading/permissionRequestPage.dart @@ -0,0 +1,189 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import '../../utils/log_util.dart'; + +/// 权限检查及请求 +/// 外部可通过此方法来进行权限的检查和请求,将自动跳转到`PermissionRequestPage`页面。 +/// 传入 `Permission` 以及对应的权限名称 `permissionTypeStr`,如果有权限则返回 `Future true` +/// `isRequiredPermission` 如果为 `true`,则 "取消" 按钮将执行 "退出app" 的操作 +Future permissionCheckAndRequest( + BuildContext context, + Permission permission, + String permissionTypeStr, + {bool isRequiredPermission = false}) async { + if (!await permission.status.isGranted) { + await Navigator.of(context).push(PageRouteBuilder( + opaque: true, + barrierColor: const Color.fromRGBO(255, 0, 0, 0.7), + pageBuilder: ((context, animation, secondaryAnimation) { + return PermissionRequestPage(permission, permissionTypeStr, + isRequiredPermission: isRequiredPermission); + }))); + } else { + return true; + } + return false; +} + +class PermissionRequestPage extends StatefulWidget { + const PermissionRequestPage(this.permission, this.permissionTypeStr, + {super.key, this.isRequiredPermission = false}); + + final Permission permission; + final String permissionTypeStr; + final bool isRequiredPermission; + + @override + State createState() => _PermissionRequestPageState(); +} + +class _PermissionRequestPageState extends State + with WidgetsBindingObserver { + bool _isGoSetting = false; + late final List msgList; + bool _isDialogShowing = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + msgList = [ + "${widget.permissionTypeStr}功能需要获取您设备的${widget.permissionTypeStr}权限,否则可能无法正常工作。\n是否申请${widget.permissionTypeStr}权限?", + "${widget.permissionTypeStr}权限不全,是否重新申请权限?", + "没有${widget.permissionTypeStr}权限,您可以手动开启权限", + widget.isRequiredPermission ? "退出应用" : "取消" + ]; + _checkPermission(widget.permission); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + Log.d("didChangeAppLifecycleState state=$state _isGoSetting=$_isGoSetting"); + // 监听 app 从后台切回前台 + if (state == AppLifecycleState.resumed && _isGoSetting) { + _checkPermission(widget.permission); + } + } + + /// 校验权限 + void _checkPermission(Permission permission) async { + final PermissionStatus status = await permission.status; + _handlePermissionStatus(permission, status); + } + + void _handlePermissionStatus(Permission permission, PermissionStatus status) { + Log.d('_handlePermissionStatus=$status permission=$permission'); + if (status.isGranted) { + _popPage(); + return; + } + + // 还未申请权限或之前拒绝了权限(在 iOS 上为首次申请权限,拒绝后将变为 `永久拒绝权限`) + if (status.isDenied) { + showAlert( + permission, msgList[0], msgList[3], _isGoSetting ? "前往系统设置" : "确定"); + } + // 权限已被永久拒绝 + if (status.isPermanentlyDenied) { + _isGoSetting = true; + showAlert( + permission, msgList[2], msgList[3], _isGoSetting ? "前往系统设置" : "确定"); + } + // 拥有部分权限 + if (status.isLimited) { + if (Platform.isIOS || Platform.isMacOS) _isGoSetting = true; + showAlert( + permission, msgList[1], msgList[3], _isGoSetting ? "前往系统设置" : "确定"); + } + // 拥有部分权限,活动限制(例如,设置了家长///控件,仅在iOS以上受支持。(仅限 iOS) + if (status.isRestricted) { + if (Platform.isIOS || Platform.isMacOS) _isGoSetting = true; + showAlert( + permission, msgList[1], msgList[3], _isGoSetting ? "前往系统设置" : "确定"); + } + } + + void showAlert(Permission permission, String message, String cancelMsg, + String confirmMsg) { + if (_isDialogShowing) { + // 对话框已经在显示中,不重复弹出 + Log.d("对话框已经在显示中,不重复弹出"); + return; + } + _isDialogShowing = true; + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text("温馨提示"), + content: Text(message), + actions: [ + TextButton( + child: Text(cancelMsg), + onPressed: () { + widget.isRequiredPermission + ? _quitApp() + : _popDialogAndPage(context); + }), + TextButton( + child: Text(confirmMsg), + onPressed: () { + if (_isGoSetting) { + openAppSettings(); + } else { + _requestPermisson(permission); + } + _popDialog(context); + }) + ], + ); + }).then((value) => { + _isDialogShowing = false, + }); + } + + /// 申请权限 + void _requestPermisson(Permission permission) async { + // 申请权限 + PermissionStatus status = await permission.request(); + Log.d('requestPermisson权限检测=$status _isGoSetting=$_isGoSetting'); + // 再次校验 + _handlePermissionStatus(permission, status); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container(); + } + + /// 退出应用程序 + void _quitApp() { + SystemChannels.platform.invokeMethod("SystemNavigator.pop"); + } + + /// 关闭整个权限申请页面 + void _popDialogAndPage(BuildContext dialogContext) { + _popDialog(dialogContext); + _popPage(); + } + + /// 关闭弹窗 + void _popDialog(BuildContext dialogContext) { + Navigator.of(dialogContext).pop(); + } + + /// 关闭透明页面 + void _popPage() { + Navigator.of(context).pop(); + } +} diff --git a/lib/pages/reading/reading_page.dart b/lib/pages/reading/reading_page.dart index d877c2d..62e509b 100644 --- a/lib/pages/reading/reading_page.dart +++ b/lib/pages/reading/reading_page.dart @@ -18,7 +18,7 @@ class ReadingPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => ReadingPageBloc(PageController(), courseLessonId ?? '') + create: (_) => ReadingPageBloc(context, PageController(), courseLessonId ?? '') ..add(InitBlocEvent()) ..add(RequestDataEvent()), child: _ReadingPage(), -- libgit2 0.22.2