Android 11 ACTION_OPEN_DOCUMENT_TREE: 将初始 URI 设置为文档文件夹
Android 11 ACTION_OPEN_DOCUMENT_TREE: set initial URI to the Documents folder
在 Android11 中使用分区存储模型我想让用户能够选择一个文件夹,从文档文件夹开始:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, ??? )
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE,null)
问题是,如何生成 phone 的文档文件夹的正确 URI? (它位于 root / )在官方文档中,没有给出示例。我真的希望所有标准位置都有一些简洁的常量?
how can I generate a proper URI of the phone's documents folder?
测试时间:
- 小米M2102J20SI
- 模拟器 Pixel 4 XL API 30
函数askPermission()
打开目标目录。
@RequiresApi(Build.VERSION_CODES.Q)
private fun askPermission() {
val storageManager = application.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val intent = storageManager.primaryStorageVolume.createOpenDocumentTreeIntent()
val targetDirectory = "WhatsApp%2FMedia%2F.Statuses" // add your directory to be selected by the user
var uri = intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI") as Uri
var scheme = uri.toString()
scheme = scheme.replace("/root/", "/document/")
scheme += "%3A$targetDirectory"
uri = Uri.parse(scheme)
intent.putExtra("android.provider.extra.INITIAL_URI", uri)
startActivityForResult(intent, REQUEST_CODE)
}
文件的 Uri 将在 onActivityResult()
中返回
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
if (data != null) {
data.data?.let { treeUri ->
// treeUri is the Uri of the file
// if life long access is required the takePersistableUriPermission() is used
contentResolver.takePersistableUriPermission(
treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
readSDK30(treeUri)
}
}
}
}
函数readSDK30()
用于从Uri
读取文件和文件夹
private fun readSDK30(treeUri: Uri) {
val tree = DocumentFile.fromTreeUri(this, treeUri)!!
thread {
val uriList = arrayListOf<Uri>()
listFiles(tree).forEach { uri ->
// Collect all the Uri from here
}
}
}
函数listFiles()
returns给定Uri
中的所有文件和文件夹
fun listFiles(folder: DocumentFile): List<Uri> {
return if (folder.isDirectory) {
folder.listFiles().mapNotNull { file ->
if (file.name != null) file.uri else null
}
} else {
emptyList()
}
}
我们将操纵从 StorageManager..getPrimaryStorageVolume().createOpenDocumentTreeIntent()
获得的 INITIAL_URI
。
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Intent intent = sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent();
//String startDir = "Android";
//String startDir = "Download"; // Not choosable on an Android 11 device
//String startDir = "DCIM";
//String startDir = "DCIM/Camera"; // replace "/", "%2F"
//String startDir = "DCIM%2FCamera";
String startDir = "Documents";
Uri uri = intent.getParcelableExtra("android.provider.extra.INITIAL_URI");
String scheme = uri.toString();
Log.d(TAG, "INITIAL_URI scheme: " + scheme);
scheme = scheme.replace("/root/", "/document/");
scheme += "%3A" + startDir;
uri = Uri.parse(scheme);
intent.putExtra("android.provider.extra.INITIAL_URI", uri);
Log.d(TAG, "uri: " + uri.toString());
((Activity) context).startActivityForResult(intent, REQUEST_ACTION_OPEN_DOCUMENT_TREE);
return;
}
所有功劳都归于上述@blackapp 的!
下面是 Kotlin 语言的相同代码:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
val sm = getSystemService(Context.STORAGE_SERVICE) as StorageManager
intent = sm.primaryStorageVolume.createOpenDocumentTreeIntent()
//String startDir = "Android";
//String startDir = "Download"; // Not choosable on an Android 11 device
//String startDir = "DCIM";
//String startDir = "DCIM/Camera"; // replace "/", "%2F"
//String startDir = "DCIM%2FCamera";
val startDir = "Documents"
var uriroot = intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI") // get system root uri
var scheme = uriroot.toString()
Log.d("Debug", "INITIAL_URI scheme: $scheme")
scheme = scheme.replace("/root/", "/document/")
scheme += "%3A$startDir" //change uri to Documents folder
uriroot = Uri.parse(scheme)
intent.putExtra("android.provider.extra.INITIAL_URI", uriroot) // give changed uri to Intent
Log.d("Debug", "uri: $uriroot")
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
}
正如一些评论者所提到的,此代码可能会损坏并且将来无法正常工作,这是事实。不过,考虑到Android的过去,他们无论如何都会每隔一年更换一次存储空间API。
在 Android11 中使用分区存储模型我想让用户能够选择一个文件夹,从文档文件夹开始:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, ??? )
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE,null)
问题是,如何生成 phone 的文档文件夹的正确 URI? (它位于 root / )在官方文档中,没有给出示例。我真的希望所有标准位置都有一些简洁的常量?
how can I generate a proper URI of the phone's documents folder?
测试时间:
- 小米M2102J20SI
- 模拟器 Pixel 4 XL API 30
函数askPermission()
打开目标目录。
@RequiresApi(Build.VERSION_CODES.Q)
private fun askPermission() {
val storageManager = application.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val intent = storageManager.primaryStorageVolume.createOpenDocumentTreeIntent()
val targetDirectory = "WhatsApp%2FMedia%2F.Statuses" // add your directory to be selected by the user
var uri = intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI") as Uri
var scheme = uri.toString()
scheme = scheme.replace("/root/", "/document/")
scheme += "%3A$targetDirectory"
uri = Uri.parse(scheme)
intent.putExtra("android.provider.extra.INITIAL_URI", uri)
startActivityForResult(intent, REQUEST_CODE)
}
文件的 Uri 将在 onActivityResult()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
if (data != null) {
data.data?.let { treeUri ->
// treeUri is the Uri of the file
// if life long access is required the takePersistableUriPermission() is used
contentResolver.takePersistableUriPermission(
treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
readSDK30(treeUri)
}
}
}
}
函数readSDK30()
用于从Uri
private fun readSDK30(treeUri: Uri) {
val tree = DocumentFile.fromTreeUri(this, treeUri)!!
thread {
val uriList = arrayListOf<Uri>()
listFiles(tree).forEach { uri ->
// Collect all the Uri from here
}
}
}
函数listFiles()
returns给定Uri
fun listFiles(folder: DocumentFile): List<Uri> {
return if (folder.isDirectory) {
folder.listFiles().mapNotNull { file ->
if (file.name != null) file.uri else null
}
} else {
emptyList()
}
}
我们将操纵从 StorageManager..getPrimaryStorageVolume().createOpenDocumentTreeIntent()
获得的 INITIAL_URI
。
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Intent intent = sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent();
//String startDir = "Android";
//String startDir = "Download"; // Not choosable on an Android 11 device
//String startDir = "DCIM";
//String startDir = "DCIM/Camera"; // replace "/", "%2F"
//String startDir = "DCIM%2FCamera";
String startDir = "Documents";
Uri uri = intent.getParcelableExtra("android.provider.extra.INITIAL_URI");
String scheme = uri.toString();
Log.d(TAG, "INITIAL_URI scheme: " + scheme);
scheme = scheme.replace("/root/", "/document/");
scheme += "%3A" + startDir;
uri = Uri.parse(scheme);
intent.putExtra("android.provider.extra.INITIAL_URI", uri);
Log.d(TAG, "uri: " + uri.toString());
((Activity) context).startActivityForResult(intent, REQUEST_ACTION_OPEN_DOCUMENT_TREE);
return;
}
所有功劳都归于上述@blackapp 的
下面是 Kotlin 语言的相同代码:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
val sm = getSystemService(Context.STORAGE_SERVICE) as StorageManager
intent = sm.primaryStorageVolume.createOpenDocumentTreeIntent()
//String startDir = "Android";
//String startDir = "Download"; // Not choosable on an Android 11 device
//String startDir = "DCIM";
//String startDir = "DCIM/Camera"; // replace "/", "%2F"
//String startDir = "DCIM%2FCamera";
val startDir = "Documents"
var uriroot = intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI") // get system root uri
var scheme = uriroot.toString()
Log.d("Debug", "INITIAL_URI scheme: $scheme")
scheme = scheme.replace("/root/", "/document/")
scheme += "%3A$startDir" //change uri to Documents folder
uriroot = Uri.parse(scheme)
intent.putExtra("android.provider.extra.INITIAL_URI", uriroot) // give changed uri to Intent
Log.d("Debug", "uri: $uriroot")
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
}
正如一些评论者所提到的,此代码可能会损坏并且将来无法正常工作,这是事实。不过,考虑到Android的过去,他们无论如何都会每隔一年更换一次存储空间API。