Skip to content

Unify image loading on shared OkHttp and defer artwork presentation during scroll - #256

Merged
Quick104 merged 1 commit into
mainfrom
t3code/android-image-pipeline
Aug 28, 2026
Merged

Unify image loading on shared OkHttp and defer artwork presentation during scroll#256
Quick104 merged 1 commit into
mainfrom
t3code/android-image-pipeline

Conversation

@Quick104

@Quick104 Quick104 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

What

One OkHttp foundation for image + API traffic. Replaces coil-network-ktor3 with coil-network-okhttp and registers the fetcher explicitly instead of relying on service-loader auto-registration (which silently built its own default HTTP client). The new SiloOkHttp object shares a single ConnectionPool between the Ktor API clients and Coil so artwork rides the same warm TLS connections, while each client keeps its own tuned Dispatcher so poster bursts and API fan-outs never queue behind each other (and long-lived websockets can't pin image slots). Loader configuration is unified in buildSiloImageLoader (android-shared), so the phone and TV apps can no longer drift. The Media3 player transport deliberately keeps its own pool — documented in SiloOkHttp's KDoc.

App-wide artwork presentation deferral. Home already held freshly decoded artwork behind its thumbhash placeholder until the vertical scroll settles; this generalizes that into DeferImagePresentationWhileScrolling (OR-combining nested scroll axes) and wires it across all artwork surfaces: horizontal rows, browse/search/personal/collection grids, detail feeds, calendar shelves, downloads, inbox, reading hub, and the player next-up overlay. Requests and decodes keep running during the gesture; only first presentation waits, and memory-cache hits still present instantly so scroll-back is unchanged.

Pagination trigger fixes. CatalogGrid and SearchResults computed their load-more signal inside a keyless remember { derivedStateOf }, permanently capturing first-composition hasMore/isLoading values — pagination could never fire (or kept re-firing at list end) depending on entry state. Now keyed on the flags.

Dependency hygiene. android-shared and androidApp imported Ktor client classes that only reached the compile classpath transitively through the old Coil artifact — now declared explicitly. Locks and verification metadata regenerated; stale TV lockfile pins removed.

Testing

  • ./gradlew :androidApp:assembleDebug :androidTvApp:assembleDebug and the full ./gradlew test suite pass.
  • Release (R8) build exercised on a physical Pixel: browsing, search, detail pages, calendar, downloads, and requests, with cold and warm image caches; frame stats captured via dumpsys gfxinfo show zero slow bitmap uploads during scroll.
  • Adversarially reviewed (spec + code); all findings addressed or explicitly documented.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Image presentation is now deferred while scrolling across feeds, grids, detail pages, search results, calendars, downloads, and other content areas for smoother navigation.
    • Images continue appearing immediately when already cached.
    • Android and Android TV now share consistent image loading and caching behavior.
  • Bug Fixes

    • Updated search and catalog pagination state to respond correctly to changing loading and availability conditions.
    • Improved image loading reliability and reuse across the app.

…work presentation during scroll

- Replace coil-network-ktor3 with coil-network-okhttp and register the
  fetcher explicitly: new SiloOkHttp foundation shares one ConnectionPool
  between the Ktor API clients and Coil, with per-client tuned dispatchers
  so image bursts and API fan-outs never queue behind each other. Loader
  config is unified in buildSiloImageLoader so phone and TV cannot drift.
- Extend Home's existing artwork-presentation deferral to all artwork
  scroll surfaces via DeferImagePresentationWhileScrolling: freshly decoded
  images hold their thumbhash until the scroll settles (memory-cache hits
  still present instantly), now covering horizontal rows, grids, detail
  feeds, and vertical-scroll columns.
- Fix stale-capture load-more triggers in CatalogGrid and SearchResults:
  keyless remember { derivedStateOf } froze first-composition
  hasMore/isLoading values, breaking pagination in some entry states.
- Declare ktor-client-core explicitly in android-shared/androidApp (it was
  only reaching the compile classpath transitively via the old Coil
  artifact); regenerate dependency locks and verification metadata and
  drop stale TV lock pins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-28T23:11:03.866757Z 5b96415 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c12c2df-83c8-41ad-9e5b-4764cde8d695

📥 Commits

Reviewing files that changed from the base of the PR and between 73d8979 and 5b96415.

📒 Files selected for processing (41)
  • android-shared/build.gradle.kts
  • android-shared/gradle.lockfile
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/images/SiloImageLoader.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/ui/components/ThumbhashImage.kt
  • androidApp/build.gradle.kts
  • androidApp/gradle.lockfile
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/SiloApplication.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/MediaRow.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/audiobook/AudiobookDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/book/BookDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/browse/CatalogGrid.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/calendar/CalendarScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/collections/CollectionDetailScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/collections/LibraryCollectionsScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/CastCrewSection.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/MovieDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/SeriesDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/SimilarRail.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/downloads/DownloadsScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/home/HomeScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/libraries/LibrariesScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/notifications/InboxScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/people/PersonDetailScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/personal/PersonalMediaGridContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/PlayerNextUpScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/reading/ReadingHubScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/recommendations/RecommendationsScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/requests/MyRequestsScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/requests/RequestDetailScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/requests/RequestsScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/search/RequestSearchSection.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/search/SearchResults.kt
  • androidTvApp/build.gradle.kts
  • androidTvApp/gradle.lockfile
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/SiloTvApplication.kt
  • baselineprofile-tv/gradle.lockfile
  • baselineprofile/gradle.lockfile
  • gradle/libs.versions.toml
  • gradle/verification-metadata.xml
  • shared/src/androidMain/kotlin/org/siloserver/silo/network/SiloHttpClient.android.kt
  • shared/src/androidMain/kotlin/org/siloserver/silo/network/SiloOkHttp.kt

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.


📝 Walkthrough

Walkthrough

Android image loading now uses Coil’s OkHttp integration with shared process-level OkHttp resources. Image-loader creation is centralized. Lazy lists and grids defer image presentation while scrolling across Android screens. Loading-state derivation updates when pagination inputs change.

Changes

Image loading and networking

Layer / File(s) Summary
Shared OkHttp foundation
shared/src/androidMain/kotlin/org/siloserver/silo/network/*
Added shared connection-pool and dispatcher configuration. Coil receives a dedicated OkHttp client. The platform API client uses the shared resources.
Coil dependency and loader wiring
gradle/*, android-shared/*, androidApp/*, androidTvApp/*, baselineprofile*/gradle.lockfile
Replaced Coil’s Ktor network integration with OkHttp. Centralized the 512 MB cached image-loader configuration for phone and TV applications. Updated Ktor, kotlinx-io, lockfile, and verification entries.

Scroll-aware image presentation

Layer / File(s) Summary
Presentation deferral state
android-shared/src/androidMain/kotlin/org/siloserver/silo/common/ui/components/ThumbhashImage.kt
Added LocalImagePresentationDeferral and DeferImagePresentationWhileScrolling. The state combines parent deferral with active scrolling.
Content screen integration
androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/*, androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/{audiobook,book,browse,calendar,collections,detail,libraries,people,personal}/*
Added remembered lazy-list or lazy-grid states and wrapped the corresponding content in scroll-aware image presentation.
Feed, request, search, and playback integration
androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/{downloads,home,notifications,player,reading,recommendations,requests,search}/*
Applied scroll-aware presentation to remaining lists, rows, and grids. Updated catalog and search load-more derived state to use current parameters.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to 5b964

The PR unifies image networking, defers artwork presentation during scrolling, and fixes pagination state handling; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: rxwatcher

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.07% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 41 functions across 34 files. (5 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: shared OkHttp image loading and deferred artwork presentation during scrolling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 17.07% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 41 functions across 34 files. (5 skipped: 5 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/android-image-pipeline

Comment @coderabbitai help to get the list of available commands.

@Quick104
Quick104 merged commit f0ab32f into main Aug 28, 2026
3 checks passed
@Quick104
Quick104 deleted the t3code/android-image-pipeline branch August 28, 2026 23:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant