background
Starting with Lollipop, apps can get access to real SD-cards (after it was not accessible on Kitkat, and was not officially supported yet worked on previous versions), as I've asked about here.
The problem
Because it has become quite rare to see a Lollipop device that supports SD-card and because the emulator doesn't really has the ability (or does it?) to emulate an SD-card support, it took me quite a while to test it.
Anyway, it seems that instead of using the normal File classes to access the SD-card (once you got a permission for it), you need to use Uris for it, using DocumentFile .
This limits the access to the normal paths, as I can't find a way to convert the Uris to paths and vice versa (plus it's quite annoying). It also means that I don't know how to check if the current SD-card/s are accessible, so I don't know when to ask the user for permission to read/write to it (or to them).
What I've tried
Currently, this is how I get the paths to all SD-cards:
/**
* 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;
}
}
This is how I check what Uris I can reach:
final List<UriPermission> persistedUriPermissions=getContentResolver().getPersistedUriPermissions();
This is how to get access to the SD cards:
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);
}
The questions
Is it possible to check if the current SD-cards are accessible, which aren't , and somehow ask the user to get permission to them?
Is there an official way to convert between the DocumentFile uris and real paths? I've found this answer, but it crashed in my case, plus it looks hack-y.
Is it possible to request permission from the user about a specific path? Maybe even show just the dialog of "do you accept yes/no ? " ?
Is it possible to use the normal File API instead of the DocumentFile API once the permission was granted?
Given a file/filepath, is it possible to just request a permission to access it (and check if it's given before), or its root path ?
Is it possible to make the emulator have an SD-card? Currently, it has "SD-card" being mentioned, but it works as the primary external storage, and I'd like to test it using the secondary external storage, in order to try and use the new API.
I think that for some of those questions, one helps a lot to answer the other.