Skip to content

Add image and video thumbnails in file browser#638

Open
lewinpauli wants to merge 3 commits into
cryptomator:developfrom
lewinpauli:develop
Open

Add image and video thumbnails in file browser#638
lewinpauli wants to merge 3 commits into
cryptomator:developfrom
lewinpauli:develop

Conversation

@lewinpauli
Copy link
Copy Markdown

Closes #41

Adds image and video thumbnails in the file browser vault view.

  • ThumbnailUtil: async loading with 3-thread pool, disk cache
    in cacheDir/thumbnails/
  • Images: BitmapFactory with inSampleSize
  • Videos: MediaMetadataRetriever.getFrameAtTime(0)
  • Tag-based stale-load prevention for RecyclerView recycling
  • Thumbnail size: 72dp

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@lewinpauli
Copy link
Copy Markdown
Author

lewinpauli commented May 16, 2026

ups i just noticed that #533 is already trying the same for a much longer time

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

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

Run ID: e35fd41d-6d3c-4a02-a135-9aea34892738

📥 Commits

Reviewing files that changed from the base of the PR and between 9ef3d24 and 03be207.

📒 Files selected for processing (2)
  • presentation/src/main/java/org/cryptomator/presentation/ui/adapter/BrowseFilesAdapter.kt
  • presentation/src/main/java/org/cryptomator/presentation/util/ThumbnailUtil.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • presentation/src/main/java/org/cryptomator/presentation/ui/adapter/BrowseFilesAdapter.kt
  • presentation/src/main/java/org/cryptomator/presentation/util/ThumbnailUtil.kt

Walkthrough

This PR implements thumbnail support for images and videos in the Cryptomator Android file browser. It adds a ThumbnailUtil singleton that loads and caches thumbnails from cloud files, supporting both image and video thumbnail extraction. A new THUMBNAILS cache type is registered in LruFileCacheUtil. The BrowseFilesAdapter is updated to use ThumbnailUtil for image/movie file types while maintaining fallback icon rendering. Dependency injection wiring adds ThumbnailUtil to ApplicationComponent. Layout attributes and resource dimensions are updated for proper thumbnail display sizing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding image and video thumbnails to the file browser interface.
Description check ✅ Passed The description is clearly related to the changeset, detailing the implementation approach with ThumbnailUtil, caching strategy, and thumbnail size configuration.
Linked Issues check ✅ Passed The PR directly addresses issue #41 by implementing thumbnail support in the file browser with image and video thumbnail loading as requested in the feature request.
Out of Scope Changes check ✅ Passed All changes are focused on implementing thumbnail functionality: utility class for async loading, adapter integration, layout updates, dimension changes, and cache directory support. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@presentation/src/main/java/org/cryptomator/presentation/ui/adapter/BrowseFilesAdapter.kt`:
- Around line 145-151: In BrowseFilesAdapter's bind path where you set
non-thumbnail icons (the else branch that calls bindCloudNodeImage and
binding.cloudNodeImage.setImageResource), clear any previous thumbnail request
state on the ImageView before setting the static icon: e.g., remove/reset the
view tag used by thumbnailUtil (the same tag used when calling
thumbnailUtil.loadThumbnail) and cancel or detach any pending thumbnail callback
so an in-flight thumbnail callback cannot overwrite folder/other icons; ensure
this uses the same ImageView (binding.cloudNodeImage) and thumbnailUtil
interaction patterns as in the thumbnail branch.

In
`@presentation/src/main/java/org/cryptomator/presentation/util/ThumbnailUtil.kt`:
- Around line 41-46: The cached-thumbnail decode currently runs on the main
thread (BitmapFactory.decodeFile on cachedFile) inside the bind path; move the
decode work onto the provided executor and only post target.setImageBitmap(...)
back onto mainHandler. Specifically, in ThumbnailUtil replace the inline
BitmapFactory.decodeFile(cachedFile.absolutePath) call with executor.execute {
val bmp = BitmapFactory.decodeFile(cachedFile.absolutePath); if (bmp != null)
mainHandler.post { target.setImageBitmap(bmp) } } and ensure the original
early-return behavior is preserved (i.e., return from the bind method only after
scheduling the decode/posting so the cached path short-circuits further work).
- Around line 71-74: The current generateImageThumbnail uses downloadToByteArray
and decodeScaledBitmap which load the entire image into memory; instead, change
the flow to download the image to a temporary file (file-backed), then perform a
bounds-only decode (BitmapFactory.Options.inJustDecodeBounds = true) on that
temp file to compute an appropriate inSampleSize, and finally perform a sampled
decode from the file (e.g., decodeFile or decodeStream) and pass that Bitmap to
saveThumbnail; replace uses of downloadToByteArray/decodeScaledBitmap with a
file-backed download + bounds decode + sampled decode pattern, and apply the
same fix to the analogous code referenced at lines ~95-100 so both image
thumbnail paths use file-backed decoding to avoid OOMs.
- Around line 82-89: Replace the MediaMetadataRetriever().use { ... } block with
an explicit try/finally to call retriever.release() (because AutoCloseable was
added API 29 and .use() will crash on minSdk 26). Instantiate
MediaMetadataRetriever as a val (e.g., retriever = MediaMetadataRetriever()),
then in try setDataSource(tmpFile.absolutePath), call getFrameAtTime and handle
the null return the same way, call scaleBitmap(frame) and saveThumbnail(bitmap,
cachedFile) inside the try, and ensure retriever.release() is called in the
finally block; keep existing return behavior and references to
MediaMetadataRetriever, scaleBitmap, saveThumbnail, tmpFile and cachedFile.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cc9d2ea7-8e11-4bfd-8229-389f45034dfa

📥 Commits

Reviewing files that changed from the base of the PR and between 0af31a0 and 9ef3d24.

📒 Files selected for processing (8)
  • .gitignore
  • .idea/misc.xml
  • presentation/src/main/java/org/cryptomator/presentation/di/component/ApplicationComponent.java
  • presentation/src/main/java/org/cryptomator/presentation/ui/adapter/BrowseFilesAdapter.kt
  • presentation/src/main/java/org/cryptomator/presentation/util/ThumbnailUtil.kt
  • presentation/src/main/res/layout/item_browse_files_node.xml
  • presentation/src/main/res/values/dimens.xml
  • util/src/main/java/org/cryptomator/util/file/LruFileCacheUtil.kt
💤 Files with no reviewable changes (1)
  • .idea/misc.xml

Comment thread presentation/src/main/java/org/cryptomator/presentation/util/ThumbnailUtil.kt Outdated
Comment on lines +71 to +74
private fun generateImageThumbnail(file: CloudFileModel, cachedFile: File): Bitmap? {
val bytes = downloadToByteArray(file) ?: return null
val bitmap = decodeScaledBitmap(bytes) ?: return null
saveThumbnail(bitmap, cachedFile)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Current image path loads entire files into heap before sampling.

Downloading full image bytes first defeats memory savings from inSampleSize and can trigger OOM on large photos. Prefer file-backed decode (download to temp file, bounds decode + sampled decode from file).

Also applies to: 95-100

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@presentation/src/main/java/org/cryptomator/presentation/util/ThumbnailUtil.kt`
around lines 71 - 74, The current generateImageThumbnail uses
downloadToByteArray and decodeScaledBitmap which load the entire image into
memory; instead, change the flow to download the image to a temporary file
(file-backed), then perform a bounds-only decode
(BitmapFactory.Options.inJustDecodeBounds = true) on that temp file to compute
an appropriate inSampleSize, and finally perform a sampled decode from the file
(e.g., decodeFile or decodeStream) and pass that Bitmap to saveThumbnail;
replace uses of downloadToByteArray/decodeScaledBitmap with a file-backed
download + bounds decode + sampled decode pattern, and apply the same fix to the
analogous code referenced at lines ~95-100 so both image thumbnail paths use
file-backed decoding to avoid OOMs.

Comment thread presentation/src/main/java/org/cryptomator/presentation/util/ThumbnailUtil.kt Outdated
…triever API 26 compat and stale tag cleanup on rebind
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.

Add thumbnail support

2 participants