From 4e53cbaa7ff8ee4d4eacde1151cafb2705401d2a Mon Sep 17 00:00:00 2001 From: Damontecres Date: Sun, 16 Aug 2026 10:20:46 -0400 Subject: [PATCH] Preload next image during slideshow (#1847) ## Description When viewing images in a slideshow, this PR enqueues the next image so it will likely be cached and ready to display when the slideshow advances. This works for both automatic and manual scrolling as well as advancing next or previous images. ### Related issues Closes #1835 ### Testing Emulator with large photos ## Screenshots N/A ## AI or LLM usage None --- .../ui/slideshow/SlideshowViewModel.kt | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt index c81ffcb7..e74ffdf2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/slideshow/SlideshowViewModel.kt @@ -6,6 +6,9 @@ import androidx.compose.runtime.Stable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.media3.common.Player +import coil3.imageLoader +import coil3.request.ImageRequest +import coil3.size.Size import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.PlaybackEffectDao import com.github.damontecres.wholphin.data.ServerRepository @@ -172,7 +175,7 @@ class SlideshowViewModel val size = state.value.items.size val newPosition = state.value.position + 1 return if (newPosition < size) { - updatePosition(newPosition) + updatePosition(newPosition, true) true } else { false @@ -182,14 +185,23 @@ class SlideshowViewModel fun previousImage(): Boolean { val newPosition = state.value.position - 1 return if (newPosition >= 0) { - updatePosition(newPosition) + updatePosition(newPosition, false) true } else { false } } - fun updatePosition(position: Int): Job? = + /** + * Update image state to the specified position + * + * @param position the index to use + * @param isNext whether moving to the next or previous image for prefetching purposes + */ + fun updatePosition( + position: Int, + isNext: Boolean = true, + ): Job? = (state.value.items as? ApiRequestPager<*>)?.let { pager -> viewModelScope.launchIO { try { @@ -286,6 +298,31 @@ class SlideshowViewModel ) } } + try { + // If there is a next/previous image, fetch it into the image cache + val nextIndex = position + if (isNext) 1 else -1 + if (nextIndex in pager.indices) { + pager.getBlocking(nextIndex)?.let { nextImage -> + if (nextImage.data.mediaType == MediaType.PHOTO) { + val url = api.libraryApi.getDownloadUrl(nextImage.id) + val request = + ImageRequest + .Builder(context) + .data(url) + .size(Size.ORIGINAL) + .build() + context.imageLoader.enqueue(request) + Timber.v( + "Prefetching index=%s, id=%s", + nextIndex, + nextImage.id, + ) + } + } + } + } catch (ex: Exception) { + Timber.e(ex, "Error preloading image") + } } } @@ -339,7 +376,7 @@ class SlideshowViewModel fun pulseSlideshow() = pulseSlideshow(slideshowDelay) fun pulseSlideshow(milliseconds: Long) { - Timber.v("pulseSlideshow $milliseconds") +// Timber.v("pulseSlideshow $milliseconds") slideshowJob?.cancel() slideshowJob = viewModelScope