fix(android): stop mangling the file name for extensions Android doesn't recognize in saveFile - #2201
Merged
Conversation
…n't recognize in saveFile Saving with an extension unregistered in the device's MimeTypeMap, e.g. trace.gpx, produced trace.gpx.xml.gpx. getMimeTypeForBytes fell back to sniffing the file's byte content when the extension was unknown, and a GPX file is valid XML, so it detected text/xml. The system's create document picker honored that declared type and appended its own default extension for it to the suggested name, turning trace.gpx into trace.gpx.xml. A later cleanup step meant to append a missing extension then appended the original one on top of that instead of replacing the mismatched one, since it only checked whether the current name already ended with the extension we asked for, never that it ended with a different one. Extensions unknown to the device now get a wildcard mime type instead of a sniffed one, which has no default extension of its own for the picker to enforce. The cleanup step now also recognizes our extension followed by exactly one more as a system appended mismatch and strips both, recovering the true base name, instead of only handling the case where no extension is present at all. Fixes #2199
1 task
navaronbracke
approved these changes
Sep 9, 2026
|
Hello, |
Co-authored-by: Navaron Bracke <brackenavaron@gmail.com>
vicajilau
enabled auto-merge
September 10, 2026 04:21
vicajilau
deleted the
fix/android-savefile-unknown-extension-mimetype
branch
September 10, 2026 04:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2199.
The bug
Saving a file with an extension unregistered in the device's
MimeTypeMap(e.g..gpx, a niche XML based format) produces a mangled name. The report's example: savingtrace.gpxproducedtrace.gpx.xml.gpx.Root cause, traced through the actual save flow
getMimeTypeForBytes(FileUtils.kt) doesn't findgpxin the device'sMimeTypeMap, so it falls back to sniffing the file's byte content. A GPX file is valid XML, so the sniff correctly detectstext/xml, a real but wrong answer for what the caller asked for.ACTION_CREATE_DOCUMENTintent alongside the suggested titletrace.gpx. The system's document picker honors the declared type and "corrects" the suggested name by appending its own default extension fortext/xml,.xml, producing the actual created documenttrace.gpx.xml.maybeRenameGenericMimeDuplicateruns afterward to append the extension when the picker didn't add one (the case it was written for, generic mime types likeapplication/octet-stream). It only checks whether the current name already ends with the extension we asked for,gpx, never whether it ends with a different one.trace.gpx.xmldoesn't end in.gpx, so the whole string is treated as the base name and.gpxgets appended on top, producingtrace.gpx.xml.gpx.Not specific to
.gpx, any extension unknown to the device whose content happens to sniff as a recognizable generic format (XML, in this case, likely others too) can hit the same chain.The fix
getMimeTypeForBytes: when the extension is present but unknown toMimeTypeMap, use a wildcard mime type (*/*) instead of falling through to content sniffing. A wildcard has no default extension of its own, so the picker has nothing to "correct" the name with.extractBaseNameAndSuffix(used bymaybeRenameGenericMimeDuplicate): added a case that recognizes our extension immediately followed by exactly one more extension, e.g.trace.gpx.xml, and strips both, recovering the true base name (trace) instead of only handling the case where no extension is present at all.Verified the new regex's matching behavior directly (a Kotlin
Regexand Python'sreshare the same backtracking semantics for this pattern) against the exact reported name plus a few edge cases (a base name containing a literal dot, the existing Android collision suffix style, a name that already has the right extension), all resolve to the expected base name.What I could not verify myself
There's no existing Kotlin unit test setup in this module (only Dart tests), and reproducing this needs an actual Android device/emulator plus a real "create document" system picker interaction, which isn't something I can drive from here. @nicolaspernoud if you're able to test this branch against your actual repro (saving with
fileName: 'trace.gpx',allowedExtensions: ["gpx"]), that would be the most reliable confirmation before merging. You can point at it with:Test plan
flutter analyzecleanandroid_file_pickerto 1.1.1 with a changelog entry