From 8f08223a188f4f5d34e3ca33a3c6cb5e5361eb21 Mon Sep 17 00:00:00 2001 From: fuenmao <980740792@qq.com> Date: Mon, 30 Dec 2024 14:56:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9D=83=E9=99=90=E5=92=8Capp=E5=86=85?= =?UTF-8?q?=E5=89=8D=E5=8F=B0=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/app/src/main/AndroidManifest.xml | 65 +- .../app/src/main/res/xml/file_path.xml | 11 + wgshare/lib/pages/homePage/home_logic.dart | 45 +- wgshare/lib/pages/homePage/home_view.dart | 7 +- wgshare/lib/pages/loginPage/login_logic.dart | 11 +- wgshare/lib/pages/loginPage/login_view.dart | 2 +- .../lib/pages/metting/meeting_main_logic.dart | 4 +- .../lib/pages/metting/meeting_main_state.dart | 3 + .../lib/pages/metting/meeting_main_view.dart | 1224 +++++++++-------- .../permission/AndroidPermissionHandler.dart | 25 +- .../permission/IosPermissionHandler.dart | 23 +- .../utils/permission/PermissionService.dart | 35 +- wgshare/lib/view/public_dialog.dart | 175 +++ .../loadJson/entity/upgrade_entity.dart | 57 + .../upgrade/loadJson/load_network_json.dart | 42 + wgshare/lib/view/upgrade/upgrade_dialog.dart | 302 ++++ .../util/load_file_cancel_request.dart | 36 + wgshare/pubspec.lock | 10 +- wgshare/pubspec.yaml | 6 + 19 files changed, 1433 insertions(+), 650 deletions(-) create mode 100644 wgshare/android/app/src/main/res/xml/file_path.xml create mode 100644 wgshare/lib/view/public_dialog.dart create mode 100644 wgshare/lib/view/upgrade/loadJson/entity/upgrade_entity.dart create mode 100644 wgshare/lib/view/upgrade/loadJson/load_network_json.dart create mode 100644 wgshare/lib/view/upgrade/upgrade_dialog.dart create mode 100644 wgshare/lib/view/upgrade/util/load_file_cancel_request.dart diff --git a/wgshare/android/app/src/main/AndroidManifest.xml b/wgshare/android/app/src/main/AndroidManifest.xml index 095a9e0..dd7af8c 100644 --- a/wgshare/android/app/src/main/AndroidManifest.xml +++ b/wgshare/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,21 @@ + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/wgshare/android/app/src/main/res/xml/file_path.xml b/wgshare/android/app/src/main/res/xml/file_path.xml new file mode 100644 index 0000000..0faed9e --- /dev/null +++ b/wgshare/android/app/src/main/res/xml/file_path.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/wgshare/lib/pages/homePage/home_logic.dart b/wgshare/lib/pages/homePage/home_logic.dart index 3179953..c42f1ed 100644 --- a/wgshare/lib/pages/homePage/home_logic.dart +++ b/wgshare/lib/pages/homePage/home_logic.dart @@ -1,12 +1,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:signalr_core/signalr_core.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:wgshare/common/models/common/base_structure_result.dart'; import 'package:wgshare/common/mixins/request_tool_mixin.dart'; import 'package:wgshare/common/store/user_store.dart'; import '../../common/models/meeting_room_item.dart'; +import '../../routes/app_routes.dart'; +import '../../utils/permission/PermissionService.dart'; import '../../utils/toast_utils.dart'; +import '../../view/public_dialog.dart'; +import '../../view/upgrade/loadJson/load_network_json.dart'; import 'home_state.dart'; class HomeLogic extends GetxController with RequestToolMixin { @@ -16,6 +20,7 @@ class HomeLogic extends GetxController with RequestToolMixin { @override void onInit() { super.onInit(); + doHttpCheckVersion(Get.context!); doHttpGetMeetingRoomList(state.pageIndex.value,state.pageSize.value); } @@ -73,5 +78,41 @@ class HomeLogic extends GetxController with RequestToolMixin { return true; } - + /// 进入会议室 + void gotoMeetingRoom(BuildContext context, int index){ + PermissionService.checkPermission(permissionList: [Permission.microphone,Permission.camera]).then((value){ + if(value == true){ + Get.back(); + Get.toNamed(Routes.meetingMainPage, + arguments: { + "roomNumber": state.meetingRooms.value[index].roomNum + }); + }else{ + publicDialog( + context, + hideCancelBtn: true, + title: '请求权限说明', + describe: '进入会议室需要获取您的麦克风以及摄像头权限,用以确保能够正常参会发言。', + leftBtnStr: '拒绝', + rightBtnStr: '同意', + leftBtnCallback: (){ + Get.toNamed(Routes.meetingMainPage, + arguments: { + "roomNumber": state.meetingRooms.value[index].roomNum + }); + }, + rightBtnCallback: (){ + PermissionService.requestPermissions().then((value){ + if(value == true){ + Get.toNamed(Routes.meetingMainPage, + arguments: { + "roomNumber": state.meetingRooms.value[index].roomNum + }); + } + }); + } + ); + } + }); + } } diff --git a/wgshare/lib/pages/homePage/home_view.dart b/wgshare/lib/pages/homePage/home_view.dart index fe845a9..db206c1 100644 --- a/wgshare/lib/pages/homePage/home_view.dart +++ b/wgshare/lib/pages/homePage/home_view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -189,11 +190,7 @@ class HomePageState extends State with AutomaticKeepAliveClientMixin { ), ), onTap: () { - Get.toNamed(Routes.meetingMainPage, - arguments: { - "roomNumber": state.meetingRooms - .value[index].roomNum - }); + logic.gotoMeetingRoom(context, index); }, ) ], diff --git a/wgshare/lib/pages/loginPage/login_logic.dart b/wgshare/lib/pages/loginPage/login_logic.dart index 01c50f7..e2d1821 100644 --- a/wgshare/lib/pages/loginPage/login_logic.dart +++ b/wgshare/lib/pages/loginPage/login_logic.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:wgshare/common/mixins/request_tool_mixin.dart'; import 'package:wgshare/utils/device_info.dart'; import 'package:wgshare/utils/toast_utils.dart'; @@ -10,15 +11,21 @@ import 'package:wgshare/utils/toast_utils.dart'; import '../../common/config/app_config.dart'; import '../../common/models/common/base_structure_result.dart'; import '../../common/models/user_info_entity.dart'; -import '../../common/store/app_storage_key.dart'; import '../../common/store/user_store.dart'; import '../../routes/app_routes.dart'; -import '../../utils/storage.dart'; +import '../../utils/permission/PermissionService.dart'; +import '../../view/public_dialog.dart'; +import '../../view/upgrade/loadJson/load_network_json.dart'; import 'login_state.dart'; class LoginLogic extends GetxController with RequestToolMixin { final LoginState state = LoginState(); + @override + void onInit() { + super.onInit(); + } + @override void onClose() { super.onClose(); diff --git a/wgshare/lib/pages/loginPage/login_view.dart b/wgshare/lib/pages/loginPage/login_view.dart index ffca5fc..70a715c 100644 --- a/wgshare/lib/pages/loginPage/login_view.dart +++ b/wgshare/lib/pages/loginPage/login_view.dart @@ -39,7 +39,7 @@ class _LoginPageState extends State { decoration: const BoxDecoration( image: DecorationImage( image: AssetImage('assets/images/login_bg.png'), - fit: BoxFit.fill, + fit: BoxFit.cover, ), ), child: Column( diff --git a/wgshare/lib/pages/metting/meeting_main_logic.dart b/wgshare/lib/pages/metting/meeting_main_logic.dart index d5b6778..0ce4835 100644 --- a/wgshare/lib/pages/metting/meeting_main_logic.dart +++ b/wgshare/lib/pages/metting/meeting_main_logic.dart @@ -7,6 +7,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:signalr_core/signalr_core.dart'; import 'package:wgshare/common/store/user_store.dart'; import 'package:wgshare/utils/count_microphone_volume.dart'; @@ -696,9 +697,6 @@ class MeetingMainLogic extends GetxController with RequestToolMixin { /// ------------------------------------------------------------------------------声网SDK相关 /// 初始化声网SDK Future initRtc() async { - // 请求麦克风、摄像头权限 - PermissionService.requestPermissions(); - // 创建 RtcEngine 对象 state.rctEngine.value = createAgoraRtcEngineEx(); diff --git a/wgshare/lib/pages/metting/meeting_main_state.dart b/wgshare/lib/pages/metting/meeting_main_state.dart index f2e9a20..c8d6d6b 100644 --- a/wgshare/lib/pages/metting/meeting_main_state.dart +++ b/wgshare/lib/pages/metting/meeting_main_state.dart @@ -44,6 +44,9 @@ class MeetingMainState { /// 会议室token late RxString meetingToken = "".obs; + /// 麦克风摄像头权限是否申请成功 + late RxBool isCameraToMicrophonePermissions = false.obs; + /// 会议室信息 late Rx meetingRoomInfo = Rx(null); /// 是否弹出showOkAlertDialog diff --git a/wgshare/lib/pages/metting/meeting_main_view.dart b/wgshare/lib/pages/metting/meeting_main_view.dart index bc00413..e1f7648 100644 --- a/wgshare/lib/pages/metting/meeting_main_view.dart +++ b/wgshare/lib/pages/metting/meeting_main_view.dart @@ -9,6 +9,7 @@ import 'package:flutter_floating/floating/floating.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:liquid_progress_indicator_v2/liquid_progress_indicator.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:preload_page_view/preload_page_view.dart'; import 'package:wgshare/common/config/app_config.dart'; import 'package:wgshare/common/store/user_store.dart'; @@ -16,6 +17,8 @@ import 'package:wgshare/utils/toast_utils.dart'; import '../../utils/color_util.dart'; import '../../utils/cus_behavior.dart'; +import '../../utils/permission/PermissionService.dart'; +import '../../view/public_dialog.dart'; import '../../view/view_svg_path.dart'; import 'meeting_main_logic.dart'; import 'meeting_main_state.dart'; @@ -50,442 +53,468 @@ class MeetingMainPageState extends State { state.context.value = context; return PopScope( canPop: false, - child: FlutterEasyLoading( - child: Scaffold( - appBar: AppBar( - surfaceTintColor: ColorUtil.Color_41_41_41, - elevation: 0, - toolbarHeight: 0, - systemOverlayStyle: const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - systemNavigationBarColor: ColorUtil.Color_41_41_41, - systemNavigationBarIconBrightness: Brightness.light, - statusBarIconBrightness: Brightness.light, - statusBarBrightness: Brightness.light, + child: FlutterEasyLoading( + child: Scaffold( + appBar: AppBar( + surfaceTintColor: ColorUtil.Color_41_41_41, + elevation: 0, + toolbarHeight: 0, + systemOverlayStyle: const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + systemNavigationBarColor: ColorUtil.Color_41_41_41, + systemNavigationBarIconBrightness: Brightness.light, + statusBarIconBrightness: Brightness.light, + statusBarBrightness: Brightness.light, + ), + backgroundColor: ColorUtil.Color_41_41_41, ), - backgroundColor: ColorUtil.Color_41_41_41, - ), - body: Obx(() => Stack( - children: [ - Column( - children: [ - /// 顶部布局 - Container( - width: double.infinity, - height: 100.h, - alignment: Alignment.center, - color: ColorUtil.Color_41_41_41, - padding: const EdgeInsets.only(left: 16, right: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + body: Obx(() => Stack( + children: [ + Column( children: [ + /// 顶部布局 Container( - width: 92.w, + width: double.infinity, + height: 100.h, + alignment: Alignment.center, + color: ColorUtil.Color_41_41_41, + padding: const EdgeInsets.only(left: 16, right: 16), child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - GestureDetector( - child: Image.asset( - 'assets/images/meeting_main_narrow.png', - width: 20.w, - height: 20.h, - ), - onTap: () {}, - ), - SizedBox(width: 16.w), - GestureDetector( - child: Image.asset( - state.communicationMode.value == 1 - ? 'assets/images/meeting_main_receiver.png' - : 'assets/images/meeting_main_speaker.png', - width: 20.w, - height: 20.h, - ), - onTap: () { - if (state.communicationMode.value == 1 || - state.communicationMode.value == 3) { - logic.changeMeetingAudioState(true); - } - }, - ), - Visibility( - visible: state.isOpenCamera.value, - child: GestureDetector( - child: Row( - children: [ - SizedBox(width: 16.w), - Image.asset( - 'assets/images/meeting_main_rotate_camera.png', + Container( + width: 92.w, + child: Row( + children: [ + GestureDetector( + child: Image.asset( + 'assets/images/meeting_main_narrow.png', width: 20.w, height: 20.h, - ) - ], - ), - onTap: () { - logic.switchCamera(); - }, - ), - ) - ], - ), - ), - GestureDetector( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - children: [ - Text( - state.meetingRoomInfo.value?.roomName ?? - '', - style: TextStyle( - color: Colors.white, - fontSize: 14.sp, - fontWeight: FontWeight.w500), - ), - SizedBox(width: 4.w), - Image.asset( - 'assets/images/meeting_main_down.png', - width: 16.w, - height: 16.h, - ) - ], - ), - SizedBox(height: 4.h), - Text( - state.duration.value, - style: TextStyle( - color: Colors.white, - fontSize: 12.sp, - ), - ) - ], - ), - onTap: () { - logic.changeMeetingInfoState(true); - }, - ), - Container( - width: 92.w, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - /// 退出会议 - GestureDetector( - child: Image.asset( - 'assets/images/meeting_leave.png', - width: 20.w, - height: 20.h, - ), - onTap: () { - Get.bottomSheet( - isScrollControlled: true, - leaveBottomSheet(context)); - }, - ) - ], - ), - ) - ], - ), - ), - - /// 中间布局 - Expanded( - child: Stack( - alignment: Alignment.bottomLeft, - children: [ - // 语音 - Visibility( - visible: state.pageState.value == 0, - child: MeetingMainVoiceComponent( - users: state.cacheUsers.value)), - - // 视频-共享 - Visibility( - visible: state.pageState.value == 1, - child: state.isJoinSuccess.value == true && null != state.rctEngine.value && null != state.users.value && state.users.value.isNotEmpty - ? Stack( - alignment: Alignment.center, - children: [ - PreloadPageView.builder( - preloadPagesCount: 2, - itemCount: 2, - itemBuilder: (BuildContext context, - int position) => - returnPage(position), - controller: PreloadPageController( - initialPage: 0), - onPageChanged: (int position) { - state.pageIndex.value = position; - if (state.isSpeak.value == true && - state.isOpenCamera.value == - true && - state.remoteUid.value != "0") { - if (position == 0) { - state.floating.value - ?.open(context); - } else { - state.floating.value?.close(); - } - } - }, - ), - - /// pageview 指示器 - Positioned( - bottom: 16, - child: Row( - children: [ - Container( - width: 8.w, - height: 8.h, - margin: const EdgeInsets.only( - right: 6), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular( - 8), - color: state.pageIndex - .value == - 0 - ? ColorUtil - .Color_255_255_255 - : ColorUtil - .Color_108_108_108), ), - Container( - width: 8.w, - height: 8.h, - margin: const EdgeInsets.only( - left: 6), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular( - 8), - color: state.pageIndex - .value == - 1 - ? ColorUtil - .Color_255_255_255 - : ColorUtil - .Color_108_108_108), - ) - ], - ), - ), - ], - ) - : Container( - color: ColorUtil.Color_0_0_0_0, - /*child: Text('加载反馈:是否成功加入会议室${state.isJoinSuccess.value}-会议室对象${state.rctEngine.value}-成员列表${state.users.value.length}-全员观看ID${state.remoteUid.value}'),*/ - )), - - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GestureDetector( - child: Container( - width: 200.w, - height: 40.h, - margin: const EdgeInsets.only( - left: 20, bottom: 40), - padding: const EdgeInsets.only(left: 20), - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(56), - topRight: Radius.circular(50), - bottomRight: Radius.circular(50)), - color: ColorUtil.Color_35_35_35_07, - border: Border.all( - width: 1.w, - color: ColorUtil.Color_99_111_158), - ), - child: Row( - crossAxisAlignment: - CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Image.asset( - 'assets/images/meeting_main_chat.png', - width: 18.w, - height: 18.h, + onTap: () {}, ), - SizedBox(width: 6.w), - Text( - '说点什么...', - style: TextStyle( - fontSize: 14.sp, - color: ColorUtil.Color_156_156_156), + SizedBox(width: 16.w), + GestureDetector( + child: Image.asset( + state.communicationMode.value == 1 + ? 'assets/images/meeting_main_receiver.png' + : 'assets/images/meeting_main_speaker.png', + width: 20.w, + height: 20.h, + ), + onTap: () { + if (state.communicationMode.value == + 1 || + state.communicationMode.value == + 3) { + logic.changeMeetingAudioState(true); + } + }, + ), + Visibility( + visible: state.isOpenCamera.value, + child: GestureDetector( + child: Row( + children: [ + SizedBox(width: 16.w), + Image.asset( + 'assets/images/meeting_main_rotate_camera.png', + width: 20.w, + height: 20.h, + ) + ], + ), + onTap: () { + logic.switchCamera(); + }, + ), ) ], ), ), - onTap: () { - Get.bottomSheet( - isScrollControlled: true, - chatBottomSheet(context)); - Future.delayed( - const Duration(milliseconds: 100), () { - state.chatController.jumpTo(state - .chatController - .position - .maxScrollExtent); - }); - }, - ), - Visibility( - visible: state.isSpeak.value, - child: GestureDetector( - child: Container( - width: 82.w, - height: 40.h, - margin: const EdgeInsets.only( - left: 12, bottom: 40, right: 20), - alignment: Alignment.center, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all( - Radius.circular(99)), - color: ColorUtil.Color_255_69_69), - child: Text( - '结束发言', - style: TextStyle( - fontSize: 14.sp, - color: ColorUtil.Color_255_255_255), - ), + GestureDetector( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + children: [ + Text( + state.meetingRoomInfo.value + ?.roomName ?? + '', + style: TextStyle( + color: Colors.white, + fontSize: 14.sp, + fontWeight: FontWeight.w500), + ), + SizedBox(width: 4.w), + Image.asset( + 'assets/images/meeting_main_down.png', + width: 16.w, + height: 16.h, + ) + ], + ), + SizedBox(height: 4.h), + Text( + state.duration.value, + style: TextStyle( + color: Colors.white, + fontSize: 12.sp, + ), + ) + ], ), onTap: () { - logic.cancelSpeak(); + logic.changeMeetingInfoState(true); }, ), - ) - ], - ) - ], - ), - ), - - /// 底部布局 - Container( - width: double.infinity, - height: 84.h, - color: ColorUtil.Color_35_35_35, - padding: const EdgeInsets.only(left: 26, right: 26), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - /// 音频 - GestureDetector( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - state.isSpeak.value == false - ? Image.asset( - state.isSpeak.value == false - ? 'assets/images/meeting_main_sqfy.png' - : state.isOpenMicrophone.value == - true - ? 'assets/images/meeting_main_microphone_default.png' - : 'assets/images/meeting_main_sqfy.png', - width: 20.w, - height: 20.h, - ) - : state.isOpenMicrophone.value == true - ? Container( - width: 20.w, - height: 20.h, - child: LiquidCustomProgressIndicator( - value: state - .microphoneVolume.value, - valueColor: - const AlwaysStoppedAnimation( - ColorUtil - .Color_2_177_136), - backgroundColor: - ColorUtil.Color_255_255_255, - direction: Axis.vertical, - shapePath: ViewSvgPath - .getMicrpphonePath()), - ) - : Image.asset( - 'assets/images/meeting_main_sqfy.png', - width: 20.w, - height: 20.h, - ), - SizedBox(height: 4.h), - Text( - state.isSpeak.value == false - ? '申请发言' - : state.isOpenMicrophone.value == true - ? "手动静音" - : "解除静音", - style: TextStyle( - fontSize: 12.sp, - color: ColorUtil.Color_202_202_202), + Container( + width: 92.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + /// 退出会议 + GestureDetector( + child: Image.asset( + 'assets/images/meeting_leave.png', + width: 20.w, + height: 20.h, + ), + onTap: () { + Get.bottomSheet( + isScrollControlled: true, + leaveBottomSheet(context)); + }, + ) + ], + ), ) ], ), - onTap: () { - if (state.isSpeak.value == false) { - Get.bottomSheet( - isScrollControlled: true, - applySpeakPermissionBottomSheet( - context, 1)); - } else { - if (state.isOpenMicrophone.value == false) { - logic.doHttpSetMicr(true); - } else { - logic.doHttpSetMicr(false); - } - } - }, ), - /// 视频 - GestureDetector( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, + /// 中间布局 + Expanded( + child: Stack( + alignment: Alignment.bottomLeft, children: [ - Image.asset( - state.isSpeak.value == true - ? state.isOpenCamera.value == true - ? 'assets/images/meeting_main_camera_open.png' - : 'assets/images/meeting_main_camera_default.png' - : 'assets/images/meeting_main_sp.png', - width: 22.w, - height: 22.h, + // 语音 + Visibility( + visible: state.pageState.value == 0, + child: MeetingMainVoiceComponent( + users: state.cacheUsers.value)), + + // 视频-共享 + Visibility( + visible: state.pageState.value == 1, + child: state.isJoinSuccess.value == true && + null != state.rctEngine.value && + null != state.users.value && + state.users.value.isNotEmpty + ? Stack( + alignment: Alignment.center, + children: [ + PreloadPageView.builder( + preloadPagesCount: 2, + itemCount: 2, + itemBuilder: + (BuildContext context, + int position) => + returnPage(position), + controller: + PreloadPageController( + initialPage: 0), + onPageChanged: (int position) { + state.pageIndex.value = + position; + if (state.isSpeak.value == + true && + state.isOpenCamera + .value == + true && + state.remoteUid.value != + "0") { + if (position == 0) { + state.floating.value + ?.open(context); + } else { + state.floating.value + ?.close(); + } + } + }, + ), + + /// pageview 指示器 + Positioned( + bottom: 16, + child: Row( + children: [ + Container( + width: 8.w, + height: 8.h, + margin: + const EdgeInsets.only( + right: 6), + decoration: BoxDecoration( + borderRadius: + BorderRadius + .circular(8), + color: state.pageIndex + .value == + 0 + ? ColorUtil + .Color_255_255_255 + : ColorUtil + .Color_108_108_108), + ), + Container( + width: 8.w, + height: 8.h, + margin: + const EdgeInsets.only( + left: 6), + decoration: BoxDecoration( + borderRadius: + BorderRadius + .circular(8), + color: state.pageIndex + .value == + 1 + ? ColorUtil + .Color_255_255_255 + : ColorUtil + .Color_108_108_108), + ) + ], + ), + ), + ], + ) + : Container( + color: ColorUtil.Color_0_0_0_0, + /*child: Text('加载反馈:是否成功加入会议室${state.isJoinSuccess.value}-会议室对象${state.rctEngine.value}-成员列表${state.users.value.length}-全员观看ID${state.remoteUid.value}'),*/ + )), + + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: Container( + width: 200.w, + height: 40.h, + margin: const EdgeInsets.only( + left: 20, bottom: 40), + padding: + const EdgeInsets.only(left: 20), + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(56), + topRight: Radius.circular(50), + bottomRight: Radius.circular(50)), + color: ColorUtil.Color_35_35_35_07, + border: Border.all( + width: 1.w, + color: + ColorUtil.Color_99_111_158), + ), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Image.asset( + 'assets/images/meeting_main_chat.png', + width: 18.w, + height: 18.h, + ), + SizedBox(width: 6.w), + Text( + '说点什么...', + style: TextStyle( + fontSize: 14.sp, + color: ColorUtil + .Color_156_156_156), + ) + ], + ), + ), + onTap: () { + Get.bottomSheet( + isScrollControlled: true, + chatBottomSheet(context)); + Future.delayed( + const Duration(milliseconds: 100), + () { + state.chatController.jumpTo(state + .chatController + .position + .maxScrollExtent); + }); + }, + ), + Visibility( + visible: state.isSpeak.value, + child: GestureDetector( + child: Container( + width: 82.w, + height: 40.h, + margin: const EdgeInsets.only( + left: 12, bottom: 40, right: 20), + alignment: Alignment.center, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(99)), + color: ColorUtil.Color_255_69_69), + child: Text( + '结束发言', + style: TextStyle( + fontSize: 14.sp, + color: ColorUtil + .Color_255_255_255), + ), + ), + onTap: () { + logic.cancelSpeak(); + }, + ), + ) + ], + ) + ], + ), + ), + + /// 底部布局 + Container( + width: double.infinity, + height: 84.h, + color: ColorUtil.Color_35_35_35, + padding: const EdgeInsets.only(left: 26, right: 26), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + /// 音频 + GestureDetector( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + state.isSpeak.value == false + ? Image.asset( + state.isSpeak.value == false + ? 'assets/images/meeting_main_sqfy.png' + : state.isOpenMicrophone + .value == + true + ? 'assets/images/meeting_main_microphone_default.png' + : 'assets/images/meeting_main_sqfy.png', + width: 20.w, + height: 20.h, + ) + : state.isOpenMicrophone.value == true + ? Container( + width: 20.w, + height: 20.h, + child: LiquidCustomProgressIndicator( + value: state + .microphoneVolume + .value, + valueColor: + const AlwaysStoppedAnimation( + ColorUtil + .Color_2_177_136), + backgroundColor: ColorUtil + .Color_255_255_255, + direction: Axis.vertical, + shapePath: ViewSvgPath + .getMicrpphonePath()), + ) + : Image.asset( + 'assets/images/meeting_main_sqfy.png', + width: 20.w, + height: 20.h, + ), + SizedBox(height: 4.h), + Text( + state.isSpeak.value == false + ? '申请发言' + : state.isOpenMicrophone.value == + true + ? "手动静音" + : "解除静音", + style: TextStyle( + fontSize: 12.sp, + color: ColorUtil.Color_202_202_202), + ) + ], + ), + onTap: () { + if (state.isSpeak.value == false) { + Get.bottomSheet( + isScrollControlled: true, + applySpeakPermissionBottomSheet( + context, 1)); + } else { + if (state.isOpenMicrophone.value == + false) { + logic.doHttpSetMicr(true); + } else { + logic.doHttpSetMicr(false); + } + } + }, ), - SizedBox(height: 4.h), - Text( - /*state.isSpeak.value == false + + /// 视频 + GestureDetector( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + state.isSpeak.value == true + ? state.isOpenCamera.value == true + ? 'assets/images/meeting_main_camera_open.png' + : 'assets/images/meeting_main_camera_default.png' + : 'assets/images/meeting_main_sp.png', + width: 22.w, + height: 22.h, + ), + SizedBox(height: 4.h), + Text( + /*state.isSpeak.value == false ? '申请发言' : */ - state.isOpenCamera.value == true - ? "关闭视频" - : "开启视频", - style: TextStyle( - fontSize: 12.sp, - color: ColorUtil.Color_202_202_202), - ) - ], - ), - onTap: () { - if (state.isSpeak.value == false) { - Get.bottomSheet( - isScrollControlled: true, - applySpeakPermissionBottomSheet( - context, 2)); - } else { - if (state.isOpenCamera.value == true) { - logic.doHttpSetCamer(false); - } else { - logic.doHttpSetCamer(true); - } - } - }, - ), + state.isOpenCamera.value == true + ? "关闭视频" + : "开启视频", + style: TextStyle( + fontSize: 12.sp, + color: ColorUtil.Color_202_202_202), + ) + ], + ), + onTap: () { + if (state.isSpeak.value == false) { + Get.bottomSheet( + isScrollControlled: true, + applySpeakPermissionBottomSheet( + context, 2)); + } else { + if (state.isOpenCamera.value == true) { + logic.doHttpSetCamer(false); + } else { + logic.doHttpSetCamer(true); + } + } + }, + ), - /// 共享(此版本不做) - /*GestureDetector( + /// 共享(此版本不做) + /*GestureDetector( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -518,43 +547,42 @@ class MeetingMainPageState extends State { }, ),*/ - /// 成员 - GestureDetector( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/meeting_main_member_default.png', - width: 22.w, - height: 22.h, + /// 成员 + GestureDetector( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/meeting_main_member_default.png', + width: 22.w, + height: 22.h, + ), + SizedBox(height: 4.h), + Text( + ' 成员 ', + style: TextStyle( + fontSize: 12.sp, + color: ColorUtil.Color_202_202_202), + ) + ], + ), + onTap: () { + Get.bottomSheet( + isScrollControlled: true, + queryMemberFloatingLayer(context), + ); + }, ), - SizedBox(height: 4.h), - Text( - ' 成员 ', - style: TextStyle( - fontSize: 12.sp, - color: ColorUtil.Color_202_202_202), - ) ], ), - onTap: () { - Get.bottomSheet( - isScrollControlled: true, - queryMemberFloatingLayer(context), - ); - }, - ), + ) ], ), - ) - ], - ), - meetingInfoFloatingLayer(), - meetingAudioFloatingLayer(), - ], - ))), - ) - ); + meetingInfoFloatingLayer(), + meetingAudioFloatingLayer(), + ], + ))), + )); } /// 退出会议底部弹窗 @@ -989,9 +1017,32 @@ class MeetingMainPageState extends State { ), ), onTap: () { - state.defaulOpenState.value = defaulOpenState; - logic.doHttpApplySpeak(); - Get.back(); + PermissionService.checkPermission(permissionList: [Permission.microphone,Permission.camera]).then((value){ + if(value == true){ + state.defaulOpenState.value = defaulOpenState; + logic.doHttpApplySpeak(); + Get.back(); + }else{ + publicDialog(context, + hideCancelBtn: true, + title: '请求权限说明', + describe: + '申请发言需要获取您的麦克风以及摄像头权限,用以确保能够正常进行会议。', + leftBtnStr: '拒绝', + rightBtnStr: '同意', + leftBtnCallback: () { + }, rightBtnCallback: () { + PermissionService.requestPermissions(); + /*PermissionService.requestPermissions().then((value){ + if(value == true){ + state.defaulOpenState.value = defaulOpenState; + logic.doHttpApplySpeak(); + Get.back(); + } + });*/ + }); + } + }); }, ), ) @@ -1156,7 +1207,8 @@ class MeetingMainPageState extends State { style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, - color: ColorUtil.Color_243_243_243), + color: + ColorUtil.Color_243_243_243), ), ), SizedBox(width: 8.w), @@ -1200,10 +1252,11 @@ class MeetingMainPageState extends State { children: [ Container( child: Image.asset( - state.users.value[index].enableShare == - true - ? 'assets/images/meeting_main_share_currently.png' - : 'assets/images/meeting_main_share_currently_n.png', + state.users.value[index] + .enableShare == + true + ? 'assets/images/meeting_main_share_currently.png' + : 'assets/images/meeting_main_share_currently_n.png', width: 17.w, height: 17.h, ), @@ -1393,13 +1446,14 @@ class MeetingMainPageState extends State { TextSpan( text: state.meetingRoomMsgs.value[index].userName, style: TextStyle( - fontSize: 12.sp, color: ColorUtil.Color_202_202_202), + fontSize: 12.sp, + color: ColorUtil.Color_202_202_202), ), TextSpan( text: " ${state.meetingRoomMsgs.value[index].time}", style: TextStyle( - fontSize: 10.sp, color: ColorUtil.Color_202_202_202 - ), + fontSize: 10.sp, + color: ColorUtil.Color_202_202_202), ), ], ), @@ -1452,13 +1506,14 @@ class MeetingMainPageState extends State { TextSpan( text: "${state.meetingRoomMsgs.value[index].time} ", style: TextStyle( - fontSize: 10.sp, color: ColorUtil.Color_202_202_202 - ), + fontSize: 10.sp, + color: ColorUtil.Color_202_202_202), ), TextSpan( text: state.meetingRoomMsgs.value[index].userName, style: TextStyle( - fontSize: 12.sp, color: ColorUtil.Color_202_202_202), + fontSize: 12.sp, + color: ColorUtil.Color_202_202_202), ), ], ), @@ -1567,36 +1622,37 @@ class MeetingMainPageState extends State { children: [ state.remoteUid.value != "" ? AgoraVideoView( - controller: VideoViewController( - rtcEngine: state.rctEngine.value!, - canvas: VideoCanvas( - uid: int.tryParse(state.remoteUid.value), - setupMode: VideoViewSetupMode.videoViewSetupAdd, - renderMode: state.remoteUid.value.length == 9 - ? RenderModeType.renderModeFit - : RenderModeType.renderModeHidden), - ), - ) - : Container( - width: double.infinity, - color: ColorUtil.Color_57_57_57, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/metting_main_no_person.png', - width: 138.w, - height: 144.h, - ), - const SizedBox(height: 20), - Text( - '主持人正在赶来的路上...', - style: TextStyle( - color: ColorUtil.Color_255_255_255, fontSize: 14.sp), + controller: VideoViewController( + rtcEngine: state.rctEngine.value!, + canvas: VideoCanvas( + uid: int.tryParse(state.remoteUid.value), + setupMode: VideoViewSetupMode.videoViewSetupAdd, + renderMode: state.remoteUid.value.length == 9 + ? RenderModeType.renderModeFit + : RenderModeType.renderModeHidden), + ), ) - ], - ), - ), + : Container( + width: double.infinity, + color: ColorUtil.Color_57_57_57, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/metting_main_no_person.png', + width: 138.w, + height: 144.h, + ), + const SizedBox(height: 20), + Text( + '主持人正在赶来的路上...', + style: TextStyle( + color: ColorUtil.Color_255_255_255, + fontSize: 14.sp), + ) + ], + ), + ), Positioned( top: 16, right: 16, @@ -1637,8 +1693,8 @@ class MeetingMainPageState extends State { ); })); - /// gridview - pageList.add(GetBuilder(builder: (controll){ + /// gridview + pageList.add(GetBuilder(builder: (controll) { return Container( color: ColorUtil.Color_57_57_57, child: GridView.builder( @@ -1649,50 +1705,51 @@ class MeetingMainPageState extends State { return Stack( children: [ state.cacheUsers.value[index].enableCamera == true - ? state.cacheUsers.value[index].uid == UserStore.to.userInfoEntity.value!.uid - ? AgoraVideoView( - controller: VideoViewController( - rtcEngine: state.rctEngine.value!, - canvas: const VideoCanvas( - uid: 0, - setupMode: - VideoViewSetupMode.videoViewSetupAdd)), - ) - : AgoraVideoView( - controller: VideoViewController.remote( - rtcEngine: state.rctEngine.value!, - canvas: VideoCanvas( - uid: int.tryParse( - state.cacheUsers.value[index].uid), - setupMode: - VideoViewSetupMode.videoViewSetupAdd), - connection: RtcConnection( - channelId: state.roomNumber.value), - ), - ) + ? state.cacheUsers.value[index].uid == + UserStore.to.userInfoEntity.value!.uid + ? AgoraVideoView( + controller: VideoViewController( + rtcEngine: state.rctEngine.value!, + canvas: const VideoCanvas( + uid: 0, + setupMode: VideoViewSetupMode + .videoViewSetupAdd)), + ) + : AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: state.rctEngine.value!, + canvas: VideoCanvas( + uid: int.tryParse( + state.cacheUsers.value[index].uid), + setupMode: + VideoViewSetupMode.videoViewSetupAdd), + connection: RtcConnection( + channelId: state.roomNumber.value), + ), + ) : Container( - color: ColorUtil.Color_16_19_13, - child: SizedBox( - width: double.infinity, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/metting_main_no_person.png', - width: 78.w, - height: 84.h, + color: ColorUtil.Color_16_19_13, + child: SizedBox( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/metting_main_no_person.png', + width: 78.w, + height: 84.h, + ), + const SizedBox(height: 12), + Text( + '该用户未开启摄像头...', + style: TextStyle( + color: ColorUtil.Color_255_255_255, + fontSize: 10.sp), + ) + ], + ), ), - const SizedBox(height: 12), - Text( - '该用户未开启摄像头...', - style: TextStyle( - color: ColorUtil.Color_255_255_255, - fontSize: 10.sp), - ) - ], - ), - ), - ), + ), Positioned( left: 4, bottom: 4, @@ -1714,62 +1771,65 @@ class MeetingMainPageState extends State { decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), color: ColorUtil.Color_0_0_0_96), - child: state.cacheUsers.value[index].enableMicr == true + child: state.cacheUsers.value[index].enableMicr == + true ? Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - width: 20.w, - height: 20.h, - child: LiquidCustomProgressIndicator( - value: state.cacheUsers.value[index] - .volume ?? - 0.0, - valueColor: - const AlwaysStoppedAnimation( - ColorUtil.Color_2_177_136), - backgroundColor: - ColorUtil.Color_255_255_255, - direction: Axis.vertical, - shapePath: - ViewSvgPath.getMicrpphonePath()), - ), - SizedBox( - width: 70, - child: Text( - state.cacheUsers.value[index].userName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12.sp, - color: ColorUtil.Color_255_255_255), - ) - ) - ], - ) + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 20.w, + height: 20.h, + child: LiquidCustomProgressIndicator( + value: state.cacheUsers.value[index] + .volume ?? + 0.0, + valueColor: + const AlwaysStoppedAnimation( + ColorUtil.Color_2_177_136), + backgroundColor: + ColorUtil.Color_255_255_255, + direction: Axis.vertical, + shapePath: + ViewSvgPath.getMicrpphonePath()), + ), + SizedBox( + width: 70, + child: Text( + state + .cacheUsers.value[index].userName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12.sp, + color: + ColorUtil.Color_255_255_255), + )) + ], + ) : Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/meeting_main_microphone_open.png', - width: 20.w, - height: 20.h, - ), - SizedBox( - width: 70, - child: Text( - state.cacheUsers.value[index].userName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12.sp, - color: ColorUtil.Color_255_255_255), - ) - ) - ], - ), + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/meeting_main_microphone_open.png', + width: 20.w, + height: 20.h, + ), + SizedBox( + width: 70, + child: Text( + state + .cacheUsers.value[index].userName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12.sp, + color: + ColorUtil.Color_255_255_255), + )) + ], + ), ) ], ), diff --git a/wgshare/lib/utils/permission/AndroidPermissionHandler.dart b/wgshare/lib/utils/permission/AndroidPermissionHandler.dart index 68460b1..b810515 100644 --- a/wgshare/lib/utils/permission/AndroidPermissionHandler.dart +++ b/wgshare/lib/utils/permission/AndroidPermissionHandler.dart @@ -17,9 +17,16 @@ class AndroidPermissionHandler { await requestMicrophonePermission(); } - Future requestCameraToMicrophonePermissions() async { - await requestCameraPermission(); - await requestMicrophonePermission(); + Future requestCameraToMicrophonePermissions() async { + var isCameraPermission = false; + var isMicrophonePermissio = false; + if(await requestCameraPermission() == true){ + isCameraPermission = true; + } + if(await requestMicrophonePermission() == true){ + isMicrophonePermissio = true; + } + return isCameraPermission == true && isMicrophonePermissio == true; } Future requestLocationPermission() async { @@ -58,10 +65,12 @@ class AndroidPermissionHandler { } } - Future requestCameraPermission() async { + Future requestCameraPermission() async { + var isCameraPermission = false; PermissionStatus status = await Permission.camera.request(); if (status.isGranted) { print("Android: 摄像头权限已授予"); + isCameraPermission = true; } else if (status.isPermanentlyDenied) { print("Android: 摄像头权限被永久拒绝,请前往设置开启"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); @@ -70,10 +79,11 @@ class AndroidPermissionHandler { print("Android: 摄像头权限被拒绝"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); } + return isCameraPermission; } Future requestStoragePermission() async { - PermissionStatus status = await Permission.storage.request(); + PermissionStatus status = await Permission.manageExternalStorage.request(); if (status.isGranted) { print("Android: 存储权限已授予"); } else if (status.isPermanentlyDenied) { @@ -105,10 +115,12 @@ class AndroidPermissionHandler { } } - static Future requestMicrophonePermission() async { + static Future requestMicrophonePermission() async { + var isMicrophonePermission = false; PermissionStatus status = await Permission.microphone.request(); if (status.isGranted) { print("Android: 麦克风权限已授予"); + isMicrophonePermission = true; } else if (status.isPermanentlyDenied) { print("Android: 麦克风权限被永久拒绝,请前往设置开启"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); @@ -117,5 +129,6 @@ class AndroidPermissionHandler { print("Android: 麦克风限被拒绝"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); } + return isMicrophonePermission; } } \ No newline at end of file diff --git a/wgshare/lib/utils/permission/IosPermissionHandler.dart b/wgshare/lib/utils/permission/IosPermissionHandler.dart index c79b289..a292cf7 100644 --- a/wgshare/lib/utils/permission/IosPermissionHandler.dart +++ b/wgshare/lib/utils/permission/IosPermissionHandler.dart @@ -13,9 +13,16 @@ class IosPermissionHandler { await requestMicrophonePermission(); } - Future requestCameraToMicrophonePermissions() async { - await requestCameraPermission(); - await requestMicrophonePermission(); + Future requestCameraToMicrophonePermissions() async { + var isCameraPermission = false; + var isMicrophonePermissio = false; + if(await requestCameraPermission() == true){ + isCameraPermission = true; + } + if(await requestMicrophonePermission() == true){ + isMicrophonePermissio = true; + } + return isCameraPermission == true && isMicrophonePermissio == true; } Future requestLocationPermission() async { @@ -38,10 +45,12 @@ class IosPermissionHandler { } } - Future requestCameraPermission() async { + Future requestCameraPermission() async { + var isCameraPermission = false; PermissionStatus status = await Permission.camera.request(); if (status.isGranted) { print("iOS: 摄像头权限已授予"); + isCameraPermission = true; } else if (status.isPermanentlyDenied) { print("iOS: 摄像头权限被永久拒绝,请前往设置开启"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); @@ -50,6 +59,7 @@ class IosPermissionHandler { print("iOS: 摄像头权限被拒绝"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); } + return isCameraPermission; } Future requestStoragePermission() async { @@ -83,10 +93,12 @@ class IosPermissionHandler { } } - static Future requestMicrophonePermission() async { + static Future requestMicrophonePermission() async { + var isMicrophonePermission = false; PermissionStatus status = await Permission.microphone.request(); if (status.isGranted) { print("iOS: 麦克风权限已授予"); + isMicrophonePermission = true; } else if (status.isPermanentlyDenied) { print("iOS: 麦克风权限被永久拒绝,请前往设置开启"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); @@ -95,5 +107,6 @@ class IosPermissionHandler { print("iOS: 麦克风限被拒绝"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); } + return isMicrophonePermission; } } \ No newline at end of file diff --git a/wgshare/lib/utils/permission/PermissionService.dart b/wgshare/lib/utils/permission/PermissionService.dart index ca7b394..013d290 100644 --- a/wgshare/lib/utils/permission/PermissionService.dart +++ b/wgshare/lib/utils/permission/PermissionService.dart @@ -1,16 +1,45 @@ import 'dart:io'; +import 'dart:ui'; + +import 'package:permission_handler/permission_handler.dart'; import 'AndroidPermissionHandler.dart'; import 'IosPermissionHandler.dart'; class PermissionService { - static Future requestPermissions() async { + static Future requestPermissions() async { + var isRequestPermissions = false; if (Platform.isIOS) { // 调用iOS权限处理逻辑 - await IosPermissionHandler().requestCameraToMicrophonePermissions(); + isRequestPermissions = await IosPermissionHandler().requestCameraToMicrophonePermissions(); } else if (Platform.isAndroid) { // 调用Android权限处理逻辑 - await AndroidPermissionHandler().requestCameraToMicrophonePermissions(); + isRequestPermissions = await AndroidPermissionHandler().requestCameraToMicrophonePermissions(); + } + return isRequestPermissions; + } + + static Future requestStoragePermissions() async { + if (Platform.isIOS) { + // 调用iOS权限处理逻辑 + IosPermissionHandler().requestStoragePermission(); + } else if (Platform.isAndroid) { + // 调用Android权限处理逻辑 + AndroidPermissionHandler().requestStoragePermission(); } } + + /// 检测是否有权限 + static Future checkPermission({required List permissionList}) async { + var isRequestPermissions = true; + ///遍历当前权限申请列表 + for (Permission permission in permissionList) { + PermissionStatus status = await permission.status; + ///如果有不允许状态 + if (!status.isGranted) { + isRequestPermissions = false; + } + } + return isRequestPermissions; + } } \ No newline at end of file diff --git a/wgshare/lib/view/public_dialog.dart b/wgshare/lib/view/public_dialog.dart new file mode 100644 index 0000000..a65ca75 --- /dev/null +++ b/wgshare/lib/view/public_dialog.dart @@ -0,0 +1,175 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/src/size_extension.dart'; +import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; +import 'package:install_plugin/install_plugin.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:wgshare/utils/color_util.dart'; +import 'package:wgshare/utils/toast_utils.dart'; +import 'package:wgshare/view/upgrade/util/load_file_cancel_request.dart'; + +publicDialog(BuildContext context, + {String title = "提示", + required String describe, + btnWidth = 116.0, + btnHeight = 42.0, + required String leftBtnStr, + required String rightBtnStr, + bool hideCancelBtn = false, + required Function leftBtnCallback, + required Function rightBtnCallback}) { + showDialog( + context: context, + barrierDismissible: hideCancelBtn, + builder: (_) => PopScope( + canPop: hideCancelBtn, + child: HintDialog(title, describe, btnWidth, btnHeight, leftBtnStr, + rightBtnStr, hideCancelBtn,leftBtnCallback,rightBtnCallback), + )); +} + +class HintDialog extends StatefulWidget { + final String title; //标题 + final String describe; //描述 + final double btnWidth; //按钮宽度 + final double btnHeight; //按钮高度 + final String leftBtnStr; // 左侧按钮内容 + final String rightBtnStr; //右侧按钮内容 + final bool hideCancelBtn; //是否隐藏取消btn(实际用途为是否可以点击空白和返回键关闭) + final Function leftBtnCallback; + final Function rightBtnCallback; + + HintDialog( + this.title, + this.describe, + this.btnWidth, + this.btnHeight, + this.leftBtnStr, + this.rightBtnStr, + this.hideCancelBtn, + this.leftBtnCallback, + this.rightBtnCallback); + + @override + State createState() { + return HintDialogState(); + } +} + +class HintDialogState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + height: 250.h, + margin: const EdgeInsets.only(left: 40, right: 40), + padding: + const EdgeInsets.only(top: 20, bottom: 20, left: 12, right: 12), + decoration: BoxDecoration( + color: const Color.fromRGBO(255, 255, 255, 1), + borderRadius: BorderRadius.circular(8), + ), + child: _contentStyle(), + ), + ); + } + + ///内容区域样式 + _contentStyle() { + return Column( + children: [ + _title(), + Expanded( + child: _hintMsg(), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: Container( + width: widget.btnWidth, + height: widget.btnHeight, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(99)), + color: ColorUtil.Color_230_230_230, + ), + margin: const EdgeInsets.only(top: 12, left: 12), + alignment: Alignment.center, + child: Text( + widget.leftBtnStr, + style: TextStyle( + fontSize: 14.sp, color: ColorUtil.Color_255_255_255), + ), + ), + onTap: () { + Get.back(); + widget.leftBtnCallback(); + }, + ), + GestureDetector( + child: Container( + width: widget.btnWidth, + height: widget.btnHeight, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(99)), + color: ColorUtil.Color_85_117_242, + ), + margin: const EdgeInsets.only(top: 12, right: 12), + alignment: Alignment.center, + child: Text( + widget.rightBtnStr, + style: TextStyle( + fontSize: 14.sp, color: ColorUtil.Color_255_255_255), + ), + ), + onTap: () { + Get.back(); + widget.rightBtnCallback(); + }, + ) + ], + ) + ], + ); + } + + ///提示标题布局 + _title() { + return Container( + alignment: Alignment.center, + margin: const EdgeInsets.only(bottom: 12), + child: Text(widget.title, + style: TextStyle( + fontSize: 16.sp, + color: ColorUtil.Color_85_117_242, + fontWeight: FontWeight.w600, + decoration: TextDecoration.none)), + ); + } + + ///提示信息布局 + _hintMsg() { + return SingleChildScrollView( + child: Container( + alignment: Alignment.topLeft, + margin: const EdgeInsets.only(left: 8, right: 8), + child: Text(widget.describe, + style: const TextStyle( + fontSize: 14, + color: ColorUtil.Color_89_88_88, + decoration: TextDecoration.none, + height: 1.4)), + ), + ); + } +} diff --git a/wgshare/lib/view/upgrade/loadJson/entity/upgrade_entity.dart b/wgshare/lib/view/upgrade/loadJson/entity/upgrade_entity.dart new file mode 100644 index 0000000..1119a93 --- /dev/null +++ b/wgshare/lib/view/upgrade/loadJson/entity/upgrade_entity.dart @@ -0,0 +1,57 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'upgrade_entity.g.dart'; + + +@JsonSerializable() +class UpgradeEntity extends Object { + + @JsonKey(name: 'code') + int code; + + @JsonKey(name: 'message') + String message; + + @JsonKey(name: 'success') + bool success; + + @JsonKey(name: 'data') + Data data; + + UpgradeEntity(this.code,this.message,this.success,this.data,); + + factory UpgradeEntity.fromJson(Map srcJson) => _$UpgradeEntityFromJson(srcJson); + + Map toJson() => _$UpgradeEntityToJson(this); + +} + + +@JsonSerializable() +class Data extends Object { + + @JsonKey(name: 'versionCode') + int versionCode; + + @JsonKey(name: 'versionName') + String versionName; + + @JsonKey(name: 'updateType') + int updateType; + + @JsonKey(name: 'versionDescribe') + String versionDescribe; + + @JsonKey(name: 'androidurl') + String androidurl; + + @JsonKey(name: 'appStoreUrl') + String appStoreUrl; + + Data(this.versionCode,this.versionName,this.updateType,this.versionDescribe,this.androidurl,this.appStoreUrl); + + factory Data.fromJson(Map srcJson) => _$DataFromJson(srcJson); + + Map toJson() => _$DataToJson(this); + +} diff --git a/wgshare/lib/view/upgrade/loadJson/load_network_json.dart b/wgshare/lib/view/upgrade/loadJson/load_network_json.dart new file mode 100644 index 0000000..4e68b0e --- /dev/null +++ b/wgshare/lib/view/upgrade/loadJson/load_network_json.dart @@ -0,0 +1,42 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import '../upgrade_dialog.dart'; +import 'entity/upgrade_entity.dart'; + +Future request(String url) async{ + Response response = await Dio().get(url); + return response.data; +} +void doHttpCheckVersion(BuildContext context){ + Future map = request("https://meeting-api.23544.com/meeting/mobile/latest.json"); + map.then((result) async { + var jsonStr = json.encode(result); + var listDynamic = jsonDecode(jsonStr); + UpgradeEntity upgradeEntity = UpgradeEntity.fromJson(listDynamic); + + if(upgradeEntity.code == 200){ + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + String versionCode = packageInfo.buildNumber; + if(int.tryParse(versionCode)! < 2){ + debugPrint("检查更新-服务器版本信息:$jsonStr"); + debugPrint("检查更新-当前版本号:$versionCode"); + upgradeDialog( + context, + upgradeEntity.data.versionDescribe, + hideCancelBtn: /*upgradeEntity.data.updateType == 1 ? false : true*/true, + title: '版本更新 v${upgradeEntity.data.versionName}', + /*upgradeEntity.data.url*/'https://s3.cn-north-1.amazonaws.com.cn/mtab.kezaihui.com/apk/takeaway_phone_release_1.apk', + upgradeEntity.data.appStoreUrl, + upgradeEntity.data.versionName + ); + }else{ + return; + } + }else{ + return; + } + }); +} \ No newline at end of file diff --git a/wgshare/lib/view/upgrade/upgrade_dialog.dart b/wgshare/lib/view/upgrade/upgrade_dialog.dart new file mode 100644 index 0000000..acb660b --- /dev/null +++ b/wgshare/lib/view/upgrade/upgrade_dialog.dart @@ -0,0 +1,302 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/src/size_extension.dart'; +import 'package:install_plugin/install_plugin.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:wgshare/utils/color_util.dart'; +import 'package:wgshare/utils/toast_utils.dart'; +import 'package:wgshare/view/upgrade/util/load_file_cancel_request.dart'; + +import '../../utils/permission/PermissionService.dart'; +import '../public_dialog.dart'; + +upgradeDialog( + BuildContext context, + String describe, + String url, + String appStoreUrl, + String versionName, { + String title = "提示", + btnWidth = 116.0, + btnHeight = 42.0, + bool hideCancelBtn = false, +}) { + showDialog( + context: context, + barrierDismissible: hideCancelBtn, + builder: (_) => PopScope( + canPop: hideCancelBtn, + child: HintDialog(title, describe, url, appStoreUrl, versionName, + btnWidth, btnHeight, hideCancelBtn), + )); +} + +class HintDialog extends StatefulWidget { + final String title; //标题 + final String describe; //版本更新内容 + final String url; + final String appStoreUrl; + final String versionName; + final double btnWidth; //按钮宽度 + final double btnHeight; //按钮高度 + final bool hideCancelBtn; //是否隐藏取消btn(实际用途为是否可以点击空白和返回键关闭) + + HintDialog(this.title, this.describe, this.url, this.appStoreUrl, + this.versionName, this.btnWidth, this.btnHeight, this.hideCancelBtn); + + @override + State createState() { + return HintDialogState(); + } +} + +class HintDialogState extends State { + // 安装按钮状态,0:未点击、1:点击下载、2:安装成功、3:安装失败 + int confirmBtnType = 0; + + // 按钮文字 + String btnStr = "更新"; + + // 下载进度 + double progressValue = 0.0; + + // apk路径 + late String apkPath; + + late loadFileCancelRequest loadRequest; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + height: 400.h, + margin: const EdgeInsets.only(left: 40, right: 40), + padding: + const EdgeInsets.only(top: 20, bottom: 20, left: 12, right: 12), + decoration: BoxDecoration( + color: const Color.fromRGBO(255, 255, 255, 1), + borderRadius: BorderRadius.circular(8), + ), + child: _contentStyle(), + ), + ); + } + + ///内容区域样式 + _contentStyle() { + return Column( + children: [ + _title(), + Expanded( + child: _hintMsg(), + ), + Visibility( + visible: confirmBtnType == 1 ? true : false, + child: Container( + width: double.infinity, + height: 4.h, + color: ColorUtil.Color_85_117_242, + margin: const EdgeInsets.only(top: 12, left: 2, right: 2), + child: LinearProgressIndicator( + value: progressValue, + valueColor: + const AlwaysStoppedAnimation(ColorUtil.Color_85_117_242), + backgroundColor: ColorUtil.Color_185_184_184, + ), + ), + ), + GestureDetector( + child: Container( + width: double.infinity, + height: 42.h, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(99)), + color: ColorUtil.Color_85_117_242, + ), + margin: const EdgeInsets.only(top: 12), + alignment: Alignment.center, + child: Text( + btnStr, + style: TextStyle( + fontSize: 14.sp, color: ColorUtil.Color_255_255_255), + ), + ), + onTap: () { + if (Platform.isIOS) { + _gotoAppStore(); + } else { + if (confirmBtnType == 0) { + _downloadApk(); + } + if (confirmBtnType == 1) { + Navigator.of(context).pop(); + } + if (confirmBtnType == 2) { + PermissionService.checkPermission(permissionList: [Permission.manageExternalStorage]).then((value){ + if(value == true){ + // 下载完成,调用安装APK函数 + _installApk(apkPath); + }else{ + publicDialog(context, + hideCancelBtn: true, + title: '请求权限说明', + describe: + 'APP需要获取您的文件访问权限,用以确保新版本下载后可以正常安装。', + leftBtnStr: '拒绝', + rightBtnStr: '同意', + leftBtnCallback: () { + }, rightBtnCallback: () { + PermissionService.requestStoragePermissions(); + }); + } + }); + } + if (confirmBtnType == 3) { + SystemNavigator.pop(); + } + if (confirmBtnType == 4) { + Navigator.of(context).pop(); + } + } + }, + ) + ], + ); + } + + ///提示标题布局 + _title() { + return Container( + alignment: Alignment.center, + margin: const EdgeInsets.only(bottom: 12), + child: Text(widget.title, + style: TextStyle( + fontSize: 16.sp, + color: ColorUtil.Color_85_117_242, + fontWeight: FontWeight.w600, + decoration: TextDecoration.none)), + ); + } + + ///提示信息布局 + _hintMsg() { + return SingleChildScrollView( + child: Container( + alignment: Alignment.topLeft, + margin: const EdgeInsets.only(left: 8, right: 8), + child: Text(widget.describe, + style: const TextStyle( + fontSize: 14, + color: ColorUtil.Color_89_88_88, + decoration: TextDecoration.none, + height: 1.4)), + ), + ); + } + + /// 下载APK + _downloadApk() async { + loadRequest = loadFileCancelRequest(); + try { + // 设置apk下载路径 + var appDocDir = await getTemporaryDirectory(); + apkPath = "${appDocDir.path}/wgshare_${widget.versionName}.apk"; + + loadRequest.load(widget.url, apkPath); + + loadRequest.setProgressCallbackListener((progress) { + setState(() { + progressValue = progress; + }); + }); + + loadRequest.setSuccessCallbackListener(() { + setState(() { + confirmBtnType = 2; + btnStr = "安装"; + }); + }); + + setState(() { + confirmBtnType = 1; + btnStr = "取消"; + }); + } catch (e) { + debugPrint("检查更新-安卓下载失败:$e"); + setState(() { + confirmBtnType = 0; + btnStr = "更新"; + }); + ToastUtils.showError("下载失败"); + } + + /*Dio dio = Dio(); + try { + // 设置apk下载路径 + var appDocDir = await getTemporaryDirectory(); + apkPath = "${appDocDir.path}/wgshare_${widget.versionName}.apk"; + + setState(() { + confirmBtnType = 1; + btnStr = "取消"; + }); + + // 下载文件 + await dio.download(widget.url, apkPath, onReceiveProgress: (received, total) { + if (total != -1) { + // 下载进度 + setState(() { + progressValue = (received / total * 100); + }); + } + }).then((onValue){ + setState(() { + confirmBtnType = 2; + btnStr = "安装"; + }); + }); + } catch (e) { + debugPrint("检查更新-下载失败:$e"); + setState(() { + confirmBtnType = 0; + btnStr = "更新"; + }); + ToastUtils.showError("下载失败"); + }*/ + } + + /// 安装APK + _installApk(String path) async { + await InstallPlugin.installApk(path).then((result) { + debugPrint("检查更新-安卓安装成功:$result"); + setState(() { + confirmBtnType = 3; + btnStr = "关闭APP,重新打开"; + }); + }).catchError((error) { + debugPrint("检查更新-安卓安装失败:$error"); + setState(() { + confirmBtnType = 4; + btnStr = "跳过"; + }); + }); + } + + /// 跳转苹果应用商店 + _gotoAppStore() async { + await InstallPlugin.install(widget.appStoreUrl).then((result){ + debugPrint("检查更新-苹果安装成功:$result"); + }).catchError((error){ + debugPrint("检查更新-苹果安装失败:$error"); + }); + } +} diff --git a/wgshare/lib/view/upgrade/util/load_file_cancel_request.dart b/wgshare/lib/view/upgrade/util/load_file_cancel_request.dart new file mode 100644 index 0000000..b658ba5 --- /dev/null +++ b/wgshare/lib/view/upgrade/util/load_file_cancel_request.dart @@ -0,0 +1,36 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; + +class loadFileCancelRequest{ + + late Function progressCallback; + late Function successCallback; + + CancelToken token = CancelToken(); + + load(String url, String path) async { + Dio dio = Dio(); + await dio.download(url, path, cancelToken: token, onReceiveProgress: (received, total) { + if (total != -1) { + // 下载进度 + progressCallback((received / total)); + debugPrint("检查更新-下载进度:${(received / total)}"); + } + }).then((onValue){ + // 下载成功 + successCallback(); + }); + } + + cancel() { + token.cancel('取消请求'); + } + + setProgressCallbackListener(Function progressCallback){ + this.progressCallback = progressCallback; + } + + setSuccessCallbackListener(Function successCallback){ + this.successCallback = successCallback; + } +} \ No newline at end of file diff --git a/wgshare/pubspec.lock b/wgshare/pubspec.lock index e85eb93..80d172a 100644 --- a/wgshare/pubspec.lock +++ b/wgshare/pubspec.lock @@ -634,6 +634,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.0.2" + install_plugin: + dependency: "direct main" + description: + name: install_plugin + sha256: "6fb67ba0781e75de4f2f2266ed25e835bfd277c5bfc2ed034af52774355857c6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" intersperse: dependency: transitive description: @@ -843,7 +851,7 @@ packages: source: hosted version: "1.9.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" diff --git a/wgshare/pubspec.yaml b/wgshare/pubspec.yaml index 7665411..168d401 100644 --- a/wgshare/pubspec.yaml +++ b/wgshare/pubspec.yaml @@ -92,6 +92,12 @@ dependencies: # 弹窗 adaptive_dialog: ^2.3.0 + # 安装apk + install_plugin: ^2.1.0 + + # 文件目录 + path_provider: ^2.0.2 + dev_dependencies: flutter_test: sdk: flutter