Android Question How to access the whole of external SD card?

Inman

Well-Known Member
Licensed User
Make pickedTree a process global variable. You can then access it from wherever you want.
Ok. What about permission? Say I make the user select external SD card folder today with the above code. Will I be able to write into that folder, say 2 weeks from now, using a background service, without any user interaction?
 

Erel

Administrator
Staff member
Licensed User
Updated code:
B4X:
Sub Activity_Click
   Dim i As Intent
   i.Initialize("android.intent.action.OPEN_DOCUMENT_TREE", "")
   StartActivityForResult(i)
End Sub

Sub ion_Event (MethodName As String, Args() As Object) As Object
   If Args(0) = -1 Then 'resultCode = RESULT_OK
       Dim i As Intent = Args(1)
       Dim jo As JavaObject = i
       Dim treeUri As Uri = jo.RunMethod("getData", Null)
       Dim ctxt As JavaObject
       ctxt.InitializeContext
       Dim takeFlags As Int = Bit.And(i.Flags, 3)
       ctxt.RunMethodJO("getContentResolver", Null).RunMethod("takePersistableUriPermission", Array(treeUri, takeFlags))
       Dim uri As String = treeUri 'ignore
       'uri should be a process global variable and you can save it
   End If
   Return Null
End Sub

Sub GetPickedDir (uri As String) As JavaObject
   Dim ctxt As JavaObject
   ctxt.InitializeContext
   Dim DocumentFileStatic As JavaObject
   Dim treeUri As Uri
   treeUri.Parse(uri)
   Dim pickedDir As JavaObject = DocumentFileStatic.InitializeStatic("android.support.v4.provider.DocumentFile").RunMethod("fromTreeUri", Array(ctxt, treeUri))
   Return pickedDir
   'usage example of pickedDir
'   Dim files() As Object = pickedDir.RunMethod("listFiles", Null)
'   For Each f As JavaObject In files
'       Log(f.RunMethod("getName", Null))
'   Next
'   Dim newFile As JavaObject = pickedDir.RunMethod("createFile", Array("text/plain", "My Novel"))
'   Dim out As OutputStream = ctxt.RunMethodJO("getContentResolver", Null).RunMethod("openOutputStream", Array(newFile.RunMethod("getUri", Null)))
'   Dim tw As TextWriter
'   tw.Initialize(out)
'   tw.Write("this is a test")
'   tw.Close
End Sub

Sub StartActivityForResult(i As Intent)
   Dim jo As JavaObject = GetBA
   ion = jo.CreateEvent("anywheresoftware.b4a.IOnActivityResult", "ion", Null)
   jo.RunMethod("startActivityForResult", Array As Object(ion, i))
End Sub

Sub GetBA As Object
   Dim jo As JavaObject
   Dim cls As String = Me
   cls = cls.SubString("class ".Length)
   jo.InitializeStatic(cls)
   Return jo.GetField("processBA")
End Sub
You can save the uri string and use it later without requesting a new permission.
Depends on ContentResolver and JavaObject libraries.
 

Inman

Well-Known Member
Licensed User
Thank you so much, Erel. The permission is retained correctly, even after restarting emulator. Couple more requests.

1. Just like how you opened an outputstream, how can I open an inputstream for a treeuri like in the above code so that I can use File.Copy2?

2. How can I get the real path of a uri (for display purpose)? Currently when I print uri, I get (HierarchicalUri) content://com.android.externalstorage.documents/tree/17FC-3214%3Atff

I then tried this
B4X:
jo=treeUri
Log(jo.RunMethod("getPath",Null))
But it printed /tree/17FC-3214:tff. How can I get a path like /storage/17FC-3214/tff? I saw this stackoverflow post. Could you please take a look?

https://stackoverflow.com/questions/3401579/get-filename-and-path-from-uri-from-mediastore/10564727#10564727
B4X:
private String getRealPathFromURI(Uri contentUri) 
{
    String[] proj = { MediaStore.Images.Media.DATA };
    CursorLoader loader = new CursorLoader(mContext, contentUri, proj, null, null, null);
    Cursor cursor = loader.loadInBackground();
    int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    String result = cursor.getString(column_index);
    cursor.close();
    return result;
}
 

Inman

Well-Known Member
Licensed User
https://www.b4x.com/android/forum/threads/using-photos-from-the-phone.37444/#post-221367

Note that there are cases where IT DOES NOT WORK! DONT RELY ON THE REAL PATH!!!!
Thank you Don. I won't be using real path for any operation; just to show user the path he selected.

Btw that code is throwing error on line Cursor1 = cr.Query(Uri1, Proj, "", Null, "")
B4X:
java.lang.UnsupportedOperationException: Unsupported Uri content://com.android.externalstorage.documents/tree/17FC-3214%3Atff
Google must have changed something is the 4 years that code was posted
 

Erel

Administrator
Staff member
Licensed User
Just like how you opened an outputstream, how can I open an inputstream for a treeuri like in the above code so that I can use File.Copy2?
Just replace "openOutputStream" with "openInputStream".
 

Inman

Well-Known Member
Licensed User
Just replace "openOutputStream" with "openInputStream".
What about the newFile javaobject that is passed with openOutputStream?
B4X:
Dim newFile As JavaObject = pickedDir.RunMethod("createFile", Array("text/plain", "My Novel"))
What should I replace "createFile" with?
 

Inman

Well-Known Member
Licensed User
Thanks for the all the help, Erel. I am posting the code I came up with as it may help someone someday. Please check this question I posted which is connected to this discussion.
B4X:
Sub StreamFileCopier(dirsource As String,filesource As String,dirdest As String,filedest As String)

    Dim inp As InputStream,out As OutputStream
    Dim suri,duri As String
    
    Dim sctxt,dctxt,spickedDir,dpickedDir As JavaObject
        
    suri=dirsource
    spickedDir=GetPickedDir(suri)
    sctxt.InitializeContext
    
    duri=dirdest
    dpickedDir=GetPickedDir(duri)
    dctxt.InitializeContext
    
    Dim oldFile As JavaObject = spickedDir.RunMethod("findFile", Array(filesource))
    inp = sctxt.RunMethodJO("getContentResolver", Null).RunMethod("openInputStream", Array(oldFile.RunMethod("getUri", Null)))
    
    Dim newFile As JavaObject = dpickedDir.RunMethod("createFile", Array("text/plain", filedest))
    out = dctxt.RunMethodJO("getContentResolver", Null).RunMethod("openOutputStream", Array(newFile.RunMethod("getUri", Null)))
    
    File.Copy2(inp,out)
    out.Close
    
End Sub
 
Top