我试图在我的应用程序中使用SAF选择器来查找我的自定义文件类型.当文件具有mime类型时,通过调用setType("text/plain")或类似方法,这样做很容易.
如果我想查找所有带有'.blah'扩展名的文件,那么这个电话会是什么样的?
编辑:更多信息:文件类型实际上是具有更改的扩展名的纯文本文件.在文件中,存在一些文本,我解析它从中创建一些数据结构.但是,当用户查找特定扩展名的文件时,我希望他们只查看我的自定义文件,而不是所有文本文件.
我正在使用类型意图ACTION_CREATE_DOCUMENT将文件从我的应用程序保存到设备上。有没有一种方法可以同时创建多个文档来实现此目的?我正在考虑使用EXTRA_ALLOW_MULTIPLE,但我不知道如何提供我想要创建的文档数量。
感谢您的帮助。
在http://developer.android.com/guide/topics/providers/document-provider.html#manifest上显示了如何在清单中注册自定义文档提供程序:
<manifest... >
...
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
....
<provider
android:name="com.example.android.storageprovider.MyCloudProvider"
android:authorities="com.example.android.storageprovider.documents"
android:grantUriPermissions="true"
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS"
android:enabled="@bool/atLeastKitKat">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>
</application>
</manifest>
Run Code Online (Sandbox Code Playgroud)
此处需要此<intent-filter>元素,但Android Studio会抱怨:
此处不允许元素intent-filter
并且提供者元素的文档似乎也表明了这一点:
CAN CONTAIN:
<meta-data>
<grant-uri-permission>
<path-permission>
Run Code Online (Sandbox Code Playgroud)
这是Android Studio和文档错误还是我错过了什么?
android android-manifest android-xml android-studio storage-access-framework
我正在尝试使用以下代码从我的应用程序中的Google驱动器访问PDF文件:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
startActivityForResult(intent, MY_ACTION_OPEN_DOCUMENT);
Run Code Online (Sandbox Code Playgroud)
这是直接从谷歌的文档中获取的.我能够让文件选择器出现.但是有两个问题:
我想知道我的实现中是否缺少某些内容.我试图搜索网络,并惊奇地发现没有任何相关性.更令人惊讶的是,我遇到了与gmail应用程序相同的问题.
提前致谢.
我正在使用 Android 存储访问框架来上传图像选择器。这是我的代码
Intent.createChooser(new
Intent(Intent.ACTION_GET_CONTENT).setType("image/*"), "Choose an image");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (multiple) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.putExtra(Intent.EXTRA_INDEX, 0);
}
Run Code Online (Sandbox Code Playgroud)
当 onActivityResult 被调用时,像下面这样解析它。
final ClipData clipData = data.getClipData();
int takeFlags = data.getFlags();
takeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
for(int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
Uri dataUri = item.getUri();
....
}
Run Code Online (Sandbox Code Playgroud)
问题是,当我选择多个图像时,顺序很奇怪。如果我按顺序选择 A、B、C 和 D,它将按顺序返回 B、C、D、A。我发现这是因为所选项目是按文档 ID 排序的。(例如,如果文档 uri 为 content://com.android.providers.media.documents/document/image%3A10473,则按“3A10473”排序)
存储访问框架总是这样工作的。无论用户按顺序选择什么,它都会返回按文档 ID 排序的项目。在得到结果之前我不知道文档 ID 是什么。
保留所选项目的顺序对我来说非常重要。我怎样才能使这成为可能?
谢谢。
我有一个应用程序,允许用户通过 CSV 或包含多个文件的压缩包导出和/或导入批量数据。
目前它使用第三方库(ar.com.daidalos.afiledialog),但我正在尝试迁移到用户更可能熟悉的存储访问框架(并且看起来也更“android-y”) )。当前的库无法使用云端硬盘,这是我想添加的额外功能。
我已经让它工作了,以便导出到两种格式都可以工作。我可以选择一个 CSV 文件来读取:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/*");
startActivityForResult(intent,4);
Run Code Online (Sandbox Code Playgroud)
但是,当我将类型更改为“application/zip”(或任何更宽松的内容)以使 zip 可选择时,文件选择器会将 zipfile 视为另一个目录并显示其内容而不是选择它。看起来它正在响应 .zip 扩展名,因为当我从文件中删除它时,它会使其变得可选择(如果我无法正确解决此问题,这可能会给我一个解决方法)。
所以问题是,我如何告诉选择器选择zip文件而不是深入其中?我必须使用 zip 文件,因为现有用户已经以他们可能需要读取的格式导出了数据。
在我的应用程序中,我想为用户提供一种从 app\xe2\x80\x99s 数据目录中选择文件的方法。这是我的代码:
\n\n// use ACTION_OPEN_DOCUMENT because ACTION_GET_CONTENT will give us\n// gallery and other stuff we don\xe2\x80\x99t need\nIntent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);\nintent.addCategory(Intent.CATEGORY_OPENABLE);\nUri uri = Uri.parse(getExternalFilesDir(null).getAbsolutePath());\nLog.d(TAG, "Browsing " + uri.toString());\nintent.setDataAndType(uri, "*/*");\n// show the entire internal storage tree\nintent.putExtra("android.content.extra.SHOW_ADVANCED", true);\nstartActivityForResult(intent, 42);\nRun Code Online (Sandbox Code Playgroud)\n\nlogcat 显示我设置的 URI 是file:///sdcard/Android/data/my.app/files,但文件选择器 UI 默认为共享存储根 ( /sdcard)。
以下代码有效(根据文档需要 API 26+,意图可从 API 中获取DocumentsContract.EXTRA_INITIAL_URI):
// works only with this intent, at the expense of gallery etc. appearing\nIntent intent = new Intent(Intent.ACTION_GET_CONTENT);\nintent.addCategory(Intent.CATEGORY_OPENABLE);\n\n// apparently we need …Run Code Online (Sandbox Code Playgroud) 在 Android Q 之前,如果我们想要获取有关 APK 文件的信息,我们可以使用WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE来访问存储,然后在文件路径上使用PackageManager.getPackageArchiveInfo函数。
类似的情况也存在,例如在压缩文件上使用ZipFile 类,可能还有无数的框架 API 和第三方库。
谷歌最近宣布了对 Android Q 的大量限制。
其中之一称为“范围存储”,当访问设备拥有的所有文件时,它会破坏存储权限。它可以让您处理媒体文件,或者使用非常受限的存储访问框架 ( SAF ),该框架不允许应用程序使用文件 API 和文件路径访问和使用文件。
当Android Q Beta 2发布时,它破坏了很多应用程序,包括谷歌的应用程序。原因是它默认打开,影响所有应用程序,无论它们是否针对 Android Q。
原因是许多应用程序、SDK 和 Android 框架本身 - 都经常使用文件 API。在许多情况下,它们也不支持 InputStream 或 SAF 相关的解决方案。一个例子就是我写的 APK 解析示例 (PackageManager.getPackageArchiveInfo)。
然而,在 Q beta 3 上,情况发生了一些变化,因此目标 Q 的应用程序将具有范围存储,并且有一个标志可以禁用它,但仍然像往常一样使用正常的存储权限和文件 API。遗憾的是,该标志只是暂时的(请阅读此处),因此它推迟了不可避免的事情。
我已经尝试并发现了接下来的事情:
android file storage-access-framework android-10.0 scoped-storage
尝试获取一个uri通过Storage Access Framework编辑音频文件时,如果用户从“音频”选项卡中进行选择,则显示java.lang.IllegalArgumentException: Media is read-only。
SAF非常适合将uri移至sdcard根目录,以及从存储卷本身中选择文件。但是,从SAF Picker的“音频”选项卡中进行选择会返回一个只读uri。当前,我有一条消息告诉用户只能从卷本身中进行选择,但是我的报告显示他们仍然相当频繁地使用它。
这是我当前调用SAF选择器的方式:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("audio/mpeg");
startActivityForResult(intent, REQUEST_CODE_SAF_READ_FILE);
Run Code Online (Sandbox Code Playgroud)
根据Android文档,在使用时Intent.CATEGORY_OPENABLE,
所有选定的文档将通过持久的读写权限授予返回到调用应用程序。
-这就是为什么出现只读错误是如此令人惊讶的原因。
有什么办法让我获得适当的可写uri?我知道某些提供程序可以是只读的,但是由于该提供程序由文件支持,有没有办法我可以查询只读uri指向的媒体文件的存储量?
我可以从SAF公开的只读uri中获取显示名称和文件大小,但是根据文档
...称为“显示名称”。这是特定于提供程序的,不一定是文件名。
任何建议表示赞赏。
我正在从服务器下载文件,并保存到android中的下载文件夹,通过以下方式保存访问文件路径
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
Run Code Online (Sandbox Code Playgroud)
在Android Q中已被弃用,所以我必须使用存储访问框架。
fun downloadFile(url: String, originalFileName: String) {
mDownloadDisposable.add(
mRCloudRepository.downloadFile(url)
.flatMap(object : Function1<Response<ResponseBody>, Flowable<File>> {
override fun invoke(p1: Response<ResponseBody>): Flowable<File> {
return Flowable.create({
try {
val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absoluteFile, originalFileName)
val sink = file.sink().buffer()
sink.writeAll(p1.body()!!.source())
sink.close()
it.onNext(file)
it.onComplete()
} catch (e: IOException) {
e.printStackTrace()
it.onError(e)
}
}, BackpressureStrategy.BUFFER)
}
})
.subscribeOn(mIoScheduler)
.observeOn(mUiScheduler)
.subscribe(
{
cancelNotification()
val contentIntent: PendingIntent = PendingIntent.getActivity(mContext, NotificationBuilder.NOTIFICATION_DOWNLOAD_ID, getOpenFileIntent(it), PendingIntent.FLAG_CANCEL_CURRENT)
NotificationBuilder.showDownloadedNotification(mContext!!, 2, "Downloaded $originalFileName ", "", contentIntent)
}, {
cancelNotification()
it.printStackTrace()
}
)
)
}
Run Code Online (Sandbox Code Playgroud)