import 'dart:ui' as ui; import 'package:aves/services/common/services.dart'; import 'package:aves_report/aves_report.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; class ThumbnailProvider extends ImageProvider { final ThumbnailProviderKey key; ThumbnailProvider(this.key); @override Future obtainKey(ImageConfiguration configuration) { // configuration can be empty (e.g. when obtaining key for eviction) // so we do not compute the target width/height here // and pass it to the key, to use it later for image loading return SynchronousFuture(key); } @override ImageStreamCompleter loadImage(ThumbnailProviderKey key, ImageDecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key, decode), scale: 1.0, debugLabel: kReleaseMode ? null : [key.uri, key.extent].join('-'), informationCollector: () sync* { yield ErrorDescription('uri=${key.uri}, pageId=${key.pageId}, mimeType=${key.mimeType}, extent=${key.extent}'); }, ); } Future _loadAsync(ThumbnailProviderKey key, ImageDecoderCallback decode) async { try { return await mediaFetchService.getThumbnail( decoded: false, request: key, decode: decode, taskKey: key, ); } catch (error) { // loading may fail if the provided MIME type is incorrect (e.g. the Media Store may report a JPEG as a TIFF) debugPrint('$runtimeType _loadAsync failed for key=$key, error=$error'); throw UnreportedStateError('thumbnail decoding failed for key=$key, error=$error'); } } @override void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, ThumbnailProviderKey key, ImageErrorListener handleError) { mediaFetchService.resumeLoading(key); super.resolveStreamForKey(configuration, stream, key, handleError); } void pause() => mediaFetchService.cancelThumbnail(key); } @immutable class ThumbnailProviderKey extends Equatable { // do not store the entry as it is, because the key should be constant // but the entry attributes may change over time final String uri, mimeType; final int? pageId; final int rotationDegrees; final bool isFlipped; final int dateModifiedMillis; final double extent; @override List get props => [uri, pageId, dateModifiedMillis, extent]; const ThumbnailProviderKey({ required this.uri, required this.mimeType, required this.pageId, required this.rotationDegrees, required this.isFlipped, required this.dateModifiedMillis, this.extent = 0, }); @override String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, pageId=$pageId, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, dateModifiedMillis=$dateModifiedMillis, extent=$extent}'; }