From 0c650bf14634f19673a40927e2c219d46846cfa0 Mon Sep 17 00:00:00 2001 From: huynguyennovem Date: Sun, 7 May 2023 12:38:18 +0700 Subject: [PATCH] Fixes #56: Can not host non-media files on Android 11 and above Signed-off-by: huynguyennovem --- android/app/src/main/AndroidManifest.xml | 2 ++ lib/ui/server/server_widget.dart | 15 +++++++++ lib/util/utility_functions.dart | 39 ++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 26ffe81..81a1316 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,8 @@ + + { return; } + // Additional check for Android limitation (Android 11 and above, + // see: https://developer.android.com/training/data-storage/manage-all-files#all-files-access-google-play) + final needGrantPermission = await UtilityFunctions.isNeedAccessAllFileStoragePermission; + if(needGrantPermission) { + final isPermissionGranted = await UtilityFunctions.checkManageExternalStoragePermission( + onPermanentlyDenied: () => context.showOpenSettingsDialog(), + ); + if (!isPermissionGranted) { + if (mounted) { + context.showSnackbar('Need storage permission to continue'); + } + return; + } + } + // Router instance to handler requests. // Use shelf_router.Router(notFoundHandler: _staticHandler(dir.path)) // as a fallback for static handler (may use this later) diff --git a/lib/util/utility_functions.dart b/lib/util/utility_functions.dart index 144b68e..71b61a9 100644 --- a/lib/util/utility_functions.dart +++ b/lib/util/utility_functions.dart @@ -83,6 +83,37 @@ class UtilityFunctions { } } + static Future checkManageExternalStoragePermission({ + Function? onGranted, + Function? onDenied, + Function? onPermanentlyDenied, + }) async { + final status = await Permission.manageExternalStorage.status; + switch (status) { + case PermissionStatus.granted: + onGranted?.call(); + return true; + case PermissionStatus.permanentlyDenied: + onPermanentlyDenied?.call(); + return false; + default: + { + final requested = await Permission.manageExternalStorage.request(); + switch (requested) { + case PermissionStatus.granted: + onGranted?.call(); + return true; + case PermissionStatus.permanentlyDenied: + onPermanentlyDenied?.call(); + return false; + default: + onDenied?.call(); + return false; + } + } + } + } + static Future get androidSDKVersion async { DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); final androidInfo = await deviceInfoPlugin.androidInfo; @@ -97,6 +128,14 @@ class UtilityFunctions { return 28 >= sdkVersion; } + static Future get isNeedAccessAllFileStoragePermission async { + if (!Platform.isAndroid) { + return false; + } + final sdkVersion = await androidSDKVersion; + return 30 <= sdkVersion; + } + /// Parse ip (v4) address with [rawInput] which has format: /// ip:port (eg: 192.168.0.100:8080) static Tuple2 parseIPAddress(String rawInput) {