immich/mobile/lib/utils/cache/custom_image_cache.dart
shenlong 7855974a29
feat(mobile): sqlite asset viewer ()
* add full image provider and refactor thumb providers

* photo_view updates

* wip: asset-viewer

* fix controller dispose on page change

* wip: bottom sheet

* fix interactions

* more bottomsheet changes

* generate schema

* PR feedback

* refactor asset viewer

* never rotate and fix background on page change

* use photoview as the loading builder

* precache after delay

* claude: optimizing rebuild of image provider

* claude: optimizing image decoding and caching

* use proper cache for new full size image providers

* chore: load local HEIC fullsize for iOS

* make controller callbacks nullable

* remove imageprovider cache

* do not handle drag gestures when zoomed

* use loadOriginal setting for HEIC / larger images

* preload assets outside timer

* never use same controllers in photo-view gallery

* fix: cannot scroll down once swipe with bottom sheet

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-07-02 18:24:37 +00:00

84 lines
2.8 KiB
Dart

import 'package:flutter/painting.dart';
import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
import 'package:immich_mobile/providers/image/immich_local_image_provider.dart';
import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.dart';
import 'package:immich_mobile/providers/image/immich_remote_image_provider.dart';
import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart';
/// [ImageCache] that uses two caches for small and large images
/// so that a single large image does not evict all small images
final class CustomImageCache implements ImageCache {
final _small = ImageCache();
final _large = ImageCache()..maximumSize = 5; // Maximum 5 images
@override
int get maximumSize => _small.maximumSize + _large.maximumSize;
@override
int get maximumSizeBytes => _small.maximumSizeBytes + _large.maximumSizeBytes;
@override
set maximumSize(int value) => _small.maximumSize = value;
@override
set maximumSizeBytes(int value) => _small.maximumSize = value;
@override
void clear() {
_small.clear();
_large.clear();
}
@override
void clearLiveImages() {
_small.clearLiveImages();
_large.clearLiveImages();
}
/// Gets the cache for the given key
/// [_large] is used for [ImmichLocalImageProvider] and [ImmichRemoteImageProvider]
/// [_small] is used for [ImmichLocalThumbnailProvider] and [ImmichRemoteThumbnailProvider]
ImageCache _cacheForKey(Object key) => (key is ImmichLocalImageProvider ||
key is ImmichRemoteImageProvider ||
key is LocalFullImageProvider ||
key is RemoteFullImageProvider)
? _large
: _small;
@override
bool containsKey(Object key) {
// [ImmichLocalImageProvider] and [ImmichRemoteImageProvider] are both
// large size images while the other thumbnail providers are small
return _cacheForKey(key).containsKey(key);
}
@override
int get currentSize => _small.currentSize + _large.currentSize;
@override
int get currentSizeBytes => _small.currentSizeBytes + _large.currentSizeBytes;
@override
bool evict(Object key, {bool includeLive = true}) =>
_cacheForKey(key).evict(key, includeLive: includeLive);
@override
int get liveImageCount => _small.liveImageCount + _large.liveImageCount;
@override
int get pendingImageCount =>
_small.pendingImageCount + _large.pendingImageCount;
@override
ImageStreamCompleter? putIfAbsent(
Object key,
ImageStreamCompleter Function() loader, {
ImageErrorListener? onError,
}) =>
_cacheForKey(key).putIfAbsent(key, loader, onError: onError);
@override
ImageCacheStatus statusForKey(Object key) =>
_cacheForKey(key).statusForKey(key);
}