持久权限 URI android Q

Persistable permissions URI android Q

我正在使用模拟器并以这种方式发送意图:

StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Intent i;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
   i = sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent();
} else {
   return null;
}
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
i.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);

并在 onActivityResult() 中:

Uri uri = data.getData();
if (uri == null)
    return null;
context.getContentResolver()
                .takePersistableUriPermission(uri,
                        Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

每次我重新启动时,应用程序都无法访问。任何人都可以在模拟器上确认 beta 6 上的这个错误吗?我做错了什么吗?

我将你的代码片段转换成这个 Kotlin activity:

package com.commonsware.jetpack.myapplication

import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.os.storage.StorageManager
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile

private const val PREF_URI = "uri"
private const val REQUEST_SAF = 1337

class MainActivity : AppCompatActivity() {
  private val prefs: SharedPreferences by lazy {
    getSharedPreferences("test", Context.MODE_PRIVATE)
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val uriString = prefs.getString(PREF_URI, null)
    val storageManager = getSystemService(StorageManager::class.java)!!

    if (uriString == null) {
      startActivityForResult(
        storageManager.primaryStorageVolume.createOpenDocumentTreeIntent(),
        REQUEST_SAF
      )
    } else {
      val uri = Uri.parse(uriString)
      val docFile = DocumentFile.fromTreeUri(this, uri)

      Toast.makeText(
        this,
        "canRead: ${docFile?.canRead()} canWrite: ${docFile?.canWrite()}",
        Toast.LENGTH_LONG
      ).show()
    }
  }

  override fun onActivityResult(
    requestCode: Int,
    resultCode: Int,
    data: Intent?
  ) {
    val uri = data?.data

    if (uri == null) {
      Toast.makeText(this, "Did not get a Uri??!?", Toast.LENGTH_LONG).show()
    } else {
      contentResolver.takePersistableUriPermission(
        uri,
        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
      )

      prefs.edit().putString(PREF_URI, uri.toString()).apply()
      Toast.makeText(this, "OK, run the activity again", Toast.LENGTH_LONG).show()
    }

    finish()
  }
}

然后我 运行 包含此 activity 的应用程序作为其启动器 activity 并选择 Downloads/ 作为文档树。此 activity 的后续启动 - 在重新启动之前和之后 - 显示 canRead()canWrite() 两者 return true。这是在 Google Pixel 运行 Q Beta 6 上测试的。

我无法在 Q 模拟器上尝试此操作 because Google,因此我无法确认是否可以在那里重现您的行为。但是,这在硬件上按预期工作的事实应该是一个积极的信号。

对于那些欣赏接受的答案但不想使用 startActivityForResult 的人,在接受原始答案后已弃用。

我的示例展示了如何从您的应用程序中将文件保存到存储空间。

首先,创建一个 class 范围的变量来保存 ActivityResultLauncher 信息:

private lateinit var saveDataResultLauncher: ActivityResultLauncher<String>

然后,您可以不使用已弃用的功能,而是将其放在您的 MainActivityonCreate 乐趣中:

/** Backup Website Entries */
saveDataResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) {}

让用户在按下绑定到上面实现的功能的菜单项后选择保存文件的位置:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
      R.id.action_save_file -> {
        saveDataResultLauncher.launch("example.txt")
        true
      }
      else -> super.onOptionsItemSelected(item)
    }
  }