fix: permission dialog (#1799)

* don't request photo permission on A13+

saving to system album requires no additional permission

* fix permission dialog

* update

Signed-off-by: dom <githubaccount56556@proton.me>

---------

Co-authored-by: dom <githubaccount56556@proton.me>
This commit is contained in:
KoishiMoe
2026-01-09 11:07:39 +08:00
committed by GitHub
parent fffce10b31
commit dbc11c36df
6 changed files with 29 additions and 58 deletions

View File

@@ -117,10 +117,7 @@ void imageSaveDialog({
iconBtn( iconBtn(
tooltip: '保存封面图', tooltip: '保存封面图',
onPressed: () async { onPressed: () async {
bool saveStatus = await ImageUtils.downloadImg( bool saveStatus = await ImageUtils.downloadImg([cover]);
context,
[cover],
);
if (saveStatus) { if (saveStatus) {
SmartDialog.dismiss(); SmartDialog.dismiss();
} }

View File

@@ -437,10 +437,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
ListTile( ListTile(
onTap: () { onTap: () {
Get.back(); Get.back();
ImageUtils.downloadImg( ImageUtils.downloadImg([item.url]);
this.context,
[item.url],
);
}, },
dense: true, dense: true,
title: const Text('保存图片', style: TextStyle(fontSize: 14)), title: const Text('保存图片', style: TextStyle(fontSize: 14)),
@@ -459,7 +456,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
onTap: () { onTap: () {
Get.back(); Get.back();
ImageUtils.downloadImg( ImageUtils.downloadImg(
this.context,
widget.sources.map((item) => item.url).toList(), widget.sources.map((item) => item.url).toList(),
); );
}, },
@@ -471,7 +467,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
onTap: () { onTap: () {
Get.back(); Get.back();
ImageUtils.downloadLivePhoto( ImageUtils.downloadLivePhoto(
context: this.context,
url: item.url, url: item.url,
liveUrl: item.liveUrl!, liveUrl: item.liveUrl!,
width: item.width!, width: item.width!,
@@ -503,7 +498,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
), ),
PopupMenuItem( PopupMenuItem(
height: 42, height: 42,
onTap: () => ImageUtils.downloadImg(context, [item.url]), onTap: () => ImageUtils.downloadImg([item.url]),
child: const Text('保存图片', style: TextStyle(fontSize: 14)), child: const Text('保存图片', style: TextStyle(fontSize: 14)),
), ),
PopupMenuItem( PopupMenuItem(
@@ -515,7 +510,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
PopupMenuItem( PopupMenuItem(
height: 42, height: 42,
onTap: () => ImageUtils.downloadLivePhoto( onTap: () => ImageUtils.downloadLivePhoto(
context: context,
url: item.url, url: item.url,
liveUrl: item.liveUrl!, liveUrl: item.liveUrl!,
width: item.width!, width: item.width!,

View File

@@ -291,7 +291,7 @@ class _SavePanelState extends State<SavePanel> {
Future<void> _onSaveOrSharePic([bool isShare = false]) async { Future<void> _onSaveOrSharePic([bool isShare = false]) async {
if (!isShare && PlatformUtils.isMobile) { if (!isShare && PlatformUtils.isMobile) {
if (mounted && !await ImageUtils.checkPermissionDependOnSdkInt(context)) { if (mounted && !await ImageUtils.checkPermissionDependOnSdkInt()) {
return; return;
} }
} }

View File

@@ -1312,10 +1312,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
), ),
if (videoDetailController.cover.value.isNotEmpty) if (videoDetailController.cover.value.isNotEmpty)
PopupMenuItem( PopupMenuItem(
onTap: () => ImageUtils.downloadImg( onTap: () =>
context, ImageUtils.downloadImg([videoDetailController.cover.value]),
[videoDetailController.cover.value],
),
child: const Text('保存封面'), child: const Text('保存封面'),
), ),
if (!videoDetailController.isFileSource && videoDetailController.isUgc) if (!videoDetailController.isFileSource && videoDetailController.isUgc)

View File

@@ -407,10 +407,9 @@ class HeaderControlState extends State<HeaderControl>
dense: true, dense: true,
onTap: () { onTap: () {
Get.back(); Get.back();
ImageUtils.downloadImg( ImageUtils.downloadImg([
context, widget.videoDetailCtr.cover.value,
[widget.videoDetailCtr.cover.value], ]);
);
}, },
leading: const Icon(Icons.image_outlined, size: 20), leading: const Icon(Icons.image_outlined, size: 20),
title: const Text('保存封面', style: titleStyle), title: const Text('保存封面', style: titleStyle),

View File

@@ -50,22 +50,24 @@ abstract final class ImageUtils {
} }
// 获取存储权限 // 获取存储权限
static Future<bool> requestStoragePer(BuildContext context) async { static Future<bool> requestPer() async {
await Permission.storage.request(); final status = Platform.isAndroid
PermissionStatus status = await Permission.storage.status; ? await Permission.storage.request()
: await Permission.photos.request();
if (status == PermissionStatus.denied || if (status == PermissionStatus.denied ||
status == PermissionStatus.permanentlyDenied) { status == PermissionStatus.permanentlyDenied) {
if (!context.mounted) return false; SmartDialog.show(
showDialog(
context: context,
builder: (context) { builder: (context) {
return const AlertDialog( return AlertDialog(
title: Text('提示'), title: const Text('提示'),
content: Text('存储权限未授权'), content: const Text('存储权限未授权'),
actions: [ actions: [
TextButton( TextButton(
onPressed: openAppSettings, onPressed: () {
child: Text('去授权'), SmartDialog.dismiss();
openAppSettings();
},
child: const Text('去授权'),
), ),
], ],
); );
@@ -77,42 +79,25 @@ abstract final class ImageUtils {
} }
} }
// 获取相册权限 static Future<bool> checkPermissionDependOnSdkInt() async {
static Future<bool> requestPhotoPer() async {
await Permission.photos.request();
PermissionStatus status = await Permission.photos.status;
if (status == PermissionStatus.denied ||
status == PermissionStatus.permanentlyDenied) {
return false;
} else {
return true;
}
}
static Future<bool> checkPermissionDependOnSdkInt(
BuildContext context,
) async {
if (Platform.isAndroid) { if (Platform.isAndroid) {
if (await Utils.sdkInt <= 32) { if (await Utils.sdkInt < 29) {
if (!context.mounted) return false; return requestPer();
return requestStoragePer(context);
} else { } else {
return requestPhotoPer(); return true;
} }
} }
return requestStoragePer(context); return requestPer();
} }
static Future<bool> downloadLivePhoto({ static Future<bool> downloadLivePhoto({
required BuildContext context,
required String url, required String url,
required String liveUrl, required String liveUrl,
required int width, required int width,
required int height, required int height,
}) async { }) async {
try { try {
if (PlatformUtils.isMobile && if (PlatformUtils.isMobile && !await checkPermissionDependOnSdkInt()) {
!await checkPermissionDependOnSdkInt(context)) {
return false; return false;
} }
if (!silentDownImg) SmartDialog.showLoading(msg: '正在下载'); if (!silentDownImg) SmartDialog.showLoading(msg: '正在下载');
@@ -167,12 +152,10 @@ abstract final class ImageUtils {
} }
static Future<bool> downloadImg( static Future<bool> downloadImg(
BuildContext context,
List<String> imgList, [ List<String> imgList, [
CacheManager? manager, CacheManager? manager,
]) async { ]) async {
if (PlatformUtils.isMobile && if (PlatformUtils.isMobile && !await checkPermissionDependOnSdkInt()) {
!await checkPermissionDependOnSdkInt(context)) {
return false; return false;
} }
CancelToken? cancelToken; CancelToken? cancelToken;