Skip to content

Commit 5988b2c

Browse files
Merge pull request #1925 from miguelpruivo/fix/restore-msf-uri-handling
Fix: Restore ms[df] URI handling logic
2 parents c218f3c + 730a609 commit 5988b2c

File tree

3 files changed

+46
-10
lines changed

3 files changed

+46
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 10.3.8
2+
### Android
3+
- Restores the ms[df] URI handling logic in FileUtils.kt to fix file selection returning null on some devices.
4+
15
## 10.3.7
26
### Android
37
- Fixed an issue where file type filtering was not being applied correctly, now only displaying files that match the selected MIME types. [#1906](https://github.com/miguelpruivo/flutter_file_picker/pull/1906)

android/src/main/kotlin/com/mr/flutter/plugin/filepicker/FileUtils.kt

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ object FileUtils {
8181
uri,
8282
DocumentsContract.getTreeDocumentId(uri)
8383
)
84-
val dirPath = getFullPathFromTreeUri(uri)
84+
val dirPath = getFullPathFromTreeUri(uri, activity)
8585
if (dirPath != null) {
8686
finishWithSuccess(dirPath)
8787
} else {
@@ -131,6 +131,14 @@ object FileUtils {
131131
}
132132
}
133133

134+
/**
135+
* Creates and launches an intent for the given file type.
136+
*
137+
* This method is responsible for creating the appropriate intent based on the [type] of file
138+
* that is requested to be picked.
139+
*
140+
* This may be either a directory, a regular file, or a gallery pick.
141+
*/
134142
fun FilePickerDelegate.startFileExplorer() {
135143
val intent: Intent
136144

@@ -142,7 +150,8 @@ object FileUtils {
142150
if (type == "dir") {
143151
intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
144152
} else {
145-
if (type != "*/*") {
153+
if (type == "image/*") {
154+
// Use ACTION_PICK for images to allow using the Gallery app, which provides a better UX for image selection.
146155
intent = Intent(Intent.ACTION_PICK)
147156
val uri = (Environment.getExternalStorageDirectory().path + File.separator).toUri()
148157
intent.setDataAndType(uri, type)
@@ -159,7 +168,12 @@ object FileUtils {
159168
intent.putExtra(Intent.EXTRA_MIME_TYPES, allowedExtensions)
160169
}
161170
} else {
162-
intent = Intent(Intent.ACTION_GET_CONTENT).apply {
171+
// Use ACTION_OPEN_DOCUMENT to allow selecting files from any document provider (SAF).
172+
// We prefer ACTION_OPEN_DOCUMENT over ACTION_GET_CONTENT because it offers persistent
173+
// access to the files via URI permissions, which is crucial for some use cases
174+
// (e.g. caching, repeated access). ACTION_GET_CONTENT is more suitable for
175+
// "importing" content and might not provide a persistent URI.
176+
intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
163177
addCategory(Intent.CATEGORY_OPENABLE)
164178
type = this@startFileExplorer.type
165179
if (!allowedExtensions.isNullOrEmpty()) {
@@ -186,6 +200,16 @@ object FileUtils {
186200
}
187201
}
188202

203+
/**
204+
* Called by the plugin to start a new file explorer activity.
205+
*
206+
* @param type The file types that will be selectable.
207+
* @param isMultipleSelection Whether multiple files can be selected.
208+
* @param withData Whether the file data should be loaded into memory.
209+
* @param allowedExtensions The allowed file extensions for custom file types.
210+
* @param compressionQuality The compression quality for images.
211+
* @param result The MethodChannel result to send the file picking result to.
212+
*/
189213
fun FilePickerDelegate?.startFileExplorer(
190214
type: String?,
191215
isMultipleSelection: Boolean?,
@@ -413,7 +437,7 @@ object FileUtils {
413437
try {
414438
context.contentResolver.openInputStream(originalImageUri).use { imageStream ->
415439
val compressFormat = getCompressFormat(context, originalImageUri)
416-
val compressedFile = createImageFile(context, originalImageUri, compressFormat)
440+
val compressedFile = createImageFile(context, compressFormat)
417441
val originalBitmap = BitmapFactory.decodeStream(imageStream)
418442
// Compress and save the image
419443
val fileOutputStream = FileOutputStream(compressedFile)
@@ -429,7 +453,7 @@ object FileUtils {
429453
}
430454

431455
@Throws(IOException::class)
432-
private fun createImageFile(context: Context, uri: Uri, compressFormat: Bitmap.CompressFormat): File {
456+
private fun createImageFile(context: Context, compressFormat: Bitmap.CompressFormat): File {
433457
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
434458
val imageFileName = "IMAGE_" + timeStamp + "_"
435459
val storageDir = context.cacheDir
@@ -557,7 +581,7 @@ object FileUtils {
557581
}
558582

559583
@JvmStatic
560-
fun getFullPathFromTreeUri(treeUri: Uri?): String? {
584+
fun getFullPathFromTreeUri(treeUri: Uri?, context: Context): String? {
561585
if (treeUri == null) {
562586
return null
563587
}
@@ -569,6 +593,14 @@ object FileUtils {
569593
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
570594
if (docId == "downloads") {
571595
return extPath
596+
} else if (docId.matches("^ms[df]:.*".toRegex())) {
597+
// Handle "msf:" (Media Store File) and "msd:" (Media Store Directory) prefixes.
598+
// These are commonly seen on Android 10+ (API 29+) when selecting files from the
599+
// "Downloads" category in the system picker.
600+
// Note that this does not happen on all devices.
601+
// Example URI: content://com.android.providers.downloads.documents/document/msf:1000000033
602+
val fileName = getFileName(treeUri, context)
603+
return "$extPath/$fileName"
572604
} else if (docId.startsWith("raw:")) {
573605
return docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }
574606
.toTypedArray()[1]
@@ -580,13 +612,13 @@ object FileUtils {
580612
var volumePath = getPathFromTreeUri(treeUri)
581613

582614
if (volumePath.endsWith(File.separator)) {
583-
volumePath = volumePath.substring(0, volumePath.length - 1)
615+
volumePath = volumePath.dropLast(1)
584616
}
585617

586618
var documentPath = getDocumentPathFromTreeUri(treeUri)
587619

588620
if (documentPath.endsWith(File.separator)) {
589-
documentPath = documentPath.substring(0, documentPath.length - 1)
621+
documentPath = documentPath.dropLast(1)
590622
}
591623
return if (documentPath.isNotEmpty()) {
592624
if (volumePath.endsWith(documentPath)) {
@@ -623,4 +655,4 @@ object FileUtils {
623655

624656
file.delete()
625657
}
626-
}
658+
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: A package that allows you to use a native file explorer to pick sin
33
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
44
repository: https://github.com/miguelpruivo/flutter_file_picker
55
issue_tracker: https://github.com/miguelpruivo/flutter_file_picker/issues
6-
version: 10.3.7
6+
version: 10.3.8
77

88
dependencies:
99
flutter:

0 commit comments

Comments
 (0)