diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt index a96f84e90..5ac512f91 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt @@ -340,7 +340,7 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler { } ioScope.launch { - provider.fetchSingle(context, uri, mimeType, object : ImageProvider.ImageOpCallback { + provider.fetchSingle(context, uri, mimeType, false, object : ImageProvider.ImageOpCallback { override fun onSuccess(fields: FieldMap) { resultFields.putAll(fields) result.success(resultFields) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt index 941928831..be5696a03 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt @@ -29,6 +29,7 @@ class MediaFetchObjectHandler(private val context: Context) : MethodCallHandler private fun getEntry(call: MethodCall, result: MethodChannel.Result) { val mimeType = call.argument("mimeType") // MIME type is optional val uri = call.argument("uri")?.let { Uri.parse(it) } + val allowUnsized = call.argument("allowUnsized") ?: false if (uri == null) { result.error("getEntry-args", "missing arguments", null) return @@ -40,7 +41,7 @@ class MediaFetchObjectHandler(private val context: Context) : MethodCallHandler return } - provider.fetchSingle(context, uri, mimeType, object : ImageOpCallback { + provider.fetchSingle(context, uri, mimeType, allowUnsized, object : ImageOpCallback { override fun onSuccess(fields: FieldMap) = result.success(fields) override fun onFailure(throwable: Throwable) = result.error("getEntry-failure", "failed to get entry for uri=$uri mimeType=$mimeType", throwable.message) }) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt index cfa0c34e8..bc45c1c2c 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt @@ -12,7 +12,7 @@ import deckers.thibault.aves.utils.LogUtils import java.io.File internal class FileImageProvider : ImageProvider() { - override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) { + override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, allowUnsized: Boolean, callback: ImageOpCallback) { var mimeType = sourceMimeType if (mimeType == null) { @@ -54,7 +54,7 @@ internal class FileImageProvider : ImageProvider() { } entry.fillPreCatalogMetadata(context) - if (entry.isSized || entry.isSvg || entry.isVideo) { + if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) { callback.onSuccess(entry.toMap()) } else { callback.onFailure(Exception("entry has no size")) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt index d406f12fd..be8dc5270 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt @@ -70,7 +70,7 @@ import java.util.TimeZone import kotlin.math.absoluteValue abstract class ImageProvider { - open fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) { + open fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, allowUnsized: Boolean, callback: ImageOpCallback) { callback.onFailure(UnsupportedOperationException("`fetchSingle` is not supported by this image provider")) } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt index 87c166c28..faa55eef7 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt @@ -89,7 +89,7 @@ class MediaStoreImageProvider : ImageProvider() { // the provided URI can point to the wrong media collection, // e.g. a GIF image with the URI `content://media/external/video/media/[ID]` // so the effective entry URI may not match the provided URI - override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) { + override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, allowUnsized: Boolean, callback: ImageOpCallback) { var found = false val fetched = arrayListOf() val id = uri.tryParseId() diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt index 8fc4e6db0..ffcc2400f 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt @@ -17,7 +17,7 @@ open class UnknownContentProvider : ImageProvider() { open val reliableProviderMimeType: Boolean get() = false - override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) { + override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, allowUnsized: Boolean, callback: ImageOpCallback) { var mimeType = sourceMimeType if (sourceMimeType == null || !reliableProviderMimeType) { // source MIME type may be incorrect, so we get a second opinion if possible @@ -71,7 +71,7 @@ open class UnknownContentProvider : ImageProvider() { } val entry = SourceEntry(fields).fillPreCatalogMetadata(context) - if (entry.isSized || entry.isSvg || entry.isVideo) { + if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) { callback.onSuccess(entry.toMap()) } else { callback.onFailure(Exception("entry has no size")) diff --git a/android/build.gradle b/android/build.gradle index 87bd1337b..90a76ffec 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - agp_version = '8.4.1' // same as `settings.ext.agp_version` in `/android/settings.gradle` + agp_version = '8.5.0' // same as `settings.ext.agp_version` in `/android/settings.gradle` glide_version = '4.16.0' // AppGallery Connect plugin versions: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-sdk-changenotes-0000001058732550 huawei_agconnect_version = '1.9.1.300' diff --git a/android/settings.gradle b/android/settings.gradle index 293bc6423..5ad529b31 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -10,7 +10,7 @@ pluginManagement { settings.ext.kotlin_version = '1.9.24' settings.ext.ksp_version = "$kotlin_version-1.0.20" - settings.ext.agp_version = '8.4.1' + settings.ext.agp_version = '8.5.0' includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index 1b1e4c318..d616dbe98 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -360,7 +360,7 @@ class MediaStoreSource extends CollectionSource { existingDirectories.add(existingDirectory); } } else { - final sourceEntry = await mediaFetchService.getEntry(uri, null); + final sourceEntry = await mediaFetchService.getEntry(uri, null, allowUnsized: true); if (sourceEntry != null) { newEntries.add(sourceEntry.copyWith( id: metadataDb.nextId, diff --git a/lib/model/source/trash.dart b/lib/model/source/trash.dart index 0249da2d4..98393b0b1 100644 --- a/lib/model/source/trash.dart +++ b/lib/model/source/trash.dart @@ -67,7 +67,7 @@ mixin TrashMixin on SourceBase { await metadataDb.updateTrash(id, entry.trashDetails); } else { // there is no matching entry - final sourceEntry = await mediaFetchService.getEntry(uri, null); + final sourceEntry = await mediaFetchService.getEntry(uri, null, allowUnsized: true); if (sourceEntry != null) { final id = metadataDb.nextId; sourceEntry.id = id; diff --git a/lib/model/vaults/vaults.dart b/lib/model/vaults/vaults.dart index 90a442dab..1491a9ec0 100644 --- a/lib/model/vaults/vaults.dart +++ b/lib/model/vaults/vaults.dart @@ -166,7 +166,7 @@ class Vaults extends ChangeNotifier { debugPrint('Recovering ${untrackedPaths.length} untracked vault items'); await Future.forEach(untrackedPaths, (untrackedPath) async { final uri = Uri.file(untrackedPath).toString(); - final sourceEntry = await mediaFetchService.getEntry(uri, null); + final sourceEntry = await mediaFetchService.getEntry(uri, null, allowUnsized: true); if (sourceEntry != null) { sourceEntry.id = metadataDb.nextId; sourceEntry.origin = EntryOrigins.vault; diff --git a/lib/services/media/media_fetch_service.dart b/lib/services/media/media_fetch_service.dart index e249024d2..756e1d58e 100644 --- a/lib/services/media/media_fetch_service.dart +++ b/lib/services/media/media_fetch_service.dart @@ -13,7 +13,7 @@ import 'package:flutter/services.dart'; import 'package:streams_channel/streams_channel.dart'; abstract class MediaFetchService { - Future getEntry(String uri, String? mimeType); + Future getEntry(String uri, String? mimeType, {bool allowUnsized = false}); Future getSvg( String uri, @@ -75,11 +75,12 @@ class PlatformMediaFetchService implements MediaFetchService { static const double _thumbnailDefaultSize = 64.0; @override - Future getEntry(String uri, String? mimeType) async { + Future getEntry(String uri, String? mimeType, {bool allowUnsized = false}) async { try { final result = await _platformObject.invokeMethod('getEntry', { 'uri': uri, 'mimeType': mimeType, + 'allowUnsized': allowUnsized, }) as Map; return AvesEntry.fromMap(result); } on PlatformException catch (e, stack) { diff --git a/lib/widgets/viewer/info/info_page.dart b/lib/widgets/viewer/info/info_page.dart index 95bfd9642..85cb274c7 100644 --- a/lib/widgets/viewer/info/info_page.dart +++ b/lib/widgets/viewer/info/info_page.dart @@ -286,5 +286,8 @@ class _InfoPageContentState extends State<_InfoPageContent> { }); } - void _onFilter(CollectionFilter filter) => FilterSelectedNotification(filter).dispatch(context); + void _onFilter(CollectionFilter filter) { + if (!mounted) return; + FilterSelectedNotification(filter).dispatch(context); + } } diff --git a/lib/widgets/viewer/overlay/video/progress_bar.dart b/lib/widgets/viewer/overlay/video/progress_bar.dart index 0a9144c9c..310b363a2 100644 --- a/lib/widgets/viewer/overlay/video/progress_bar.dart +++ b/lib/widgets/viewer/overlay/video/progress_bar.dart @@ -222,6 +222,6 @@ class _VideoProgressBarState extends State { double? _progressToDx(double progress) { final box = _getProgressBarRenderBox(); - return box == null ? null : progress * box.size.width; + return box != null && box.hasSize ? progress * box.size.width : null; } } diff --git a/plugins/aves_screen_state/android/build.gradle b/plugins/aves_screen_state/android/build.gradle index 941d165b2..2a7c98004 100644 --- a/plugins/aves_screen_state/android/build.gradle +++ b/plugins/aves_screen_state/android/build.gradle @@ -4,7 +4,7 @@ version '1.0-SNAPSHOT' buildscript { ext { kotlin_version = '1.9.24' - agp_version = '8.4.1' + agp_version = '8.5.0' } repositories { diff --git a/test/fake/media_fetch_service.dart b/test/fake/media_fetch_service.dart index 50dd1d969..9b18b6e6e 100644 --- a/test/fake/media_fetch_service.dart +++ b/test/fake/media_fetch_service.dart @@ -7,7 +7,7 @@ class FakeMediaFetchService extends Fake implements MediaFetchService { Set entries = {}; @override - Future getEntry(String uri, String? mimeType) async { + Future getEntry(String uri, String? mimeType, {bool allowUnsized = false}) async { return entries.firstWhereOrNull((v) => v.uri == uri); } }