目前iOS无法修改播放音频的音调,增加提示和判断
修复部分我的音频和喜欢数据变化列表未刷新问题
This commit is contained in:
parent
422a3f8802
commit
564f4b7b18
@ -519,12 +519,15 @@
|
|||||||
baseConfigurationReference = 3D79BFA926F2692E951E423D /* Pods-RunnerTests.debug.xcconfig */;
|
baseConfigurationReference = 3D79BFA926F2692E951E423D /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.fl.musicPlayer.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.fl.musicPlayer.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|||||||
@ -51,7 +51,6 @@ class RenameDialogState extends State<RenameDialog> {
|
|||||||
canPop: true,
|
canPop: true,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
primary: true,
|
|
||||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 0.8.sw,
|
width: 0.8.sw,
|
||||||
@ -81,8 +80,6 @@ class RenameDialogState extends State<RenameDialog> {
|
|||||||
color: const Color(0xFFf9f9fc),
|
color: const Color(0xFFf9f9fc),
|
||||||
borderRadius: BorderRadius.circular(8).r,
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
),
|
),
|
||||||
child: FocusScope(
|
|
||||||
canRequestFocus: true,
|
|
||||||
child: TextField(
|
child: TextField(
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
controller: _textEditingController,
|
controller: _textEditingController,
|
||||||
@ -106,7 +103,6 @@ class RenameDialogState extends State<RenameDialog> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const DividerWidget(),
|
const DividerWidget(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 52.h,
|
height: 52.h,
|
||||||
|
|||||||
@ -140,7 +140,7 @@ class PlayerController extends GetxController {
|
|||||||
isPlaying.value = false;
|
isPlaying.value = false;
|
||||||
isCompleted.value = true;
|
isCompleted.value = true;
|
||||||
positionValue.value = 0.0;
|
positionValue.value = 0.0;
|
||||||
duration.value = Duration.zero;
|
// duration.value = Duration.zero;
|
||||||
positionDuration.value = Duration.zero;
|
positionDuration.value = Duration.zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:ffmpeg_kit_flutter_audio/ffmpeg_kit.dart';
|
import 'package:ffmpeg_kit_flutter_audio/ffmpeg_kit.dart';
|
||||||
import 'package:ffmpeg_kit_flutter_audio/ffmpeg_session.dart';
|
import 'package:ffmpeg_kit_flutter_audio/ffmpeg_session.dart';
|
||||||
import 'package:ffmpeg_kit_flutter_audio/return_code.dart';
|
import 'package:ffmpeg_kit_flutter_audio/return_code.dart';
|
||||||
@ -14,6 +16,7 @@ import 'package:tone_snap/utils/file_util.dart';
|
|||||||
import 'package:tone_snap/utils/local_path_util.dart';
|
import 'package:tone_snap/utils/local_path_util.dart';
|
||||||
import 'package:tone_snap/utils/log_print.dart';
|
import 'package:tone_snap/utils/log_print.dart';
|
||||||
import 'package:tone_snap/utils/num_util.dart';
|
import 'package:tone_snap/utils/num_util.dart';
|
||||||
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class ChangeVoiceController extends GetxController {
|
class ChangeVoiceController extends GetxController {
|
||||||
var timberList = [
|
var timberList = [
|
||||||
@ -41,9 +44,9 @@ class ChangeVoiceController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() async {
|
||||||
playerController.setSpeed(1.0);
|
if (Platform.isAndroid) await playerController.setPitch(1.0);
|
||||||
playerController.setPitch(1.0);
|
await playerController.setSpeed(1.0);
|
||||||
playerController.stopPlay();
|
playerController.stopPlay();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
@ -54,7 +57,7 @@ class ChangeVoiceController extends GetxController {
|
|||||||
}
|
}
|
||||||
toneValue.value = item.check ? item.tone : 1.0;
|
toneValue.value = item.check ? item.tone : 1.0;
|
||||||
soundSpeedValue.value = item.check ? item.soundSpeed : 1.0;
|
soundSpeedValue.value = item.check ? item.soundSpeed : 1.0;
|
||||||
playerController.setPitch(toneValue.value);
|
if (Platform.isAndroid) playerController.setPitch(toneValue.value);
|
||||||
playerController.setSpeed(soundSpeedValue.value);
|
playerController.setSpeed(soundSpeedValue.value);
|
||||||
timberList.refresh();
|
timberList.refresh();
|
||||||
}
|
}
|
||||||
@ -72,7 +75,7 @@ class ChangeVoiceController extends GetxController {
|
|||||||
var item = timberList.firstWhereOrNull((e) => e.check);
|
var item = timberList.firstWhereOrNull((e) => e.check);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
toneValue.value = value;
|
toneValue.value = value;
|
||||||
playerController.setPitch(toneValue.value);
|
if (Platform.isAndroid) playerController.setPitch(toneValue.value);
|
||||||
if (item?.tone.toString() != NumUtil.formatNum(value)) {
|
if (item?.tone.toString() != NumUtil.formatNum(value)) {
|
||||||
item?.check = false;
|
item?.check = false;
|
||||||
timberList.refresh();
|
timberList.refresh();
|
||||||
@ -110,18 +113,20 @@ class ChangeVoiceController extends GetxController {
|
|||||||
int index = timberList.indexOf(timber);
|
int index = timberList.indexOf(timber);
|
||||||
// Rapper
|
// Rapper
|
||||||
// filter = ",aecho=0.8:0.88:60:0.4,areverb=50:50:100:100:0.5:0.5";
|
// filter = ",aecho=0.8:0.88:60:0.4,areverb=50:50:100:100:0.5:0.5";
|
||||||
|
if (index == 4) filter = ",afftdn=nf=-30";
|
||||||
filter = index == 4 ? ",afftdn=nf=-30" : ",aresample=44100";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取采样率
|
||||||
|
String sampleRate = await _getSampleRate() ?? '24000';
|
||||||
|
|
||||||
// 构建 FFmpeg 命令
|
// 构建 FFmpeg 命令
|
||||||
final String command = '-i $filePath -af "asetrate=44100*${toneValue.value},atempo=${soundSpeedValue.value}$filter" $outputPath';
|
final String command = '-i $filePath -af "asetrate=$sampleRate*${toneValue.value},atempo=${soundSpeedValue.value}$filter" $outputPath';
|
||||||
|
|
||||||
// 执行 FFmpeg 命令
|
// 执行 FFmpeg 命令
|
||||||
FFmpegSession session = await FFmpegKit.execute(command);
|
FFmpegSession session = await FFmpegKit.execute(command);
|
||||||
|
|
||||||
// 检查执行结果
|
// 获取执行结果
|
||||||
ReturnCode? returnCode = await session.getReturnCode();
|
final returnCode = await session.getReturnCode();
|
||||||
if (ReturnCode.isSuccess(returnCode)) {
|
if (ReturnCode.isSuccess(returnCode)) {
|
||||||
LogPrint.d('Audio processing successful');
|
LogPrint.d('Audio processing successful');
|
||||||
try {
|
try {
|
||||||
@ -142,6 +147,28 @@ class ChangeVoiceController extends GetxController {
|
|||||||
BaseEasyLoading.toast('Audio processing failed');
|
BaseEasyLoading.toast('Audio processing failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取音频的采样率
|
||||||
|
Future<String?> _getSampleRate() async {
|
||||||
|
String? sampleRate;
|
||||||
|
String ffmpegCommand = '-i $filePath';
|
||||||
|
FFmpegSession session = await FFmpegKit.execute(ffmpegCommand);
|
||||||
|
final output = await session.getOutput();
|
||||||
|
if (ObjUtil.isNotEmpty(output)) {
|
||||||
|
// 使用正则表达式提取采样率
|
||||||
|
final regex = RegExp(r'(\d+) Hz');
|
||||||
|
final match = regex.firstMatch(output!);
|
||||||
|
if (match != null) {
|
||||||
|
sampleRate = match.group(1);
|
||||||
|
LogPrint.d('采样率: $sampleRate Hz');
|
||||||
|
} else {
|
||||||
|
LogPrint.e('未找到采样率');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogPrint.e('获取采样率失败');
|
||||||
|
}
|
||||||
|
return sampleRate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Timber {
|
class Timber {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -150,6 +152,8 @@ class ChangeVoiceView extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 24).w,
|
padding: const EdgeInsets.symmetric(horizontal: 24).w,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i == 0 ? 'Tone' : 'Sound speed',
|
i == 0 ? 'Tone' : 'Sound speed',
|
||||||
@ -159,6 +163,22 @@ class ChangeVoiceView extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Visibility(
|
||||||
|
visible: i == 0 && Platform.isIOS,
|
||||||
|
child: Text(
|
||||||
|
' (Only take effect after saving)',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
SizedBox(height: 16.h),
|
SizedBox(height: 16.h),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -188,15 +208,18 @@ class ChangeVoiceView extends StatelessWidget {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 16.w),
|
SizedBox(width: 20.w),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return Text(
|
return SizedBox(
|
||||||
|
width: 40.w,
|
||||||
|
child: Text(
|
||||||
i == 0 ? NumUtil.formatNum(controller.toneValue.value) : NumUtil.formatNum(controller.soundSpeedValue.value),
|
i == 0 ? NumUtil.formatNum(controller.toneValue.value) : NumUtil.formatNum(controller.soundSpeedValue.value),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: const Color(0x73000000),
|
color: const Color(0x73000000),
|
||||||
fontSize: 16.sp,
|
fontSize: 16.sp,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -23,6 +23,10 @@ class FavouriteController extends GetxController {
|
|||||||
|
|
||||||
void getData() {
|
void getData() {
|
||||||
voiceList.value = FavoriteData().getList().reversed.toList();
|
voiceList.value = FavoriteData().getList().reversed.toList();
|
||||||
|
_refreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _refreshList() {
|
||||||
viewState.value = voiceList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
viewState.value = voiceList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
voiceList.refresh();
|
voiceList.refresh();
|
||||||
}
|
}
|
||||||
@ -43,7 +47,7 @@ class FavouriteController extends GetxController {
|
|||||||
voiceList.refresh();
|
voiceList.refresh();
|
||||||
|
|
||||||
// 若 item 和当前正播放的 item 是同个对象,则需要同步修改
|
// 若 item 和当前正播放的 item 是同个对象,则需要同步修改
|
||||||
if (identical(item, InitialController.to.currentPlayVoiceModel.value)) {
|
if (item.path == InitialController.to.currentPlayVoiceModel.value?.path) {
|
||||||
InitialController.to.currentPlayVoiceModel.update((e) => e?.name = value);
|
InitialController.to.currentPlayVoiceModel.update((e) => e?.name = value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -60,13 +64,13 @@ class FavouriteController extends GetxController {
|
|||||||
BaseEasyLoading.loading();
|
BaseEasyLoading.loading();
|
||||||
await item.delete();
|
await item.delete();
|
||||||
voiceList.remove(item);
|
voiceList.remove(item);
|
||||||
voiceList.refresh();
|
|
||||||
BaseEasyLoading.toast('Removed');
|
BaseEasyLoading.toast('Removed');
|
||||||
|
|
||||||
// 若 item 和当前正播放的 item 是同个对象,则需要同步修改
|
// 若 item 和当前正播放的 item 是同个对象,则需要同步修改
|
||||||
if (identical(item, InitialController.to.currentPlayVoiceModel.value)) {
|
if (item.path == InitialController.to.currentPlayVoiceModel.value?.path) {
|
||||||
InitialController.to.isFavourite.value = false;
|
InitialController.to.isFavourite.value = false;
|
||||||
}
|
}
|
||||||
|
_refreshList();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ class FavouriteView extends GetView<FavouriteController> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.find<FavouriteController>();
|
Get.put(FavouriteController());
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
return ViewStateWidget(
|
return ViewStateWidget(
|
||||||
viewState: controller.viewState.value,
|
viewState: controller.viewState.value,
|
||||||
|
|||||||
@ -40,8 +40,9 @@ class InitialController extends GetxController {
|
|||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBottomAppBarItemChanged(int index) {
|
Future<void> onBottomAppBarItemChanged(int index) async {
|
||||||
if (index == 1) {
|
if (index == 1) {
|
||||||
|
await PlayerController.to.stopPlay();
|
||||||
Get.toNamed(AppRoutes.uploadMethod);
|
Get.toNamed(AppRoutes.uploadMethod);
|
||||||
} else {
|
} else {
|
||||||
if (index == 2) _refreshMe();
|
if (index == 2) _refreshMe();
|
||||||
@ -66,26 +67,24 @@ class InitialController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onTapFavourite() async {
|
Future<void> onTapFavourite() async {
|
||||||
if (currentPlayVoiceModel.value != null) {
|
|
||||||
if (isFavourite.value) {
|
if (isFavourite.value) {
|
||||||
getIsFavouriteModel()?.delete();
|
getIsFavouriteModel()?.delete();
|
||||||
isFavourite.value = false;
|
isFavourite.value = false;
|
||||||
} else {
|
} else {
|
||||||
|
if (currentPlayVoiceModel.value != null) {
|
||||||
await FavoriteData().addData(currentPlayVoiceModel.value!.copyWith());
|
await FavoriteData().addData(currentPlayVoiceModel.value!.copyWith());
|
||||||
isFavourite.value = true;
|
isFavourite.value = true;
|
||||||
}
|
}
|
||||||
_refreshMe();
|
|
||||||
}
|
}
|
||||||
|
_refreshMe();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 刷新我的页面
|
/// 刷新我的页面
|
||||||
void _refreshMe() {
|
void _refreshMe() {
|
||||||
if (Get.isRegistered<MeController>()) {
|
|
||||||
if (Get.isRegistered<MyVoiceController>()) MyVoiceController.to.getData();
|
if (Get.isRegistered<MyVoiceController>()) MyVoiceController.to.getData();
|
||||||
if (Get.isRegistered<FavouriteController>()) FavouriteController.to.getData();
|
if (Get.isRegistered<FavouriteController>()) FavouriteController.to.getData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class PageItem {
|
class PageItem {
|
||||||
late final List<String> icons;
|
late final List<String> icons;
|
||||||
|
|||||||
@ -23,6 +23,10 @@ class MyVoiceController extends GetxController {
|
|||||||
|
|
||||||
void getData() {
|
void getData() {
|
||||||
voiceList.value = MyVoiceData().getList().reversed.toList();
|
voiceList.value = MyVoiceData().getList().reversed.toList();
|
||||||
|
_refreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _refreshList() {
|
||||||
viewState.value = voiceList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
viewState.value = voiceList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
voiceList.refresh();
|
voiceList.refresh();
|
||||||
}
|
}
|
||||||
@ -60,8 +64,8 @@ class MyVoiceController extends GetxController {
|
|||||||
BaseEasyLoading.loading();
|
BaseEasyLoading.loading();
|
||||||
await item.delete();
|
await item.delete();
|
||||||
voiceList.remove(item);
|
voiceList.remove(item);
|
||||||
voiceList.refresh();
|
|
||||||
BaseEasyLoading.toast('Removed');
|
BaseEasyLoading.toast('Removed');
|
||||||
|
_refreshList();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ class MyVoiceView extends GetView<MyVoiceController> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.find<MyVoiceController>();
|
Get.put(MyVoiceController());
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
return ViewStateWidget(
|
return ViewStateWidget(
|
||||||
viewState: controller.viewState.value,
|
viewState: controller.viewState.value,
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class PlaySoundController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void goChangeVoice() async {
|
void goChangeVoice() async {
|
||||||
|
await PlayerController.to.stopPlay();
|
||||||
Get.toNamed(AppRoutes.changeVoice, arguments: voiceModel.path);
|
Get.toNamed(AppRoutes.changeVoice, arguments: voiceModel.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,14 +5,12 @@ import 'package:file_picker/file_picker.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:tone_snap/components/base_easyloading.dart';
|
import 'package:tone_snap/components/base_easyloading.dart';
|
||||||
import 'package:tone_snap/controllers/player_controller.dart';
|
|
||||||
import 'package:tone_snap/routes/app_routes.dart';
|
import 'package:tone_snap/routes/app_routes.dart';
|
||||||
import 'package:tone_snap/utils/permission_util.dart';
|
import 'package:tone_snap/utils/permission_util.dart';
|
||||||
|
|
||||||
class UploadMethodController extends GetxController {
|
class UploadMethodController extends GetxController {
|
||||||
|
|
||||||
Future<void> goRecordSound() async {
|
Future<void> goRecordSound() async {
|
||||||
await PlayerController.to.pausePlay();
|
|
||||||
Get.toNamed(AppRoutes.recordSound);
|
Get.toNamed(AppRoutes.recordSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@ class AppPages {
|
|||||||
GetPage(
|
GetPage(
|
||||||
name: AppRoutes.initial,
|
name: AppRoutes.initial,
|
||||||
page: () => InitialView(),
|
page: () => InitialView(),
|
||||||
bindings: [InitialBinding(), HomeBinding(), MeBinding(), SettingsBinding(), MyVoiceBinding(), FavouriteBinding()],
|
bindings: [InitialBinding(), HomeBinding(), MeBinding(), SettingsBinding()],
|
||||||
),
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: AppRoutes.uploadMethod,
|
name: AppRoutes.uploadMethod,
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class LocalPathUtil {
|
|||||||
|
|
||||||
/// 获取录音文件保存目录
|
/// 获取录音文件保存目录
|
||||||
static Future<Directory> getRecordingsDir() async {
|
static Future<Directory> getRecordingsDir() async {
|
||||||
Directory cacheDir = await getTemporaryDirectory();
|
Directory cacheDir = await getTemporaryPath();
|
||||||
Directory recordingsDir = Directory('${cacheDir.path}/recordings');
|
Directory recordingsDir = Directory('${cacheDir.path}/recordings');
|
||||||
bool exist = await recordingsDir.exists();
|
bool exist = await recordingsDir.exists();
|
||||||
if (!exist) {
|
if (!exist) {
|
||||||
@ -41,7 +41,7 @@ class LocalPathUtil {
|
|||||||
|
|
||||||
/// 获取录音文件保存目录
|
/// 获取录音文件保存目录
|
||||||
static Future<Directory> getAssetsDir() async {
|
static Future<Directory> getAssetsDir() async {
|
||||||
Directory cacheDir = await getTemporaryDirectory();
|
Directory cacheDir = await getTemporaryPath();
|
||||||
Directory recordingsDir = Directory('${cacheDir.path}/assets');
|
Directory recordingsDir = Directory('${cacheDir.path}/assets');
|
||||||
bool exist = await recordingsDir.exists();
|
bool exist = await recordingsDir.exists();
|
||||||
if (!exist) {
|
if (!exist) {
|
||||||
@ -53,7 +53,7 @@ class LocalPathUtil {
|
|||||||
|
|
||||||
/// 获取音频文件变声后输出目录
|
/// 获取音频文件变声后输出目录
|
||||||
static Future<Directory> getVoiceChangeOutputDir() async {
|
static Future<Directory> getVoiceChangeOutputDir() async {
|
||||||
Directory cacheDir = await getTemporaryDirectory();
|
Directory cacheDir = await getDocumentsPath();
|
||||||
Directory recordingsDir = Directory('${cacheDir.path}/change_voice');
|
Directory recordingsDir = Directory('${cacheDir.path}/change_voice');
|
||||||
bool exist = await recordingsDir.exists();
|
bool exist = await recordingsDir.exists();
|
||||||
if (!exist) {
|
if (!exist) {
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
// Date: 2024/5/10
|
// Date: 2024/5/10
|
||||||
// Description: 权限处理
|
// Description: 权限处理
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:tone_snap/components/dialog/remind_dialog.dart';
|
import 'package:tone_snap/components/dialog/remind_dialog.dart';
|
||||||
@ -32,9 +34,9 @@ class PermissionUtil {
|
|||||||
// 允许状态
|
// 允许状态
|
||||||
case PermissionStatus.granted:
|
case PermissionStatus.granted:
|
||||||
case PermissionStatus.limited:
|
case PermissionStatus.limited:
|
||||||
case PermissionStatus.provisional:
|
|
||||||
return true;
|
return true;
|
||||||
// 永久拒绝 活动限制
|
// 永久拒绝
|
||||||
|
case PermissionStatus.provisional:
|
||||||
case PermissionStatus.restricted:
|
case PermissionStatus.restricted:
|
||||||
case PermissionStatus.permanentlyDenied:
|
case PermissionStatus.permanentlyDenied:
|
||||||
_showFailedDialog(newPermissionList, isPermanentlyDenied: true);
|
_showFailedDialog(newPermissionList, isPermanentlyDenied: true);
|
||||||
@ -59,45 +61,42 @@ class PermissionUtil {
|
|||||||
return currentPermissionStatus;
|
return currentPermissionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 请求位置权限
|
||||||
static Future<bool> checkLocationAlways() async {
|
static Future<bool> checkLocationAlways() async {
|
||||||
// 获取前置状态
|
// 获取前置状态
|
||||||
// Android没有这一步 ios会先访问这个再访问其他的
|
// Android没有这一步 ios会先访问这个再访问其他的
|
||||||
PermissionStatus status = PermissionStatus.granted;
|
PermissionStatus status1 = PermissionStatus.granted;
|
||||||
status = await _checkSinglePermission(Permission.locationWhenInUse);
|
status1 = await _checkSinglePermission(Permission.locationWhenInUse);
|
||||||
|
|
||||||
// 获取第二个状态
|
// 获取第二个状态
|
||||||
PermissionStatus status2 = PermissionStatus.denied;
|
PermissionStatus status2 = PermissionStatus.denied;
|
||||||
|
|
||||||
// 如果前置状态为成功才能执行获取第二个状态
|
// 如果前置状态为成功才能执行获取第二个状态
|
||||||
if (status.isGranted) {
|
if (status1.isGranted) {
|
||||||
status2 = await _checkSinglePermission(Permission.locationAlways);
|
status2 = await _checkSinglePermission(Permission.locationAlways);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果两个都成功那么就返回成功
|
// 如果两个都成功那么就返回成功
|
||||||
if (status.isGranted && status2.isGranted) {
|
if (status1.isGranted && status2.isGranted) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// 如果有一个拒绝那么就失败了
|
|
||||||
} else if (status.isDenied || status2.isDenied) {
|
|
||||||
_showFailedDialog([Permission.locationWhenInUse, Permission.locationAlways]);
|
|
||||||
} else {
|
} else {
|
||||||
|
// 如果有一个拒绝那么就失败了
|
||||||
_showFailedDialog([Permission.locationWhenInUse, Permission.locationAlways],
|
_showFailedDialog([Permission.locationWhenInUse, Permission.locationAlways],
|
||||||
isPermanentlyDenied: true,
|
isPermanentlyDenied: Platform.isIOS ? true : false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 请求单个权限
|
||||||
static _checkSinglePermission(Permission permission) async {
|
static _checkSinglePermission(Permission permission) async {
|
||||||
// 获取当前状态
|
// 获取当前状态
|
||||||
PermissionStatus status = await permission.status;
|
PermissionStatus status = await permission.status;
|
||||||
PermissionStatus currentPermissionStatus = PermissionStatus.granted;
|
PermissionStatus currentPermissionStatus = PermissionStatus.granted;
|
||||||
|
|
||||||
// 如果它状态不是允许那么就去获取
|
// 如果它状态不是允许那么就去获取
|
||||||
if (!status.isGranted) {
|
if (!status.isGranted) {
|
||||||
currentPermissionStatus = await _requestPermission([permission]);
|
currentPermissionStatus = await _requestPermission([permission]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回最终状态
|
// 返回最终状态
|
||||||
return currentPermissionStatus;
|
return currentPermissionStatus;
|
||||||
}
|
}
|
||||||
@ -107,7 +106,7 @@ class PermissionUtil {
|
|||||||
Get.dialog(
|
Get.dialog(
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
RemindDialog(
|
RemindDialog(
|
||||||
content: await _getInstructions(permissionList),
|
content: await _getDescription(permissionList),
|
||||||
confirmText: isPermanentlyDenied ? 'Go open' : 'Confirm',
|
confirmText: isPermanentlyDenied ? 'Go open' : 'Confirm',
|
||||||
confirmOnTap: () {
|
confirmOnTap: () {
|
||||||
if (isPermanentlyDenied) {
|
if (isPermanentlyDenied) {
|
||||||
@ -120,20 +119,16 @@ class PermissionUtil {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取权限使用说明
|
/// 获取权限描述
|
||||||
static Future<String> _getInstructions(List<Permission> permissionList) async {
|
static Future<String> _getDescription(List<Permission> permissionList) async {
|
||||||
late Permission failedPermission;
|
late Permission failedPermission;
|
||||||
|
|
||||||
// 遍历当前权限申请列表
|
|
||||||
for (Permission permission in permissionList) {
|
for (Permission permission in permissionList) {
|
||||||
PermissionStatus status = await permission.status;
|
if (!await permission.status.isGranted) {
|
||||||
|
|
||||||
// 如果不是允许状态就添加到新的申请列表中
|
|
||||||
if (!status.isGranted || !status.isLimited) {
|
|
||||||
failedPermission = permission;
|
failedPermission = permission;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String description = '';
|
String description = '';
|
||||||
if (failedPermission == Permission.microphone) {
|
if (failedPermission == Permission.microphone) {
|
||||||
description = 'We need to access the microphone to record or select audio files.';
|
description = 'We need to access the microphone to record or select audio files.';
|
||||||
|
|||||||
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.2+4
|
version: 1.0.2+5
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.4.1 <4.0.0'
|
sdk: '>=3.4.1 <4.0.0'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user