访问ActivityResult Android 28 Samsung Galaxy S9+(Verizon)上的公开下载文件 [英] Accessing public download files onActivityResult Android 28 Samsung Galaxy S9+ (Verizon)

查看:18
本文介绍了访问ActivityResult Android 28 Samsung Galaxy S9+(Verizon)上的公开下载文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


更新

我的三星Galaxy S8+运行8.0.0 T-Mobile,它在运行时运行得很好 8.0.0

我的三星Galaxy S9+运行8.0.0 Verizon,每次都会因为非法参数而失败。

我的三星Galaxy S9+运行8.0.0 T-Mobile没有问题,工作正常

因此,这可能是OEM特定型号的问题,但不是 当然了,怎么修好它呢。我也试过重启手机,没有 结果发生变化。

此外,我还从Evernote中打开了公共下载并保存了 文件作为Note的附件,这告诉我Evernote能够 为了很好地访问公共目录并附加文件,所以它是 可以在设备上进行操作。让我相信这是代码 相关。


所以我最近升级了一个运行良好的项目,现在它有一个错误,因为它是用最新版本的Android的Build Tools 28编译的。

所以我总是使用这个PathUtil从隐式意图中获取所需的文件路径,以便从用户那里获得文件选择。我将在下面共享指向我现在使用了很长时间的代码的链接。

PathUtil

它只是一个实用程序类,用于检查提供程序权限并获取您尝试读取的文件的绝对路径。

当用户从公共下载目录中选择一个文件时,它将返回到onActivityResult,并显示:

content://com.android.providers.downloads.documents/document/2025
现在,NICE实用程序解析出来,并告诉我这是一个下载目录文件,是一个ID为2025的文档。感谢实用程序,这是一个很好的开始。

下一步是使用内容解析器查找文件的绝对路径。 这是过去有效的方法,但现在不起作用了:(.

现在,路径实用程序只使用他们自己最有可能从核心库获得的约定数据。我试图导入提供程序类以避免静态字符串,但它似乎不可用,所以我想现在最好的方法就是简单地使用匹配字符串。

下面是核心DownloadProvider以供参考,它为内容解析器提供所有访问权限。 DownloadProvider

注意*此DownloadProvider是Android,不是我的

以下是为Content Provider构建URI的代码

 val id = DocumentsContract.getDocumentId(uri)
 val contentUri = ContentUris.withAppendedId(Uri.parse(PUBLIC_DOWNLOAD_PATH), id.toLong())
 return getDataColumn(context, contentUri, null, null)

调用引用:

    private fun getDataColumn(context: Context, uri: Uri, selection: String?, selectionArgs: Array<String>?): String? {
        var cursor: Cursor? = null
        val column = "_data"
        val projection = arrayOf(column)
        try {
            cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
            if (cursor != null && cursor.moveToFirst()) {
                val column_index = cursor.getColumnIndexOrThrow(column)
                return cursor.getString(column_index)
            }
        }catch (ex: Exception){
            A35Log.e("PathUtils", "Error getting uri for cursor to read file: ${ex.message}")
        } finally {
            if (cursor != null)
                cursor.close()
        }
        return null
    }

基本上要解析的Content Uri最终为

content://downloads/public_downloads/2025

然后,当您调用查询方法时,它会抛出:

java.lang.IlLegalArgumentException:未知URI:Content://Downloads/Public_Downloads/2025

我已确认或尝试的事情

  1. 读取外部权限(附带写入,但仍执行了该操作)
  2. 写入外部权限
  3. 权限在清单中,并在运行时检索
  4. 我已经选择了多个不同的文件,看看是否有一个文件很奇怪
  5. 我已确认在应用程序设置中授予权限
  6. 我已将URI硬编码为/1,甚至是结尾的/#2052,以尝试各种结尾类型
  7. 我研究了核心库上的uriMatking,以了解它希望如何格式化它并确保它匹配
  8. 我已经处理过URI中的all_ownloads目录,它可以解析!!,但存在安全异常,因此解析程序必须存在。

我不知道还可以尝试什么,任何帮助都将不胜感激。

推荐答案

所以我仍然需要做一些向后兼容的测试,但经过几个小时的反复试验,我已经成功地解决了我自己的问题。

我是如何解决这个问题的,就是修改getPath的isDownloadDirectory路径流。我还不知道所有的连锁反应,但由于QA明天将开始对它进行测试,如果我从中了解到任何新情况,我会更新的。

使用直接URI获取文件名的Content Resolver(注意*这不是获取文件名的好方法,除非您确定它是Google的本地文件,但对我来说,我确定它已下载。)

接下来,使用Environment外部公共下载常量和返回的内容解析器名称来获得您的绝对路径。新代码如下所示。

private val PUBLIC_DOWNLOAD_PATH = "content://downloads/public_downloads"
private val EXTERNAL_STORAGE_DOCUMENTS_PATH = "com.android.externalstorage.documents"
private val DOWNLOAD_DOCUMENTS_PATH = "com.android.providers.downloads.documents"
private val MEDIA_DOCUMENTS_PATH = "com.android.providers.media.documents"
private val PHOTO_CONTENTS_PATH = "com.google.android.apps.photos.content"

//HELPER METHODS
    private fun isExternalStorageDocument(uri: Uri): Boolean {
        return EXTERNAL_STORAGE_DOCUMENTS_PATH == uri.authority
    }
    private fun isDownloadsDocument(uri: Uri): Boolean {
        return DOWNLOAD_DOCUMENTS_PATH == uri.authority
    }
    private fun isMediaDocument(uri: Uri): Boolean {
        return MEDIA_DOCUMENTS_PATH == uri.authority
    }
    private fun isGooglePhotosUri(uri: Uri): Boolean {
        return PHOTO_CONTENTS_PATH == uri.authority
    }

 fun getPath(context: Context, uri: Uri): String? {
    if (DocumentsContract.isDocumentUri(context, uri)) {
        if (isExternalStorageDocument(uri)) {
            val docId = DocumentsContract.getDocumentId(uri)
            val split = docId.split(COLON.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            val type = split[0]
            val storageDefinition: String
            if (PRIMARY_LABEL.equals(type, ignoreCase = true)) {
                return Environment.getExternalStorageDirectory().toString() + FORWARD_SLASH + split[1]
            } else {
                if (Environment.isExternalStorageRemovable()) {
                    storageDefinition = EXTERNAL_STORAGE
                } else {
                    storageDefinition = SECONDARY_STORAGE
                }
                return System.getenv(storageDefinition) + FORWARD_SLASH + split[1]
            }
        } else if (isDownloadsDocument(uri)) {
            //val id = DocumentsContract.getDocumentId(uri) //MAY HAVE TO USE FOR OLDER PHONES, HAVE TO TEST WITH REGRESSION MODELS
            //val contentUri = ContentUris.withAppendedId(Uri.parse(PUBLIC_DOWNLOAD_PATH), id.toLong()) //SAME NOTE AS ABOVE
            val fileName = getDataColumn(context, uri, null, null)
            var uriToReturn: String? = null
            if(fileName != null){
                uriToReturn = Uri.withAppendedPath(Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath), fileName).toString()
            }
            return uriToReturn
        } else if (isMediaDocument(uri)) {
            val docId = DocumentsContract.getDocumentId(uri)
            val split = docId.split(COLON.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            val type = split[0]
            var contentUri: Uri? = null
            if (IMAGE_PATH == type) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            } else if (VIDEO_PATH == type) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            } else if (AUDIO_PATH == type) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
            }
            val selection = "_id=?"
            val selectionArgs = arrayOf(split[1])
            return getDataColumn(context, contentUri!!, selection, selectionArgs)
        }
    } else if (CONTENT.equals(uri.scheme, ignoreCase = true)) {
        return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
    } else if (FILE.equals(uri.scheme, ignoreCase = true)) {
        return uri.path
    }
    return null
}




    private fun getDataColumn(context: Context, uri: Uri, selection: String?, selectionArgs: Array<String>?): String? {
        var cursor: Cursor? = null
        //val column = "_data" REMOVED IN FAVOR OF NULL FOR ALL   
        //val projection = arrayOf(column) REMOVED IN FAVOR OF PROJECTION FOR ALL 
        try {
            cursor = context.contentResolver.query(uri, null, selection, selectionArgs, null)
            if (cursor != null && cursor.moveToFirst()) {
                val columnIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME) //_display_name
                return cursor.getString(columnIndex) //returns file name
            }
        }catch (ex: Exception){
            A35Log.e(SSGlobals.SEARCH_STRING + "PathUtils", "Error getting uri for cursor to read file: ${ex.message}")
        } finally {
            if (cursor != null)
                cursor.close()
        }
        return null
    }

这篇关于访问ActivityResult Android 28 Samsung Galaxy S9+(Verizon)上的公开下载文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆