Why ContentResolver does not see added files by another app?

≡放荡痞女 提交于 2021-02-07 16:43:28

问题


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:

  1. Your app can always access its own directories.
  2. 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 the READ_EXTERNAL_STORAGE permission.
  3. 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:

  1. As accessing files through the MediaStore.Files works for you now, you can try to request READ_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.
  2. You can use ACTION_OPEN_DOCUMENT or ACTION_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.
  3. 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.
  4. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!