Keep shared media that providers generate on the fly - #23239
Conversation
downloadExternalMedia() names its cached copy after the provider's display name, falling back to a MIME type parsed out of the URI string - which a content:// URI never carries. Providers that generate media on the fly (Google Photos sharing an "Enhanced" photo, the photo picker, SAF) report no usable display name, so the copy is left with no extension at all. Every MIME check downstream reads the file name, and FluxCUtils.mediaModelFromLocalUri ends its fallback chain at image/jpeg, so a shared PNG, HEIC or video was uploaded labelled as a JPEG. Rename the copy using the type the provider reported, which isAllowedMediaType() already resolves.
isMediaTypeIntent() derives the type from the URI's file extension, and
getFileExtensionFromUrl() returns an empty string both for a name left without an
extension by the provider that shared it and for any name containing a space
("Screenshot 2026-08-19.jpg"). Those items were dropped with no toast and no
snackbar. Single ACTION_SEND escaped this because it reads intent.type instead.
Fall back to intent.type when the URI yields nothing, so ACTION_SEND_MULTIPLE
behaves like the single-share path it already trusts.
Generated by 🚫 Danger |
|
|
|
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## trunk #23239 +/- ##
=======================================
Coverage 37.93% 37.94%
=======================================
Files 2334 2334
Lines 127137 127138 +1
Branches 17608 17609 +1
=======================================
+ Hits 48234 48241 +7
+ Misses 74959 74952 -7
- Partials 3944 3945 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…type getExtensionForMimeType() never fails: when MimeTypeMap doesn't know the type it returns the subtype, so a provider reporting application/octet-stream produced "wp-123.octet-stream". That reads as a real extension downstream, which suppresses the image/jpeg fallback in FluxCUtils.mediaModelFromLocalUri() that had been making the upload work - leaving the media worse off than with no extension at all. Also cover the case the intent.type fallback could have regressed: a URI whose extension resolves to a non-media type must still be rejected when the intent type is media, because the URI wins whenever it resolves.
…ypeMap MimeTypeMap is backed by libcore's MimeUtils, whose coverage of heic, heif, ogv and 3g2 varies by API level - exactly the types isAllowedMediaType() accepts and the ones Google Photos is most likely to hand us. Where the table is short the guard rejected a good extension, no rename happened, and the media fell back to the image/jpeg default this change exists to avoid. MimeTypes.getMimeTypeForExtension() searches the same lists isSupportedImageMimeType() and isSupportedVideoMimeType() gate on, so the guard is now symmetric with the accept check by construction instead of depending on platform coverage.
|
Overall looks good. I have a couple of points I think are worth to note:
screen-20260820-105120-1787215851574.mp4 |
I don't think either of those are related to changes in this branch, so it'd be worth filing separate issues for them. |


Description
TL/DR: Fixes #23047, where a photo shared from Google Photos opens a blank post. The blank post itself is already fixed on trunk by #23233. This PR closes two follow-on gaps in the same path that still mislabel or silently drop shared media.
The "Enhanced" framing in the issue is a red herring. What actually matters is whether the shared
content://URI resolves to a real file: "Original" arrives as a MediaStore URI with a_datacolumn, while "Enhanced" is a stream generated on the fly with no_dataand no extension anywhere. The photo picker, SAF/Files, Drive and Dropbox all behave the same way.MediaUtils.downloadExternalMedia()names its cached copy after the provider'sDISPLAY_NAME, falling back to a MIME type parsed out of the URI string — which acontent://URI never carries. A provider reporting no usable display name leaves a file calledwp-1755600000000., and since every MIME check downstream reads the file name,FluxCUtils.mediaModelFromLocalUri()runs out of fallbacks and defaults toimage/jpeg. A shared PNG, HEIC or video was uploaded labelled as a JPEG.ShareIntentReceiverActivitynow renames the copy using the type the provider reported, whichisAllowedMediaType()already resolves.Separately,
EditorUnitFunctions.isMediaTypeIntent()derived the type from the URI's file extension forACTION_SEND_MULTIPLE.getFileExtensionFromUrl()returns an empty string both for those extension-less names and for any name containing a space (Screenshot 2026-08-19.jpg), so sharing several photos at once dropped some of them with no toast and no snackbar. It now falls back tointent.type, the branch the single-share path already trusts, which covers bothEditPostActivityandGutenbergKitActivity.Two related problems are not addressed here and deserve their own issues: the copy still runs on the main thread (an ANR risk for cloud-only photos, but deliberate per #5818 — some providers revoke access as soon as the requesting context goes away), and
MediaUtils.getDocumentProviderPathKitkatOrHigher()in wordpress-utils dereferencesdownloadExternalMedia(...).getPath()with no null check.Testing instructions
Sharing a generated photo to a post:
Correct file type on upload (the main payoff of the first commit):
.jpg.Sharing several photos at once:
Media Library route: