It is well known that there are good reasons, whenever possible, to use the "content-path" as it was provided.
For requirements where a file path is actually needed, solutions were offered here and here.
Erel: "In most cases it is a mistake to use this sub. Especially in newer versions of Android. You shouldn't assume that the resource returned from ContentChooser comes from the file system and if it is, you most probably won't have permissions to directly access it."
For a current project, it is necessary to determine the file name of a file that was selected with the ContentChooser. The file names were previously defined by other users and are relevant for further processing of the file.
Based on the solution of Erel and Inman the attached test project was created.
B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
CreateResourceFromFile(Macro, Themes.DarkTheme)
'End of default text.
B4X:
' Based on Erel and Inman --> https://www.b4x.com/android/forum/threads/how-to-get-full-path-from-uri.89974/#post-569010
#Region Project Attributes
#ApplicationLabel: B4A Example
#VersionCode: 1
#VersionName:
'SupportedOrientations possible values: unspecified, landscape or portrait.
#SupportedOrientations: unspecified
#CanInstallToExternalStorage: False
#AdditionalJar: com.android.support:support-v4
#End Region
#Region Activity Attributes
#FullScreen: False
#IncludeTitle: True
#End Region
Sub Process_Globals
End Sub
Sub Globals
Private Label1 As Label
Private Label2 As Label
Private Button2 As Button
Private Button4 As Button
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("Layout1")
Label1.Text = ""
Label2.Text = ""
Button2.Visible = False
Button4.Visible = False
End Sub
Sub Activity_Resume
End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub
' ────────────────────────────────────────────────────────────────────────
Sub Button1_Click
Label2.Visible = False
Label2.Text = ""
CallSubDelayed2(Me, "ChooseFile", "application")
End Sub
Sub Button3_Click
Label2.Visible = False
Label2.Text = ""
CallSubDelayed2(Me, "ChooseFile", "image")
End Sub
Sub Button4_Click
Label2.Visible = True
Label2.Text = "...Trying GetPath(Label1.Text)"
Label2.Text = GetPath(Label1.Text) ' <<<<<<<<<<<<<<<<------------------
End Sub
Sub Button2_Click
Label2.Visible = True
Label2.Text = "...Trying GetPathFromContentResult(Label1.Text)"
Label2.Text = GetPathFromContentResult(Label1.Text)
End Sub
' ────────────────────────────────────────────────────────────────────────
Sub ChooseFile(Mime As String) As ResumableSub
Label1.Text = "..."
Dim cc As ContentChooser
cc.Initialize("cc")
cc.Show(Mime & "/*", "Choose a file")
wait for cc_Result (Success As Boolean, Dir As String, FileName As String)
Label1.Text = FileName
If Not(Success) Then Return Null
Button2.Visible=True
Button4.Visible=True
Return Null
End Sub
' ────────────────────────────────────────────────────────────────────────
Sub GetPathFromContentResult(UriString As String) As String
' Erel --> https://www.b4x.com/android/forum/threads/get-the-path-to-media-files-returned-from-contentchooser.39313/
If UriString.StartsWith("/") Then Return UriString 'If the user used a file manager to choose the image
Dim Cursor1 As Cursor
Dim Uri1 As Uri
Dim Proj() As String = Array As String("_data")
Dim cr As ContentResolver
cr.Initialize("")
If UriString.StartsWith("content://com.android.providers.media.documents") Then
Dim i As Int = UriString.IndexOf("%3A")
Dim id As String = UriString.SubString(i + 3)
Uri1.Parse("content://media/external/images/media")
Cursor1 = cr.Query(Uri1, Proj, "_id = ?", Array As String(id), "")
Else
Uri1.Parse(UriString)
Cursor1 = cr.Query(Uri1, Proj, "", Null, "")
End If
Cursor1.Position = 0
Dim res As String
res = Cursor1.GetString("_data")
Cursor1.Close
Return res
End Sub
' ────────────────────────────────────────────────────────────────────────
Sub GetPath(uristring As String) As String
' Inman --> https://www.b4x.com/android/forum/threads/how-to-get-full-path-from-uri.89974/#post-569092
Dim path As String
Dim uri As Uri
Dim ctxt As JavaObject
Dim FileUtils As JavaObject
ctxt.InitializeContext
uri.Parse(uristring)
FileUtils.InitializeStatic(Application.PackageName & ".main$FileUtil")
path=FileUtils.RunMethod("getFullPathFromTreeUri", Array(uri,ctxt))
Return path
End Sub
#if JAVA
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.Log;
@SuppressLint("NewApi")
public static final class FileUtil {
static String TAG="TAG";
private static final String PRIMARY_VOLUME_NAME = "primary";
public static boolean isKitkat() {
return Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT;
}
public static boolean isAndroid5() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
@NonNull
public static String getSdCardPath() {
String sdCardDirectory = Environment.getExternalStorageDirectory().getAbsolutePath();
try {
sdCardDirectory = new File(sdCardDirectory).getCanonicalPath();
}
catch (IOException ioe) {
Log.e(TAG, "Could not get SD directory", ioe);
}
return sdCardDirectory;
}
public static ArrayList<String> getExtSdCardPaths(Context con) {
ArrayList<String> paths = new ArrayList<String>();
File[] files = ContextCompat.getExternalFilesDirs(con, "external");
File firstFile = files[0];
for (File file : files) {
if (file != null && !file.equals(firstFile)) {
int index = file.getAbsolutePath().lastIndexOf("/Android/data");
if (index < 0) {
Log.w("", "Unexpected external file dir: " + file.getAbsolutePath());
}
else {
String path = file.getAbsolutePath().substring(0, index);
try {
path = new File(path).getCanonicalPath();
}
catch (IOException e) {
// Keep non-canonical path.
}
paths.add(path);
}
}
}
return paths;
}
@Nullable
public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
if (treeUri == null) {
return null;
}
String volumePath = FileUtil.getVolumePath(FileUtil.getVolumeIdFromTreeUri(treeUri),con);
if (volumePath == null) {
return File.separator;
}
if (volumePath.endsWith(File.separator)) {
volumePath = volumePath.substring(0, volumePath.length() - 1);
}
String documentPath = FileUtil.getDocumentPathFromTreeUri(treeUri);
if (documentPath.endsWith(File.separator)) {
documentPath = documentPath.substring(0, documentPath.length() - 1);
}
if (documentPath.length() > 0) {
if (documentPath.startsWith(File.separator)) {
return volumePath + documentPath;
}
else {
return volumePath + File.separator + documentPath;
}
}
else {
return volumePath;
}
}
private static String getVolumePath(final String volumeId, Context con) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return null;
}
try {
StorageManager mStorageManager =
(StorageManager) con.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
// primary volume?
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId)) {
return (String) getPath.invoke(storageVolumeElement);
}
// other volumes?
if (uuid != null) {
if (uuid.equals(volumeId)) {
return (String) getPath.invoke(storageVolumeElement);
}
}
}
// not found.
return null;
}
catch (Exception ex) {
return null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getVolumeIdFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if (split.length > 0) {
return split[0];
}
else {
return null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getDocumentPathFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if ((split.length >= 2) && (split[1] != null)) {
return split[1];
}
else {
return File.separator;
}
}
}
#End If
Unfortunately an error occurs when calling the function "GetPath":
I/B4A(10490): ~e:main_getpath (java line: 535)
I/B4A(10490): ~e:java.lang.reflect.InvocationTargetException
I/B4A(10490): ~e: at java.lang.reflect.Method.invoke(Native Method)
I/B4A(10490): ~e: at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:131)
I/B4A(10490): ~e: at b4a.example.main._getpath(main.java:535)
I/B4A(10490): ~e: at b4a.example.main._button4_click(main.java:424)
I/B4A(10490): ~e: at java.lang.reflect.Method.invoke(Native Method)
I/B4A(10490): ~e: at anywheresoftware.b4a.BA.raiseEvent2(BA.java:191)
I/B4A(10490): ~e: at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
I/B4A(10490): ~e: at anywheresoftware.b4a.BA.raiseEvent(BA.java:171)
I/B4A(10490): ~e: at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:80)
I/B4A(10490): ~e: at android.view.View.performClick(View.java:6600)
I/B4A(10490): ~e: at android.view.View.performClickInternal(View.java:6577)
I/B4A(10490): ~e: at android.view.View.access$3100(View.java:781)
I/B4A(10490): ~e: at android.view.View$PerformClick.run(View.java:25967)
I/B4A(10490): ~e: at android.os.Handler.handleCallback(Handler.java:873)
I/B4A(10490): ~e: at android.os.Handler.dispatchMessage(Handler.java:99)
I/B4A(10490): ~e: at android.os.Looper.loop(Looper.java:210)
I/B4A(10490): ~e: at android.app.ActivityThread.main(ActivityThread.java:7046)
I/B4A(10490): ~e: at java.lang.reflect.Method.invoke(Native Method)
I/B4A(10490): ~e: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
I/B4A(10490): ~e: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:879)
I/B4A(10490): ~e:Caused by: java.lang.IllegalArgumentException: Invalid URI: content://com.android.providers.downloads.documents/document/693
I/B4A(10490): ~e: at android.provider.DocumentsContract.getTreeDocumentId(DocumentsContract.java:1014)
I/B4A(10490): ~e: at b4a.example.main$FileUtil.getVolumeIdFromTreeUri(main.java:758)
I/B4A(10490): ~e: at b4a.example.main$FileUtil.getFullPathFromTreeUri(main.java:685)
I/B4A(10490): ~e: ... 20 more
E/AndroidRuntime(10490): FATAL EXCEPTION: main
E/AndroidRuntime(10490): Process: b4a.example, PID: 10490
E/AndroidRuntime(10490): java.lang.reflect.InvocationTargetException
E/AndroidRuntime(10490): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10490): at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:131)
E/AndroidRuntime(10490): at b4a.example.main._getpath(main.java:535)
E/AndroidRuntime(10490): at b4a.example.main._button4_click(main.java:424)
E/AndroidRuntime(10490): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10490): at anywheresoftware.b4a.BA.raiseEvent2(BA.java:191)
E/AndroidRuntime(10490): at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
E/AndroidRuntime(10490): at anywheresoftware.b4a.BA.raiseEvent(BA.java:171)
E/AndroidRuntime(10490): at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:80)
E/AndroidRuntime(10490): at android.view.View.performClick(View.java:6600)
E/AndroidRuntime(10490): at android.view.View.performClickInternal(View.java:6577)
E/AndroidRuntime(10490): at android.view.View.access$3100(View.java:781)
E/AndroidRuntime(10490): at android.view.View$PerformClick.run(View.java:25967)
E/AndroidRuntime(10490): at android.os.Handler.handleCallback(Handler.java:873)
E/AndroidRuntime(10490): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(10490): at android.os.Looper.loop(Looper.java:210)
E/AndroidRuntime(10490): at android.app.ActivityThread.main(ActivityThread.java:7046)
E/AndroidRuntime(10490): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10490): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/AndroidRuntime(10490): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:879)
E/AndroidRuntime(10490): Caused by: java.lang.IllegalArgumentException: Invalid URI: content://com.android.providers.downloads.documents/document/693
E/AndroidRuntime(10490): at android.provider.DocumentsContract.getTreeDocumentId(DocumentsContract.java:1014)
E/AndroidRuntime(10490): at b4a.example.main$FileUtil.getVolumeIdFromTreeUri(main.java:758)
E/AndroidRuntime(10490): at b4a.example.main$FileUtil.getFullPathFromTreeUri(main.java:685)
E/AndroidRuntime(10490): ... 20 more
I/B4A(10490): ~e:java.lang.reflect.InvocationTargetException
I/B4A(10490): ~e: at java.lang.reflect.Method.invoke(Native Method)
I/B4A(10490): ~e: at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:131)
I/B4A(10490): ~e: at b4a.example.main._getpath(main.java:535)
I/B4A(10490): ~e: at b4a.example.main._button4_click(main.java:424)
I/B4A(10490): ~e: at java.lang.reflect.Method.invoke(Native Method)
I/B4A(10490): ~e: at anywheresoftware.b4a.BA.raiseEvent2(BA.java:191)
I/B4A(10490): ~e: at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
I/B4A(10490): ~e: at anywheresoftware.b4a.BA.raiseEvent(BA.java:171)
I/B4A(10490): ~e: at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:80)
I/B4A(10490): ~e: at android.view.View.performClick(View.java:6600)
I/B4A(10490): ~e: at android.view.View.performClickInternal(View.java:6577)
I/B4A(10490): ~e: at android.view.View.access$3100(View.java:781)
I/B4A(10490): ~e: at android.view.View$PerformClick.run(View.java:25967)
I/B4A(10490): ~e: at android.os.Handler.handleCallback(Handler.java:873)
I/B4A(10490): ~e: at android.os.Handler.dispatchMessage(Handler.java:99)
I/B4A(10490): ~e: at android.os.Looper.loop(Looper.java:210)
I/B4A(10490): ~e: at android.app.ActivityThread.main(ActivityThread.java:7046)
I/B4A(10490): ~e: at java.lang.reflect.Method.invoke(Native Method)
I/B4A(10490): ~e: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
I/B4A(10490): ~e: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:879)
I/B4A(10490): ~e:Caused by: java.lang.IllegalArgumentException: Invalid URI: content://com.android.providers.downloads.documents/document/693
I/B4A(10490): ~e: at android.provider.DocumentsContract.getTreeDocumentId(DocumentsContract.java:1014)
I/B4A(10490): ~e: at b4a.example.main$FileUtil.getVolumeIdFromTreeUri(main.java:758)
I/B4A(10490): ~e: at b4a.example.main$FileUtil.getFullPathFromTreeUri(main.java:685)
I/B4A(10490): ~e: ... 20 more
E/AndroidRuntime(10490): FATAL EXCEPTION: main
E/AndroidRuntime(10490): Process: b4a.example, PID: 10490
E/AndroidRuntime(10490): java.lang.reflect.InvocationTargetException
E/AndroidRuntime(10490): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10490): at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:131)
E/AndroidRuntime(10490): at b4a.example.main._getpath(main.java:535)
E/AndroidRuntime(10490): at b4a.example.main._button4_click(main.java:424)
E/AndroidRuntime(10490): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10490): at anywheresoftware.b4a.BA.raiseEvent2(BA.java:191)
E/AndroidRuntime(10490): at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
E/AndroidRuntime(10490): at anywheresoftware.b4a.BA.raiseEvent(BA.java:171)
E/AndroidRuntime(10490): at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:80)
E/AndroidRuntime(10490): at android.view.View.performClick(View.java:6600)
E/AndroidRuntime(10490): at android.view.View.performClickInternal(View.java:6577)
E/AndroidRuntime(10490): at android.view.View.access$3100(View.java:781)
E/AndroidRuntime(10490): at android.view.View$PerformClick.run(View.java:25967)
E/AndroidRuntime(10490): at android.os.Handler.handleCallback(Handler.java:873)
E/AndroidRuntime(10490): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(10490): at android.os.Looper.loop(Looper.java:210)
E/AndroidRuntime(10490): at android.app.ActivityThread.main(ActivityThread.java:7046)
E/AndroidRuntime(10490): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10490): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/AndroidRuntime(10490): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:879)
E/AndroidRuntime(10490): Caused by: java.lang.IllegalArgumentException: Invalid URI: content://com.android.providers.downloads.documents/document/693
E/AndroidRuntime(10490): at android.provider.DocumentsContract.getTreeDocumentId(DocumentsContract.java:1014)
E/AndroidRuntime(10490): at b4a.example.main$FileUtil.getVolumeIdFromTreeUri(main.java:758)
E/AndroidRuntime(10490): at b4a.example.main$FileUtil.getFullPathFromTreeUri(main.java:685)
E/AndroidRuntime(10490): ... 20 more
Although the Contentchooser offers media from many sources, it should be at least possible to get path information from locally stored files.
Could someone with extended system knowledge point out a practicable solution?
Could someone with extended system knowledge point out a practicable solution?