From a074ff5dd6b4bb30ebb662ccb1e351dbe0319a4d Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Thu, 17 Mar 2022 11:17:44 +0900 Subject: [PATCH] various edge case fixes --- .../aves/channel/calls/DebugHandler.kt | 7 +++++-- .../streams/StorageAccessStreamHandler.kt | 21 ++++++++++++++----- .../thibault/aves/utils/StorageUtils.kt | 7 ++++--- lib/widgets/dialogs/add_shortcut_dialog.dart | 6 +++++- .../dialogs/entry_editors/rename_dialog.dart | 6 +++++- .../filter_editors/create_album_dialog.dart | 6 +++++- .../filter_editors/rename_album_dialog.dart | 8 +++++-- .../viewer/overlay/thumbnail_preview.dart | 6 +++++- 8 files changed, 51 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt index 82c84520e..6f555b18e 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt @@ -33,7 +33,10 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.util.PathUtils -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch import org.beyka.tiffbitmapfactory.TiffBitmapFactory import java.io.IOException @@ -84,7 +87,7 @@ class DebugHandler(private val context: Context) : MethodCallHandler { } }.mapValues { it.value?.path }.toMutableMap() dirs["externalCacheDirs"] = context.externalCacheDirs.joinToString { it.path } - dirs["externalFilesDirs"] = context.getExternalFilesDirs(null).joinToString { it.path } + dirs["externalFilesDirs"] = context.getExternalFilesDirs(null).joinToString { it?.path ?: "null" } // used by flutter plugin `path_provider` dirs.putAll( diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/StorageAccessStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/StorageAccessStreamHandler.kt index 0bc307f92..1e742403c 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/StorageAccessStreamHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/StorageAccessStreamHandler.kt @@ -13,6 +13,7 @@ import deckers.thibault.aves.PendingStorageAccessResultHandler import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.MimeTypes import deckers.thibault.aves.utils.PermissionManager +import deckers.thibault.aves.utils.StorageUtils import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel.EventSink import kotlinx.coroutines.CoroutineScope @@ -80,6 +81,11 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any? return } + if (uris.any { !StorageUtils.isMediaStoreContentUri(it) }) { + error("requestMediaFileAccess-nonmediastore", "request is only valid for Media Store content URIs, uris=$uris", null) + return + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { error("requestMediaFileAccess-unsupported", "media file bulk access is not allowed before Android R", null) return @@ -148,12 +154,17 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any? fun onGranted(uri: Uri) { ioScope.launch { - activity.contentResolver.openInputStream(uri)?.use { input -> - val buffer = ByteArray(BUFFER_SIZE) - var len: Int - while (input.read(buffer).also { len = it } != -1) { - success(buffer.copyOf(len)) + try { + activity.contentResolver.openInputStream(uri)?.use { input -> + val buffer = ByteArray(BUFFER_SIZE) + var len: Int + while (input.read(buffer).also { len = it } != -1) { + success(buffer.copyOf(len)) + } } + } catch (e: Exception) { + Log.e(LOG_TAG, "failed to open input stream for uri=$uri", e) + } finally { endOfStream() } } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt index 03d158403..b2980e11b 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt @@ -36,13 +36,14 @@ object StorageUtils { const val TRASH_PATH_PLACEHOLDER = "#trash" private fun isAppFile(context: Context, path: String): Boolean { - return context.getExternalFilesDirs(null).any { filesDir -> path.startsWith(filesDir.path) } + val filesDirs = context.getExternalFilesDirs(null).filterNotNull() + return filesDirs.any { path.startsWith(it.path) } } private fun appExternalFilesDirFor(context: Context, path: String): File? { - val filesDirs = context.getExternalFilesDirs(null) + val filesDirs = context.getExternalFilesDirs(null).filterNotNull() val volumePath = getVolumePath(context, path) - return volumePath?.let { filesDirs.firstOrNull { it.startsWith(volumePath) } } ?: filesDirs.first() + return volumePath?.let { filesDirs.firstOrNull { it.startsWith(volumePath) } } ?: filesDirs.firstOrNull() } fun trashDirFor(context: Context, path: String): File? { diff --git a/lib/widgets/dialogs/add_shortcut_dialog.dart b/lib/widgets/dialogs/add_shortcut_dialog.dart index 6316dfa31..480430a31 100644 --- a/lib/widgets/dialogs/add_shortcut_dialog.dart +++ b/lib/widgets/dialogs/add_shortcut_dialog.dart @@ -154,5 +154,9 @@ class _AddShortcutDialogState extends State { _isValidNotifier.value = name.isNotEmpty; } - void _submit(BuildContext context) => Navigator.pop(context, Tuple2(_coverEntry, _nameController.text)); + void _submit(BuildContext context) { + if (_isValidNotifier.value) { + Navigator.pop(context, Tuple2(_coverEntry, _nameController.text)); + } + } } diff --git a/lib/widgets/dialogs/entry_editors/rename_dialog.dart b/lib/widgets/dialogs/entry_editors/rename_dialog.dart index 768d9f11a..fd184473f 100644 --- a/lib/widgets/dialogs/entry_editors/rename_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/rename_dialog.dart @@ -87,5 +87,9 @@ class _RenameEntryDialogState extends State { _isValidNotifier.value = newName.isNotEmpty && !exists; } - void _submit(BuildContext context) => Navigator.pop(context, _nameController.text); + void _submit(BuildContext context) { + if (_isValidNotifier.value) { + Navigator.pop(context, _nameController.text); + } + } } diff --git a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart index e32a949c0..ba5ffc5f1 100644 --- a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart @@ -157,5 +157,9 @@ class _CreateAlbumDialogState extends State { _isValidNotifier.value = newName.isNotEmpty && !exists; } - void _submit(BuildContext context) => Navigator.pop(context, _buildAlbumPath(_nameController.text)); + void _submit(BuildContext context) { + if (_isValidNotifier.value) { + Navigator.pop(context, _buildAlbumPath(_nameController.text)); + } + } } diff --git a/lib/widgets/dialogs/filter_editors/rename_album_dialog.dart b/lib/widgets/dialogs/filter_editors/rename_album_dialog.dart index 288cdaed5..525fb8ff4 100644 --- a/lib/widgets/dialogs/filter_editors/rename_album_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/rename_album_dialog.dart @@ -84,8 +84,12 @@ class _RenameAlbumDialogState extends State { final path = _buildAlbumPath(newName); final exists = newName.isNotEmpty && await FileSystemEntity.type(path) != FileSystemEntityType.notFound; _existsNotifier.value = exists && newName != initialValue; - _isValidNotifier.value = newName.isNotEmpty && !exists; + _isValidNotifier.value = newName.isNotEmpty; } - void _submit(BuildContext context) => Navigator.pop(context, _nameController.text); + void _submit(BuildContext context) { + if (_isValidNotifier.value) { + Navigator.pop(context, _nameController.text); + } + } } diff --git a/lib/widgets/viewer/overlay/thumbnail_preview.dart b/lib/widgets/viewer/overlay/thumbnail_preview.dart index 18f736556..4218fd089 100644 --- a/lib/widgets/viewer/overlay/thumbnail_preview.dart +++ b/lib/widgets/viewer/overlay/thumbnail_preview.dart @@ -62,5 +62,9 @@ class _ViewerThumbnailPreviewState extends State { ); } - void _onScrollerIndexChange() => _debouncer(() => ViewEntryNotification(index: _entryIndexNotifier.value).dispatch(context)); + void _onScrollerIndexChange() => _debouncer(() { + if (mounted) { + ViewEntryNotification(index: _entryIndexNotifier.value).dispatch(context); + } + }); }