From 8fa3f18aef08645cfd8376461c770d9918405f0d Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Thu, 4 Feb 2021 17:45:46 +0900 Subject: [PATCH] multipage: thumbnail request cancellation --- lib/model/entry.dart | 9 +++++++++ lib/model/multipage.dart | 11 ++++++++--- lib/services/metadata_service.dart | 2 +- lib/widgets/collection/grid/thumbnail.dart | 5 ++++- lib/widgets/collection/thumbnail/decorated.dart | 6 +++--- lib/widgets/collection/thumbnail/raster.dart | 10 +++------- lib/widgets/viewer/overlay/multipage.dart | 8 +++++++- 7 files changed, 35 insertions(+), 16 deletions(-) diff --git a/lib/model/entry.dart b/lib/model/entry.dart index 52c6987e0..92417998e 100644 --- a/lib/model/entry.dart +++ b/lib/model/entry.dart @@ -172,6 +172,15 @@ class AvesEntry { addressChangeNotifier.dispose(); } + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) return false; + return other is AvesEntry && other.uri == uri && other.pageId == pageId && other._dateModifiedSecs == _dateModifiedSecs; + } + + @override + int get hashCode => hashValues(uri, pageId, _dateModifiedSecs); + @override String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, path=$path, pageId=$pageId}'; diff --git a/lib/model/multipage.dart b/lib/model/multipage.dart index 9400c1beb..257b09e85 100644 --- a/lib/model/multipage.dart +++ b/lib/model/multipage.dart @@ -1,11 +1,13 @@ import 'package:flutter/foundation.dart'; class MultiPageInfo { + final String uri; final List pages; int get pageCount => pages.length; MultiPageInfo({ + @required this.uri, this.pages, }) { if (pages.isNotEmpty) { @@ -18,8 +20,11 @@ class MultiPageInfo { } } - factory MultiPageInfo.fromPageMaps(List pageMaps) { - return MultiPageInfo(pages: pageMaps.map((page) => SinglePageInfo.fromMap(page)).toList()); + factory MultiPageInfo.fromPageMaps(String uri, List pageMaps) { + return MultiPageInfo( + uri: uri, + pages: pageMaps.map((page) => SinglePageInfo.fromMap(page)).toList(), + ); } SinglePageInfo get defaultPage => pages.firstWhere((page) => page.isDefault, orElse: () => null); @@ -29,7 +34,7 @@ class MultiPageInfo { SinglePageInfo getById(int pageId) => pages.firstWhere((page) => page.pageId == pageId, orElse: () => null); @override - String toString() => '$runtimeType#${shortHash(this)}{pages=$pages}'; + String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, pages=$pages}'; } class SinglePageInfo implements Comparable { diff --git a/lib/services/metadata_service.dart b/lib/services/metadata_service.dart index d55799255..b7a830623 100644 --- a/lib/services/metadata_service.dart +++ b/lib/services/metadata_service.dart @@ -89,7 +89,7 @@ class MetadataService { 'uri': entry.uri, }); final pageMaps = (result as List).cast(); - return MultiPageInfo.fromPageMaps(pageMaps); + return MultiPageInfo.fromPageMaps(entry.uri, pageMaps); } on PlatformException catch (e) { debugPrint('getMultiPageInfo failed with code=${e.code}, exception=${e.message}, details=${e.details}'); } diff --git a/lib/widgets/collection/grid/thumbnail.dart b/lib/widgets/collection/grid/thumbnail.dart index d6ddf27bd..09dcd14ca 100644 --- a/lib/widgets/collection/grid/thumbnail.dart +++ b/lib/widgets/collection/grid/thumbnail.dart @@ -43,7 +43,10 @@ class InteractiveThumbnail extends StatelessWidget { entry: entry, extent: tileExtent, collection: collection, - isScrollingNotifier: isScrollingNotifier, + // when the user is scrolling faster than we can retrieve the thumbnails, + // the retrieval task queue can pile up for thumbnails that got disposed + // in this case we pause the image retrieval task to get it out of the queue + cancellableNotifier: isScrollingNotifier, ), ), ); diff --git a/lib/widgets/collection/thumbnail/decorated.dart b/lib/widgets/collection/thumbnail/decorated.dart index 938e30a34..2a4fa81b9 100644 --- a/lib/widgets/collection/thumbnail/decorated.dart +++ b/lib/widgets/collection/thumbnail/decorated.dart @@ -9,7 +9,7 @@ class DecoratedThumbnail extends StatelessWidget { final AvesEntry entry; final double extent; final CollectionLens collection; - final ValueNotifier isScrollingNotifier; + final ValueNotifier cancellableNotifier; final bool selectable, highlightable; static final Color borderColor = Colors.grey.shade700; @@ -20,7 +20,7 @@ class DecoratedThumbnail extends StatelessWidget { @required this.entry, @required this.extent, this.collection, - this.isScrollingNotifier, + this.cancellableNotifier, this.selectable = true, this.highlightable = true, }) : super(key: key); @@ -40,7 +40,7 @@ class DecoratedThumbnail extends StatelessWidget { : RasterImageThumbnail( entry: entry, extent: extent, - isScrollingNotifier: isScrollingNotifier, + cancellableNotifier: cancellableNotifier, heroTag: heroTag, ); diff --git a/lib/widgets/collection/thumbnail/raster.dart b/lib/widgets/collection/thumbnail/raster.dart index 006e83480..e7ac7c516 100644 --- a/lib/widgets/collection/thumbnail/raster.dart +++ b/lib/widgets/collection/thumbnail/raster.dart @@ -9,14 +9,14 @@ import 'package:flutter/material.dart'; class RasterImageThumbnail extends StatefulWidget { final AvesEntry entry; final double extent; - final ValueNotifier isScrollingNotifier; + final ValueNotifier cancellableNotifier; final Object heroTag; const RasterImageThumbnail({ Key key, @required this.entry, @required this.extent, - this.isScrollingNotifier, + this.cancellableNotifier, this.heroTag, }) : super(key: key); @@ -70,11 +70,7 @@ class _RasterImageThumbnailState extends State { } void _pauseProvider() { - final isScrolling = widget.isScrollingNotifier?.value ?? false; - // when the user is scrolling faster than we can retrieve the thumbnails, - // the retrieval task queue can pile up for thumbnails that got disposed - // in this case we pause the image retrieval task to get it out of the queue - if (isScrolling) { + if (widget.cancellableNotifier?.value ?? false) { _fastThumbnailProvider?.pause(); _sizedThumbnailProvider?.pause(); } diff --git a/lib/widgets/viewer/overlay/multipage.dart b/lib/widgets/viewer/overlay/multipage.dart index b1568b837..3f96e80bb 100644 --- a/lib/widgets/viewer/overlay/multipage.dart +++ b/lib/widgets/viewer/overlay/multipage.dart @@ -27,6 +27,7 @@ class MultiPageOverlay extends StatefulWidget { } class _MultiPageOverlayState extends State { + final _cancellableNotifier = ValueNotifier(true); ScrollController _scrollController; bool _syncScroll = true; @@ -90,7 +91,8 @@ class _MultiPageOverlayState extends State { future: controller.info, builder: (context, snapshot) { final multiPageInfo = snapshot.data; - if ((multiPageInfo?.pageCount ?? 0) <= 1) return SizedBox.shrink(); + if ((multiPageInfo?.pageCount ?? 0) <= 1) return SizedBox(); + if (multiPageInfo.uri != mainEntry.uri) return SizedBox(); return Container( height: extent + separatorWidth * 2, child: Stack( @@ -125,6 +127,10 @@ class _MultiPageOverlayState extends State { child: DecoratedThumbnail( entry: pageEntry, extent: extent, + // the retrieval task queue can pile up for thumbnails of heavy pages + // (e.g. thumbnails of 15MP HEIF images inside 100MB+ HEIC containers) + // so we cancel these requests when possible + cancellableNotifier: _cancellableNotifier, selectable: false, highlightable: false, ),