cryptomator / android

Cryptomator for Android
https://cryptomator.org
GNU General Public License v3.0
761 stars 127 forks source link

Thumbnail support #533

Open JustFanta01 opened 6 months ago

JustFanta01 commented 6 months ago

Hello guys 👋 This is our proposal for implementing thumbnail support [/issues/41] Me, @WheelyMcBones and @taglioIsCoding have made the following changes.

We have implemented a uniform solution that works both for local and the remote clouds, this solution exploits the DiskLruCache in the CryptoImplDecorator and in the CryptoImplVaultFormat(Pre)7. We have decided these two location because we have access to all necessary informations: the decrypted image and the cloud type.

In cache we save a thumbnail when someone reads an image file and retrieve it from the same cache during the listing process. Thumbnails are stored as decrypted files in cache and, unlike other files in the /decrypted folder, these are persistent until the cache is deleted. We added the attribute ".thumbnail" in the CryptoFile pointing to the file in the disk cache and the CloudFileModel wraps it around. We also added the Preference in the Settings for when it is supposed to generate the thumbnails. Finally we got rid of the full duplication of the image by elaborating the thumbnail in stream with an ad-hoc Thread pool.

coderabbitai[bot] commented 6 months ago

Walkthrough

This update enhances Cryptomator's functionality for managing image files by implementing thumbnail caching and management. It introduces new methods for associating thumbnails with cloud files, improves cache handling during file operations, and integrates user preferences for thumbnail generation. The changes also include enhancements to the user interface for better interaction with visible cloud file nodes.

Changes

File Path Changes Summary
.../crypto/CryptoImplDecorator.kt Enhanced thumbnail caching, added methods for cache management, and introduced thumbnail generation logic.
.../presentation/presenter/BrowseFilesPresenter.kt Added methods for managing thumbnail associations and downloads, updated constructor to include new use case.
.../presentation/ui/fragment/BrowseFilesFragment.kt Implemented functionality for fast scrolling and managing visible nodes in the RecyclerView.

Possibly related PRs

🐇✨
A hop and a skip in the code so neat,
Thumbnails cache and files so sweet.
With every change, a better flow,
Cryptomator's charm, now on the show!
🌟📁🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)
🪧 Tips ### Chat There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai): - Review comments: Directly reply to a review comment made by CodeRabbit. Example: - `I pushed a fix in commit , please review it.` - `Generate unit testing code for this file.` - `Open a follow-up GitHub issue for this discussion.` - Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples: - `@coderabbitai generate unit testing code for this file.` - `@coderabbitai modularize this function.` - PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples: - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.` - `@coderabbitai read src/utils.ts and generate unit testing code.` - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.` - `@coderabbitai help me debug CodeRabbit configuration file.` Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. ### CodeRabbit Commands (Invoked using PR comments) - `@coderabbitai pause` to pause the reviews on a PR. - `@coderabbitai resume` to resume the paused reviews. - `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository. - `@coderabbitai full review` to do a full review from scratch and review all the files again. - `@coderabbitai summary` to regenerate the summary of the PR. - `@coderabbitai resolve` resolve all the CodeRabbit review comments. - `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository. - `@coderabbitai help` to get help. ### Other keywords and placeholders - Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed. - Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description. - Add `@coderabbitai` anywhere in the PR title to generate the title automatically. ### CodeRabbit Configuration File (`.coderabbit.yaml`) - You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository. - Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information. - If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json` ### Documentation and Community - Visit our [Documentation](https://coderabbit.ai/docs) for detailed information on how to use CodeRabbit. - Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback. - Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.
infeo commented 6 months ago

@JustFanta01 Not a review, but a general suggestion: ~For caching you use https://github.com/JakeWharton/DiskLruCache, which is unmaintained since 8 yrs.~ Consider using https://github.com/ben-manes/caffeine instead, which is a modern and quite good caching library.

Edit: my bad, this was the wrong dependency.

SailReal commented 6 months ago

Thank you so much for this contribution :heart:, will have a closer look to it on Monday!

Consider using https://github.com/ben-manes/caffeine instead, which is a modern and quite good caching library.

@infeo can you please explain in detail why we should switch from DiskLruCache to Caffeine?

infeo commented 6 months ago

@SailReal I withdraw my suggestion^^ First, i thought this was an outdated, unmaintained dependency, but i was wrong. Second, the project already uses this dependency and then it is good practice to use what's already there. And third, the dependency targets Android, so i guess it is also "optimized" for the OS in some way.

Regarding Caffeine: It uses a different algorithm with a statistically higher hit rate. See also https://github.com/ben-manes/caffeine/wiki/Efficiency.

SailReal commented 6 months ago

Also there is a bug if you use an svg-file, then BitmapFactory.decodeStream(thumbnailReader, null, options) returns null, thumbnailBitmap is then null (I think we shouldn't even call ThumbnailUtils.extractThumbnail if it is null) and then it crashes when we write to it in thumbnailWriter.write(buff.array(), 0, buff.remaining()) because we didn't even create the file we want to write into

org.cryptomator.domain.exception.FatalBackendException: java.io.IOException: Pipe closed
        at org.cryptomator.data.cloud.crypto.CryptoImplDecorator.read(CryptoImplDecorator.kt:422)
        at org.cryptomator.data.cloud.crypto.CryptoCloudContentRepository.read(CryptoCloudContentRepository.kt:95)
        at org.cryptomator.data.cloud.crypto.CryptoCloudContentRepository.read(CryptoCloudContentRepository.kt:21)
        at org.cryptomator.data.repository.DispatchingCloudContentRepository.read(DispatchingCloudContentRepository.kt:160)
        at org.cryptomator.domain.usecases.cloud.DownloadFiles.execute(DownloadFiles.java:32)
        at org.cryptomator.domain.usecases.cloud.DownloadFilesUseCase$Launcher$2.subscribe(DownloadFilesUseCase.java:99)
        at io.reactivex.internal.operators.flowable.FlowableFromPublisher.subscribeActual(FlowableFromPublisher.java:29)
        at io.reactivex.Flowable.subscribe(Flowable.java:14935)
        at io.reactivex.Flowable.subscribe(Flowable.java:14882)
        at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
        at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288)
        at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker.run(ExecutorScheduler.java:253)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:920)
    Caused by: java.io.IOException: Pipe closed
        at java.io.PipedInputStream.checkStateForReceive(PipedInputStream.java:263)
        at java.io.PipedInputStream.awaitSpace(PipedInputStream.java:271)
        at java.io.PipedInputStream.receive(PipedInputStream.java:234)
        at java.io.PipedOutputStream.write(PipedOutputStream.java:149)
        at org.cryptomator.data.cloud.crypto.CryptoImplDecorator.read(CryptoImplDecorator.kt:398)
        at org.cryptomator.data.cloud.crypto.CryptoCloudContentRepository.read(CryptoCloudContentRepository.kt:95) 
        at org.cryptomator.data.cloud.crypto.CryptoCloudContentRepository.read(CryptoCloudContentRepository.kt:21) 
        at org.cryptomator.data.repository.DispatchingCloudContentRepository.read(DispatchingCloudContentRepository.kt:160) 
        at org.cryptomator.domain.usecases.cloud.DownloadFiles.execute(DownloadFiles.java:32) 
        at org.cryptomator.domain.usecases.cloud.DownloadFilesUseCase$Launcher$2.subscribe(DownloadFilesUseCase.java:99) 
        at io.reactivex.internal.operators.flowable.FlowableFromPublisher.subscribeActual(FlowableFromPublisher.java:29) 
        at io.reactivex.Flowable.subscribe(Flowable.java:14935) 
        at io.reactivex.Flowable.subscribe(Flowable.java:14882) 
        at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82) 
        at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288) 
        at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker.run(ExecutorScheduler.java:253) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:920) 

ErrorCode: F1D9:OBF0

In this case, the file can not be opened anymore.

Please also test it with further other file types.