In my app, the user can choose where the created files (text files) are created.
This part is working fine.
But now, I want to open an external "file explorer" app, pointing directly to the chosen folder.
The "file explorer " apps I know accept an absolute path as input (like /storage/emulated/0/Documents/test_folder
)
When the user chooses a folder (with Intent.ACTION_OPEN_DOCUMENT_TREE), I get a content uri (like content://com.android.externalstorage.documents/tree/home%3Atest_folder
)
Another example with an external sd card:
content://com.android.externalstorage.documents/tree/3877-DB74%3ADocuments%2Ftest_folder
/storage/3877-DB74/Documents/test_folder
The uri points to a folder, not a file, so I can't use something like openInputStream
I have tried :
File f = new File(uri.getPath());
String path = f.getAbsolutePath();
but it gives: /tree/home:test_folder
or /tree/3877-DB74:Documents/test_folder
if on sd card
How can I get the real absolute path?
The code I use to call a file explorer:
Intent intent = new Intent(Intent.ACTION_VIEW);
String path = getExternalFilesDir(null).getAbsolutePath();
intent.setDataAndType(Uri.parse(path), "resource/folder");
if (intent.resolveActivityInfo(getPackageManager(), 0) != null)
{
startActivity(intent);
}
I finally wrote my own method to get the absolute path for a folder from a Uri. It is surely not fully generic, but it meets my need.
if it can help someone, here is my code:
Note: VOLUME_MAP is a map containing all mounted external volumes
/**************************************************************************/
public static String getRealPathFromContentUri(final Uri uri)
{
if (!isExternalStorageDocument(uri))
{
return null;
}
List<String> segs = uri.getPathSegments();
if (!"tree".equalsIgnoreCase(segs.get(0)))
{
return null;
}
String path = uri.getLastPathSegment();
final String[] split = path.split(":");
final String volumeId = split[0];
String userPath = "";
if (split.length > 1)
{
userPath = "/" + split[1];
}
if ("primary".equalsIgnoreCase(volumeId))
{
return Environment.getExternalStorageDirectory().getAbsolutePath() + userPath;
}
if ("home".equalsIgnoreCase(volumeId))
{
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + userPath;
}
// look for real volumeId
final String volumeName = VOLUME_MAP.get(volumeId);
if (volumeName == null)
{
return null;
}
path = "/storage";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
path = Environment.getStorageDirectory().getAbsolutePath();
}
return path + "/" + volumeId + userPath;
}
Here is how to get VOLUME_MAP:
private static Map<String, String> initVolumeMap()
{
final Map<String, String> volumeMap = new HashMap<>();
volumeMap.put("primary", null);
volumeMap.put("home", "Documents");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && PadocApp.getInstance() != null)
{
final Context context = MyApp.getInstance().getApplicationContext();
final StorageManager storageManager = ContextCompat.getSystemService(context, StorageManager.class);
assert storageManager != null;
final List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
for (StorageVolume sv : storageVolumes)
{
volumeMap.put(sv.getUuid(), sv.getDescription(context));
}
}
else
{
//at least we create a default entry for the primary drive
volumeMap.put(null, "internal shared storage");
}
return volumeMap;
}
Thanks to all contributors on this topic.