问题
I added files to Documents/MyExcelsFolder
by using ContentResolver.insert
and then also added new file to Documents/MyExcelsFolder
folder by another app (for ex. FileManager)
Then I try to get all files from the MyExcelsFolder
folder
fun getAppFiles(context: Context): List<AppFile> {
val appFiles = mutableListOf<AppFile>()
val contentResolver = context.contentResolver
val columns = mutableListOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.MIME_TYPE
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
add(
MediaStore.MediaColumns.RELATIVE_PATH
)
}
}.toTypedArray()
val extensions = listOf("xls", "xlsx")
val mimes = extensions.map { MimeTypeMap.getSingleton().getMimeTypeFromExtension(it) }
val selection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
"${MediaStore.MediaColumns.RELATIVE_PATH} LIKE ?"
} else {
"${MediaStore.Images.Media.DATA} LIKE ?"
}
val selectionArgs = arrayOf(
"%${Environment.DIRECTORY_DOCUMENTS}/MyExcelsFolder%"
)
contentResolver.query(
MediaStore.Files.getContentUri("external"),
columns,
selection,
selectionArgs,
MediaStore.Files.FileColumns.DATE_ADDED + " DESC"
)?.use { cursor ->
while (cursor.moveToNext()) {
val pathColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
val mimeColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE)
val filePath = cursor.getString(pathColumnIndex)
val mimeType = cursor.getString(mimeColumnIndex)
if (mimeType != null && mimes.contains(mimeType)) {
// handle cursor
appFiles.add(cursor.toAppFile())
} else {
// need to check extension, because the Mime Type is null
val extension = File(filePath).extension
if (extensions.contains(extension)) {
// handle cursor
appFiles.add(cursor.toAppFile())
}
}
}
}
return appFiles
}
fun Cursor.toAppFile(): AppFile {
val cursor = this
val idColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)
val nameColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val mimeColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE)
val pathColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
val id = cursor.getLong(idColumnIndex)
val uri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id)
val fileDisplayName = cursor.getString(nameColumnIndex)
val filePath = cursor.getString(pathColumnIndex)
var mimeType = cursor.getString(mimeColumnIndex)
val relativePath = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.RELATIVE_PATH))
} else {
null
}
var type = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
if (type == null) {
type = File(filePath).extension
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(type)
}
return AppFile(
id = id,
uri = uri,
absolutePath = filePath,
name = fileDisplayName,
mimeType = mimeType,
extension = type,
relativePath = relativePath
)
}
And in result there are only files from ContentResolver
added by insert
command, and there no files copied by FileManager. How to see all files in cursor
?
Operation system: Android 10 (Q) (API level 29)
Target API version: api 29
回答1:
Starting from Android 10 there is a new storage access model in action which is called Scoped Storage and it is much more restrictive. In short:
- Your app can always access its own directories.
- Your app can write (with help of
ContentResolver.insert
) to the shared media collections and can read only files created by your app from them. You can access other apps files from these collections by requesting theREAD_EXTERNAL_STORAGE
permission. - Your app can access other files and directories by using file or directory system pickers.
It's a bit weird and looks like a bug that you are able to access xls
files through the MediaStore.Files
collection. The documentation says
The media store also includes a collection called
MediaStore.Files
. Its contents depend on whether your app uses scoped storage, available on apps that target Android 10 or higher:If scoped storage is enabled, the collection shows only the photos, videos, and audio files that your app has created.
If scoped storage is unavailable or not being used, the collection shows all types of media files.
But anyway you still are not able to access files created by other apps as stated above. So depending on your use case there are a few options to go:
- As accessing files through the
MediaStore.Files
works for you now, you can try to requestREAD_EXTERNAL_STORAGE
permission as shown in this table to get a non-filtered access to media collections. But I would expect such way to work unreliably on different devices and/or expect to stop it working with the new updates, because the media collections are supposed to be used for media files only. - You can use
ACTION_OPEN_DOCUMENT
orACTION_OPEN_DOCUMENT_TREE
to show a file/directory picker to user and get access to a file or the whole directory tree. Please check the restrictions of this method also. I'd say this is the preferable way to go. - Android 10 allows you to temporarily opt-out from the scoped storage by using the
android:requestLegacyExternalStorage
flag. But Android 11 is out already and this flag does not have any effect in it. - You can request the new
MANAGE_EXTERNAL_STORAGE
permission and then request a special white-listing by user to access all files. That's how the file managers work now. This feature is available starting from Android 11, so you'll likely to use the opt-out flag for Android 10. Also be sure to check the restrictions and Google Play policies on using this feature if you are going to publish your app there.
来源:https://stackoverflow.com/questions/64276816/why-contentresolver-does-not-see-added-files-by-another-app