访问 ActivityResult 上的公共下载文件 Android 28 Samsung Galaxy S9+ (Verizon)

2024-03-20


UPDATE

我有一台运行 8.0.0 T-Mobile 的三星 Galaxy S8+,运行正常 8.0.0

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

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

所以这可能是 OEM 特定型号问题,但不是 确定如何修复它。我也试过重启手机,不行 结果的改变。

另外,我从 Evernote 中打开了公共下载并保存了 文件作为笔记的附件,这告诉我 Evernote 能够 访问公共目录就好了并附加文件,所以它是 可以在设备上执行。让我相信这是代码 有关的。


所以我最近升级了一个项目,该项目运行得很好,但现在有一个错误,因为它正在使用最新版本的 Android 的构建工具 28 进行编译。

所以我一直使用这个 PathUtil 来从隐式意图中获取我需要的文件路径,以从用户那里获取文件选择。我将在下面分享我长期以来使用的代码的链接。

PathUtil https://gist.github.com/tatocaster/32aad15f6e0c50311626

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

当用户从公共下载目录中选择文件时,它返回到活动结果 with:

content://com.android.providers.downloads.documents/document/2025

现在,nice 实用程序解析了该文件,并告诉我这是一个下载目录文件,并且是一个 id 为 2025 的文档。感谢实用程序,这是一个很好的开始。

接下来是使用内容解析器来查找文件绝对路径。 这曾经是有效的,但现在不再有效:​​(。

现在,路径实用程序仅使用他们最有可能从核心库本身获取的合约数据。我尝试导入提供程序类以避免静态字符串,但它似乎不可用,所以我想简单地使用匹配字符串是目前最好的方法。

这是供参考的核心 DownloadProvider,它为内容解析器提供所有访问权限。下载提供者 https://android.googlesource.com/platform/packages/providers/DownloadProvider/+/master/src/com/android/providers/downloads/DownloadProvider.java

注意* 这个 DownloadProvider 是 Android 的,不是我的

以下是为 contentProvider 构建 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
    }

本质上要解析的 contentUri 最终是

内容://downloads/public_downloads/2025

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

java.lang.IllegalArgumentException:未知 URI:content://downloads/public_downloads/2025

我已经确认或尝试过的事情

  1. 读取外部权限(带有写入权限,但还是这么做了)
  2. 写入外部权限
  3. 权限位于清单中并在运行时检索
  4. 我选择了多个不同的文件,看看其中一个是否奇怪
  5. 我已确认在应用程序设置中授予了权限
  6. 我已将 Uri 硬编码为 /1 甚至 /#2052 最后尝试各种结束类型
  7. 我研究了核心库上的 uriMatching,以了解它期望如何格式化并确保它匹配
  8. 我已经尝试过 uri 中的 all_downloads 目录,并且可以解析!!,但由于安全异常,因此解析器必须存在。

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


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

我解决的方法是修改getPath的isDownloadDirectory路径流程。我还不知道所有的连锁反应,尽管质量检查将于明天开始,如果我从中学到任何新东西,我会更新。

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

然后接下来使用环境外部公共下载常量与返回的内容解析器名称相结合来获取绝对路径。新代码如下所示。

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
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

访问 ActivityResult 上的公共下载文件 Android 28 Samsung Galaxy S9+ (Verizon) 的相关文章

  • 适用于 IOS 和 Android 的支付网关 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在开发一个应用程序 用户必须在澳大利亚餐馆通过应用程序 android ios 付款 有两种付款方式 通过 PayPal 或 Visa
  • 在我的 Android 应用程序中使用 ServerValue.TIMESTAMP

    我读过很多相关的 stackoverflow 问题 ServerValue TIMESTAMP 但我不知道如何在我的应用程序中使用它 我需要获取帖子创建的时间戳 时间戳应该添加到与帖子的 uid 作者等相同的位置 代码片段其中写这篇文章Fi
  • 如何在 ADB 连接期间禁用电池充电?

    问题描述 每次我在电脑和手机之间连接 USB 线时 电池都会自动充电 我想使用 ADB 协议 但我不想在 ADB 连接期间为电池充电 是否可以关闭此充电功能 当然 我该怎么做呢 环境 Android 操作系统 4 及更高版本的手机 我只需要
  • 让协程等待之前的调用

    我还没有完全掌握 Kotlin 协程 基本上我希望协程在执行之前等待任何先前的调用完成 下面的代码似乎可以工作 但它正在做我认为它正在做的事情吗 private var saveJob Job null fun save saveJob s
  • Android 应用程序在启动时打开应用程序信息屏幕,而不是启动主 Activity

    我不确定这是否是一个问题 但这是我第一次遇到这个问题 我正在开发一个应用程序 当我在进行一些编码后断开应用程序与 Android Studio 和 PC 的连接时 如果我尝试在手机上打开应用程序 它会启动app info屏幕 我们看到强制停
  • 需要使用手机后退按钮返回 Web 视图的帮助

    这是我的代码 package com testappmobile import android app Activity import android os Bundle import android view KeyEvent impor
  • 如何在android中压缩和解压png图像

    您好 在我的应用程序中 当我单击 zip 按钮时 我需要压缩图像文件 当我单击解压缩按钮时 我需要解压缩文件 我尝试使用下面的代码来压缩图像 但我的问题是当我单击 zip 按钮时 正在创建 zip 文件 但之后在使用 winzip 软件的系
  • API29 上不推荐使用 setColorFilter

    我使用以下行来更改 VectorDrawable 的颜色 mydrawable getBackground setColorFilter color PorterDuff Mode SRC ATOP 这很好用 尽管它现在已被弃用 文档建议我
  • Android:我可以创建一个不是矩形的视图/画布吗?圆形的?

    我有一个圆形视图 悬停在主要内容上方 gt 从屏幕出来的 z 轴方向 当有人点击屏幕时 我希望选择主要内容或悬停在上方的视图 当它覆盖主视图时 到目前为止效果很好 我在透明画布上有一个圆形物品 这意味着您可以看到该圆圈之外的背景的所有内容
  • android中根据屏幕尺寸计算图像尺寸

    我正在尝试根据屏幕尺寸计算图像高度和宽度 我从后端获取 5 x 7 尺寸的图像 为了将像素乘以 72 进行转换 我有 360 X 504 尺寸的图像 对于 360 X 504 我的动态透明矩形区域将显示为 1 223 x 1 179 即 8
  • Android PhoneGap 插件,UI 选项卡栏,调整 WebView 大小

    我正在创建一个美味的 PhoneGap 插件 希望一旦它能被打开 准备好了 插件基本完成了 我只需要一个漂亮的用户界面 相互作用 简而言之 我想创建一个 本机 android 工具栏组件 如果您实现 PhoneGap UIControls
  • 如何在活动中的必填字段中显示 * 符号

    我需要在活动中的必填字段中显示 符号 你能建议我怎样才能做到这一点吗 任何帮助 将不胜感激 我想说 作为必填字段的标记不遵循本机 Android 主题 的组合setHint and setError对于 Android 应用程序来说看起来更
  • 使用 HttpUrlConnection Android 将 base64 编码的图像发送到服务器

    我正在尝试使用 HttpUrlConnection 将 base64 编码的图像发送到服务器 我遇到的问题是大多数图像均已成功发送 但有些图像会生成 FileNotFound 异常 我的图像编码代码可以在下面找到 public static
  • 使用 Android Studio 进行调试永远停留在“等待调试器”状态

    UPDATE The supposed重复是一个关于陷入 等待调试器 执行时Run 而这个问题就陷入了 等待调试器 执行时Debug 产生问题的步骤不同 解决方案也不同 每当我尝试使用Android Studio的调试功能时 运行状态总是停
  • Emma 不生成coverage.ec

    我设置了艾玛 它曾经对我有用 然后我们更改了源代码 现在它没有生成coverage ec根本不 它确实生成coverage em 测试临近结束时 出现错误消息 exec INSTRUMENTATION CODE 0 echo Downloa
  • 如何从android中的外部存储中获取所选文件的文件路径?

    我在选择文件的文件路径时遇到问题 我搜索了整个堆栈溢出 但问题没有解决 从设备中选择文件的代码如下所示 Intent intent new Intent Intent ACTION GET CONTENT intent setType in
  • 如何制作在手机和平​​板电脑上使用的响应式Android应用程序?

    我创建了一个 Android 应用程序 当我运行我的应用程序时Mobile Phone它工作得很好 但是当我跑进去时Tablet应用程序的布局已更改 那么 如何制作响应式Android应用程序用于Mobile并且也在Tablet 在Andr
  • Android 纹理仅显示纯色

    我正在尝试在四边形上显示单个纹理 我有一个可用的 VertexObject 它可以很好地绘制一个正方形 或任何几何对象 现在我尝试扩展它来处理纹理 但纹理不起作用 我只看到一种纯色的四边形 坐标数据位于 arrayList 中 the ve
  • 如何在 Android 上将动态 alpha 遮罩应用于文本

    I want to make a dynamic alpha mask with drawable shapes as circles or whatever and apply it to a drawed text on Android
  • 由于“进程崩溃”,仪器运行失败。

    我想运行以下测试 package com xxx yyy import android content Context import androidx test InstrumentationRegistry import androidx

随机推荐