バックグラウンド
Lollipop 以降、アプリは実際の SD カードにアクセスできるようになりました (Kitkat ではアクセスできず、以前のバージョンでは正式にサポートされていませんでしたが)。
問題
SD カードをサポートする Lollipop デバイスを目にすることは非常にまれになっており、エミュレーターには SD カード サポートをエミュレートする機能が実際には備わっていないため (または、エミュレートしている?)、それをテストするのにかなりの時間がかかりました。 .
とにかく、通常の File クラスを使用して SD カードにアクセスする代わりに (アクセス許可を取得したら)、 DocumentFile を使用して Uris を使用する必要があるようです。
Uris をパスに、またはその逆に変換する方法が見つからないため、通常のパスへのアクセスが制限されます (さらに、非常に面倒です)。また、現在のSDカードにアクセスできるかどうかを確認する方法がわからないため、ユーザーに読み取り/書き込みの許可をいつ求めるべきかわかりません。
私が試したこと
現在、これは私がすべてのSDカードへのパスを取得する方法です:
/**
* returns a list of all available sd cards paths, or null if not found.
*
* @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static List<String> getExternalStoragePaths(final Context context,final boolean includePrimaryExternalStorage)
{
final File primaryExternalStorageDirectory=Environment.getExternalStorageDirectory();
final List<String> result=new ArrayList<>();
final File[] externalCacheDirs=ContextCompat.getExternalCacheDirs(context);
if(externalCacheDirs==null||externalCacheDirs.length==0)
return result;
if(externalCacheDirs.length==1)
{
if(externalCacheDirs[0]==null)
return result;
final String storageState=EnvironmentCompat.getStorageState(externalCacheDirs[0]);
if(!Environment.MEDIA_MOUNTED.equals(storageState))
return result;
if(!includePrimaryExternalStorage&&VERSION.SDK_INT>=VERSION_CODES.HONEYCOMB&&Environment.isExternalStorageEmulated())
return result;
}
if(includePrimaryExternalStorage||externalCacheDirs.length==1)
{
if(primaryExternalStorageDirectory!=null)
result.add(primaryExternalStorageDirectory.getAbsolutePath());
else
result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
}
for(int i=1;i<externalCacheDirs.length;++i)
{
final File file=externalCacheDirs[i];
if(file==null)
continue;
final String storageState=EnvironmentCompat.getStorageState(file);
if(Environment.MEDIA_MOUNTED.equals(storageState))
result.add(getRootOfInnerSdCardFolder(externalCacheDirs[i]));
}
return result;
}
private static String getRootOfInnerSdCardFolder(File file)
{
if(file==null)
return null;
final long totalSpace=file.getTotalSpace();
while(true)
{
final File parentFile=file.getParentFile();
if(parentFile==null||parentFile.getTotalSpace()!=totalSpace)
return file.getAbsolutePath();
file=parentFile;
}
}
これは、到達できる Uris を確認する方法です。
final List<UriPermission> persistedUriPermissions=getContentResolver().getPersistedUriPermissions();
これは、SD カードにアクセスする方法です。
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE),42);
public void onActivityResult(int requestCode,int resultCode,Intent resultData)
{
if(resultCode!=RESULT_OK)
return;
Uri treeUri=resultData.getData();
DocumentFile pickedDir=DocumentFile.fromTreeUri(this,treeUri);
grantUriPermission(getPackageName(),treeUri,Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(treeUri,Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
質問
現在の SD カードにアクセスできるかどうかを確認することはできますか?
DocumentFile uris と実際のパスを変換する公式の方法はありますか? 私はこの答えを見つけましたが、私の場合はクラッシュし、ハックのように見えます。
特定のパスについてユーザーに許可を求めることはできますか? たぶん、「はい/いいえを受け入れますか?」というダイアログだけを表示することさえありますか?
許可が与えられたら、DocumentFile API の代わりに通常の File API を使用することは可能ですか?
ファイル/ファイルパスが与えられた場合、それにアクセスするための許可を要求するだけで(そしてそれが以前に与えられているかどうかを確認して)、またはそのルートパスを要求することは可能ですか?
エミュレータにSDカードを持たせることは可能ですか? 現在、「SDカード」と記載されていますが、プライマリ外部ストレージとして機能しており、新しいAPIを試して使用するために、セカンダリ外部ストレージを使用してテストしたいと考えています。
これらの質問のいくつかについては、一方が他方に答えるのに大いに役立つと思います。