デバイスのSDカードのルートディレクトリへの書き込みアクセスを取得できません。 SDカード内のデバイスの内部ストレージとアプリパッケージフォルダーに問題なく書き込むことができます。 Storage Access Frameworkを使用してSDカードにアクセスできることがわかったので、他のアプリを調べてSDカードにアクセスする方法を確認し、Total Commanderのきちんとした権限ダイアログを見つけました。
例が見つからないため、このようなダイアログを表示するにはどうすればよいですか?私の目標は、アプリをアンインストールした後も残る大きなファイルをSDカードに保存することです。
同様の質問:
Android(外部SDカードに書き込む):Java.io.IOException:Permission denied
オープンソースのファイルマネージャを見つけました。コードを見て、最終的に解決策を見つけました。
Android N(7.0.0)(api 24)以上でのみ動作します。
最初にSDカードのルートパスを取得し、ユーザー権限ダイアログを表示します。
public void takeCardUriPermission(String sdCardRootPath) {
if (Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.N) {
File sdCard = new File(sdCardRootPath);
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
StorageVolume storageVolume = storageManager.getStorageVolume(sdCard);
Intent intent = storageVolume.createAccessIntent(null);
try {
startActivityForResult(intent, 4010);
} catch (ActivityNotFoundException e) {
}
}
}
ユーザーがリクエストを受け入れると、onActivityResult()がトリガーされ、インテントデータからURIを保存できます
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 4010) {
Uri uri = data.getData();
grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);
}
}
これで、SDカードのルートのURIを取得してStorage Access Frameworkで使用できるようになりました
public Uri getUri() {
List<UriPermission> persistedUriPermissions = getContentResolver().getPersistedUriPermissions();
if (persistedUriPermissions.size() > 0) {
UriPermission uriPermission = persistedUriPermissions.get(0);
return uriPermission.getUri();
}
return null;
}
残念ながら、createAccessIntentの使用はAndroid Qで非推奨になり、 documentation はAndroid Q、すぐにRESULT_CANCELEDを返します。
ACTION_OPEN_DOCUMENT_TREE でインテントを使用することをお勧めします。最初に開くディレクトリを指定できます(これはAndroid O以上でのみサポートされています)。ユーザー、しかしこれはまだ非常にユーザーフレンドリーなアプローチではありません。
誰かが知っている他のユーザーフレンドリーな解決策があるかどうか疑問に思っていますか? AndroidがcreateAccessIntentを削除した理由がわかりません。最もユーザーフレンドリーな方法のようです。
外部ストレージに読み取り書き込み権限を付与するには、まずAndroidManifest.xmlファイルで以下の権限を宣言する必要があります。
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE"/>
次の行をアクティビティクラスに追加します。
private static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION = 1;
Android 6.0よりも大きいOSバージョンを使用する場合、以下のように、実行時にJavaコードで2つ以上の外部ストレージアクセス許可が必要です。
// Check whether this app has write external storage permission or not.
int writeExternalStoragePermission = ContextCompat.checkSelfPermission(ExternalStorageActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
// If do not grant write external storage permission.
if(writeExternalStoragePermission!= PackageManager.PERMISSION_GRANTED)
{
// Request user to grant write external storage permission.
ActivityCompat.requestPermissions(ExternalStorageActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION);
}
プログラムが上記のコードを実行すると、Androidは確認ダイアログをポップアップし、必要な権限を許可または拒否できるようにします。
許可または拒否ボタンをクリックすると、以下のメソッドが呼び出され、その中にトーストメッセージを表示できます。これをアクティビティに追加します。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION)
{
int grantResultsLength = grantResults.length;
if(grantResultsLength > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED)
{
Toast.makeText(getApplicationContext(), "You grant write external storage permission. Please click original button again to continue.", Toast.LENGTH_LONG).show();
}else
{
Toast.makeText(getApplicationContext(), "You denied write external storage permission.", Toast.LENGTH_LONG).show();
}
}