Android Code Snippet Get the path to media files returned from ContentChooser

Erel

Administrator
Staff member
Licensed User
SubName: GetPathFromContentResult

Description: Content providers such as the media gallery return a URL that starts with content://...

If you are just interested in showing the selected image then you can use File.OpenInput to open an input stream. You can also use it together with File.Copy2 to copy the media to a new location.

With the following code you can find the actual file path (if it is available):
This code depends on ContentResolver and SQL libraries.
B4X:
Sub GetPathFromContentResult(UriString As String) As String
  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
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.
 
Last edited:

Erel

Administrator
Staff member
Licensed User
Add this line to the manifest editor:
B4X:
AddPermission(android.permission.READ_EXTERNAL_STORAGE)
 

Douglas Farias

Expert
Licensed User
Erel how can i fix this error?
LogCat connected to: B4A-Bridge: motorola XT1033-359321054369100
--------- beginning of /dev/log/main
running waiting messages (1)
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Activity (main) Create, isFirst = false **
** Activity (main) Resume **
** Service (service1) Destroy **
** Service (service1) Create **
** Service (service1) Start **
Connected to B4A-Bridge (Wifi)
Installing file.
** Activity (main) Pause, UserClosed = false **
PackageAdded: package:njdude.ocr.sample
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
Installing file.
PackageAdded: package:njdude.ocr.sample
Installing file.
PackageAdded: package:njdude.ocr.sample
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
sending message to waiting queue (OnActivityResult)
running waiting messages (1)
java.lang.IllegalArgumentException: Unknown column requested: _data
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:167)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:413)
at android.content.ContentResolver.query(ContentResolver.java:461)
at android.content.ContentResolver.query(ContentResolver.java:404)
at anywheresoftware.b4a.objects.ContentResolverWrapper.Query(ContentResolverWrapper.java:43)
at njdude.ocr.sample.main._getpathfromcontentresult(main.java:704)
at njdude.ocr.sample.main._ocrgaleria_result(main.java:845)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:159)
at anywheresoftware.b4a.phone.Phone$ContentChooser$1.ResultArrived(Phone.java:843)
at anywheresoftware.b4a.BA$5.run(BA.java:505)
at anywheresoftware.b4a.BA.setActivityPaused(BA.java:390)
at njdude.ocr.sample.main$ResumeMessage.run(main.java:271)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5086)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
I have this error sometimes, sometimes works fine can i select a image from chooser, sometimes give me this error
Moto G
 

Erel

Administrator
Staff member
Licensed User
Add Try / Catch to catch this error. Not all images come from actual files. There may be cases where there is no file at all or that the file is not accessible.

This is why it is safer to access the input stream.
 

alexb

Member
Licensed User
Is it possible to adapt the above SUB to find the actual file path for a GMAIL attachment ?

Example:
'Bundle[{android.intent.extra.STREAM=content://gmail-ls/mymail@gmail.com/messages/681/attachments/0.1/BEST/false}]

I am able to pass the UriString 'content://gmail-ls/mymail@gmail.com/messages/681/attachments/0.1/BEST/false' but for obvious reasons the SUB cannot handle it. I have no clue about the data structure, therefore I would not know how to modify the sub. Can anybody help? Thx!
(btw: this was for an image)

Note: I modified above sub and added following line because on my phone file browser intent (data) was preceded by 'file:/' :
If UriString.StartsWith("file:/") Then Return UriString.Replace("file:/","") 'If the user used a file manager to choose the image
 

Erel

Administrator
Staff member
Licensed User
It is possible that there is no file at all, or that the file is not accessible.

However you can still open a stream to this resource and then copy it.
 

alexb

Member
Licensed User
It is possible that there is no file at all, or that the file is not accessible.

However you can still open a stream to this resource and then copy it.
Erel. thank you for your reply

The file is shared correctly when I choose a different app such as another Email program where the photo is then displayed correctly(and with the original file name), so I must assume the data is valid.

I have tried to use File.OpenInput but was not successful with that either, I did not obtain a valid stream. The code looks like that:

Dim UriString As String
UriString ="content://gmail-ls/mymail@gmail.com/messages/681/attachments/0.1/BEST/false"
File.OpenInput("",UriString)

Do you consider this correct or is there a mistake with the arguments?

Btw: I had the same STREAM problem with File.OpenInput and type media "content://media/external/images/media", however for the media contents the above routine works fine, so I did not care. Maybe a full example would help that I do not make any stupid error . THANK YOU!
 

Erel

Administrator
Staff member
Licensed User
Try this:
B4X:
Dim jo As JavaObject
jo = jo.InitializeStatic("anywheresoftware.b4a.objects.streams.File").GetField("ContentDir")
Dim UriString As String = "content://gmail...")
Dim in As InputStream = File.OpenInput(jo, UriString)
 

alexb

Member
Licensed User
Try this:
B4X:
Dim jo As JavaObject
jo = jo.InitializeStatic("anywheresoftware.b4a.objects.streams.File").GetField("ContentDir")
Dim UriString As String = "content://gmail...")
Dim in As InputStream = File.OpenInput(jo, UriString)
Erel, THANKS SO MUCH FOR YOUR OUTSTANDING SUPPORT!

It worked for me with following modification:
Dim jo As JavaObject
jo = jo.InitializeStatic("anywheresoftware.b4a.objects.streams.File").GetField("ContentDir")
Dim jo2 As Object
jo2 = jo
Dim In As InputStream = File.OpenInput(jo2, UriString)

I noticed the IDE complaint about string instead of object. Maybe because I am still using an older B4A version?
 

Erel

Administrator
Staff member
Licensed User
It is better to write it this way:
B4X:
Dim jo As JavaObject
Dim cd As String = jo.InitializeStatic("anywheresoftware.b4a.objects.streams.File").GetField("ContentDir")
Dim UriString As String = "content://gmail..."
Dim In As InputStream = File.OpenInput(cd , UriString)
 

alexb

Member
Licensed User
It is better to write it this way:
B4X:
Dim jo As JavaObject
Dim cd As String = jo.InitializeStatic("anywheresoftware.b4a.objects.streams.File").GetField("ContentDir")
Dim UriString As String = "content://gmail..."
Dim In As InputStream = File.OpenInput(cd , UriString)
Confirm - works too. Again, thanks a lot for all your help!
 

delozoya

Member
Licensed User
Helo. I have a problem. When I use GetPathFromContentResult It return null. This is code
B4X:
Sub CC_Result (Success As Boolean, Dir As String, FileName As String)
    If Success Then
    Log("filename: "&FileName)

        Dim normalizedFile As String = GetPathFromContentResult(FileName)
        Log("normalize : "&normalizedFile)
    '        CallSubDelayed3(FileTranser, "SendFile",Dir,normalizedFile)
           
    Else
        If LastException.IsInitialized Then ToastMessageShow(LastException.Message, True)
    End If
End Sub



Sub GetPathFromContentResult(UriString As String) As String
If UriString.StartsWith("/") Then Return UriString 'If the user used a file manager to choose the image
   Dim Proj() As String
   Proj = Array As String("_data")
   Dim Cursor As Cursor
   Dim r As Reflector
   Dim Uri As Object
   Uri = r.RunStaticMethod("android.net.Uri", "parse", _
      Array As Object(UriString), _
      Array As String("java.lang.String"))
   r.Target = r.GetContext
   r.Target = r.RunMethod("getContentResolver")
   Cursor = r.RunMethod4("query", _
   Array As Object(Uri, Proj, Null, Null, Null), _
   Array As String("android.net.Uri", _
      "[Ljava.lang.String;", "java.lang.String", _
      "[Ljava.lang.String;", "java.lang.String"))
   Cursor.Position = 0
   Dim res As String
   res = Cursor.GetString("_data")
   Cursor.Close
   Return res
End Sub
 

Erel

Administrator
Staff member
Licensed User
What is the source of the "file"? Why do you need to find the path?

There are cases where the resource doesn't come from a real file.
 

delozoya

Member
Licensed User
Log's: - filename: content://com.android.externalstorage.documents/document/primary%3A1.wav
- normalize : null

Any Solution
 
Last edited:

Erel

Administrator
Staff member
Licensed User
Why do you need to file path?

The correct way to load such resources is with File.OpenInput.
 
Top