Skip to content

fix(share): send original file instead of cached thumbnail - #17808

Open
ToteMeiSter wants to merge 1 commit into
nextcloud:masterfrom
ToteMeiSter:fix/send-original-not-downloaded-image
Open

ToteMeiSter wants to merge 1 commit into
nextcloud:masterfrom
ToteMeiSter:fix/send-original-not-downloaded-image

Conversation

@ToteMeiSter

@ToteMeiSter ToteMeiSter commented Oct 12, 2026 •

Copy link
Copy Markdown

Problem

Refs #17414

When a picture is not stored on the device, SendShareDialog does not download it. It calls FileOperationsHelper.sendCachedImage, which puts a content://org.nextcloud.imageCache.provider/... URI into ACTION_SEND. That URI is served by DiskLruImageCacheFileProvider, which returns the preview thumbnail from the disk cache, not the original file. The receiving app gets a downscaled PNG instead of the photo.

The provider is exported with android:permission="android.permission.MANAGE_DOCUMENTS". Only the system documents UI holds that permission, so most apps (for example Google Messages, #17414) cannot open the URI at all.

The cached path was added in 2015 (a1bc147849) and extended to the file list in 2016 (d1d99c5e3f) to send a picture without downloading it. Every other file type already goes through SendShareDialogDownloader, which downloads the original and then sends it.

Change

  • SendShareDialog.onSendButtonClick: no special case for pictures. A downloaded file is sent at once. A file that is not on the device is downloaded through the host's SendShareDialogDownloader and sent afterwards. A host without a downloader shows "This file can't be sent from here because it isn't on your device yet" instead of closing the dialog without feedback. After this PR no such host is left; the message guards future hosts.
  • SendShareAction (new enum) holds this decision, so it is unit-tested without the Android SDK.
  • DiskLruImageCacheFileProvider is removed: the class, the manifest entry, the Dagger injector and image_cache_provider_authority in all setup.xml files. Its thumbnail descriptor was also used by DocumentsStorageProvider.openDocumentThumbnail; that code moves into DocumentsStorageProvider unchanged.
  • AudioPlayerActivity implements SendShareDialogDownloader through the existing SendShareDownloader, the same way PreviewImageActivity does.
  • "Set as" used the same provider for a picture that is not downloaded. FileMenuFilter now hides the entry for such a picture, like "Open with". Every caller of setPictureAs goes through this filter.
  • actionbar_send_file was only used by sendCachedImage and is removed.

Hosts of SendShareDialog:

Host Downloader Not downloaded file
FileDisplayActivity yes downloaded, then sent
PreviewImageActivity yes downloaded, then sent
AudioPlayerActivity yes downloaded, then sent

Notes

How to test

  • Unit: SendShareActionTest (4 cases). The existing unit tests pass (jacocoTestGplayDebugUnitTest).
  • Instrumented: FileMenuFilterIT.filter_setPictureAs_onlyForDownloadedImages (compiled, not run locally).
  • Device: Checked on a HUAWEI Mate X7 (DEL-LX9, EMUI 15.0.0, Android 12) with a test build: works, the original picture is sent instead of the thumbnail.
  • Manual steps:
    1. Open the file list, pick a picture that is not downloaded, open "Send/share" and choose an app (for example Google Messages).
    2. The picture is downloaded and the app receives the original file in full resolution.
    3. Repeat from the image preview.
    4. Repeat with a downloaded picture: it is sent at once, as before.
    5. In the audio player, send a file that is not downloaded: it is downloaded and sent.
    6. Open "⋮" on a picture that is not downloaded: "Set as" is not shown.

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

The change was drafted with Claude Code (Assisted-by: Claude-Code:claude-opus-5-5).

A picture that was not downloaded was sent as a content URI of
DiskLruImageCacheFileProvider, which serves a downscaled thumbnail from the
preview cache. The provider is exported with the MANAGE_DOCUMENTS permission,
so other apps could not read it either.

Every file that is not on the device now goes through
SendShareDialogDownloader: the original is downloaded and then sent. The
audio player implements the downloader like the image preview does. A host
without a downloader shows a message instead of closing the dialog silently.
The choice is made by SendShareAction and covered by a unit test.

DiskLruImageCacheFileProvider is removed together with its manifest entry,
injector and image_cache_provider_authority. "Set as" is hidden for a picture
that is not downloaded, because it used the same provider. The thumbnail
descriptor for DocumentsStorageProvider moves into that class.

Signed-off-by: Krainov Gleb <krajnov.g@kontentplus.ru>
Assisted-by: Claude-Code:claude-opus-5-5
Assisted-by: Claude-Code:claude-sonnet-5-5

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant