Support for Storage Access Framework (SAF) URIs

216 views
Skip to first unread message

Ferhat

unread,
Jun 18, 2026, 9:29:22 AMJun 18
to DroidScript

Hello,

I'm having an issue with Storage Access Framework (SAF).

When I select a folder, DroidScript returns a URI such as:

content://com.android.externalstorage.documents/tree/primary:Documents/MyData

I cannot use this URI with app.ListFolder().

I also tried using the equivalent filesystem path:

/storage/emulated/0/Documents/MyData

Even after obtaining write permission and successfully creating files with app.WriteFile(), app.ListFolder() is still unable to list the folder contents.

Is there a way to access and list files from a SAF folder URI, or is this a limitation of app.ListFolder()?

If not, could support for SAF URIs be added to functions like ListFolder(), ReadFile(), and WriteFile()? Android increasingly returns content URIs instead of direct file paths, so it would be very useful.

Thank you.

Alan Hendry

unread,
Jun 18, 2026, 10:51:51 AMJun 18
to DroidScript
HI, 
I think this was brought up in
and is outstanding.
Regards, ah

Ferhat

unread,
Jul 4, 2026, 7:13:56 AM (13 days ago) Jul 4
to DroidScript

Hello,

Thanks but, I'm currently working on an urgent project. I need to list the files and subfolders inside a folder that the user has granted access to using ListFolder.

I can successfully request and obtain permission to access the folder, but ListFolder does not return its contents.

Is there another way to list the files and folders in a user-selected directory? If this is not currently supported, is there any plan to add this functionality in a future update? If so, is there an estimated release date?

Thank you for your help.


18 Haziran 2026 Perşembe tarihinde saat 17:51:51 UTC+3 itibarıyla hendr...@gmail.com şunları yazdı:

Jumar

unread,
Jul 5, 2026, 6:11:51 AM (12 days ago) Jul 5
to DroidScript
You can try this:

appFolder = "/Internal/Documents/MyData"
var files = app.ListFolder(appFolder)

Ferhat

unread,
Jul 8, 2026, 9:05:13 AM (9 days ago) Jul 8
to DroidScript
Hello Jumar,
I tried, but it didn't work.


Example code:

//Called when application is started.
function OnStart()
{
 //Create a layout with objects vertically centered.
 lay = app.CreateLayout( "Linear", "VCenter,FillXY" )
  
 filelist = app.CreateText( "Files: ",1,null )
 lay.AddChild( filelist )
 permbtn = app.CreateButton( "Get Perm" )
 permbtn.SetOnTouch( permbtn_OnTouch )
 lay.AddChild( permbtn )
 //Add layout to app. 
 app.AddLayout( lay )
}

function permbtn_OnTouch()
{
 app.GetPermission( "Internal", OnPermission ); // Get acces
}

function OnPermission( pathURI )
{
  app.WriteFile( pathURI+"/demo.txt", "Hello" ) // Succes
  filelist.SetText( "Files: "+app.ListFolder(pathURI) ) // Error
  if(app.ListFolder(pathURI)=="") app.ShowPopup( "No files were found to list." ) // Error
}

5 Temmuz 2026 Pazar tarihinde saat 13:11:51 UTC+3 itibarıyla Jumar şunları yazdı:

Alan Hendry

unread,
Jul 9, 2026, 4:34:16 AM (8 days ago) Jul 9
to DroidScript
HI,
Someone posted code (testUriFileFunctions)
that seems to show that some file functions
don't work with uris provided by GetPermissions.
Regards, ah

appFilename  = app.GetAppName() + ".js"
testFilename = "test.js"
myFilename   = "myFile.js"
permUri      = ""

function OnStart()
{
    lay = app.CreateLayout( "linear", "VCenter,FillXY" );
    permBtn = app.AddButton(lay, "GetPermission - choose a foldername without spaces", -1, -1, "Monospace");
    permBtn.SetOnTouch(permBtnOnTouch)

    app.AddLayout( lay );
}

function permBtnOnTouch() {
    permUri = ""
    app.GetPermission("internal", onPermission)
}

function onPermission(uri) {
    if (uri) {
        uri = app.Uri2Path(uri) // ?
        txtUriHeader = app.AddText(lay, "Choosed Uri folder", -1, -1, "Bold")
        txtUri = app.AddText(lay, "", -1, -1, "MultiLine")
        txtListUriFolderHeader = app.AddText(lay, "Files in Uri folder (no indication)", -1, -1, "Bold")
        txtListUriFolderHeader.SetTextColor("red")
        txtListUriFolder = app.AddText(lay, "", -1, -1, "MultiLine")
        copyFileBtn = app.AddButton(lay, "Copy appFile to Uri folder", -1, -1, "Monospace");
        copyFileBtn.SetOnTouch(copyFileBtnOnTouch)
        copyFolderBtn = app.AddButton(lay, "Copy Uri folder to 'copiedFolder' in Uri folder (warning)", -1, -1, "Monospace");
        copyFolderBtn.SetTextColor("red")
        copyFolderBtn.SetOnTouch(copyFolderBtnOnTouch)
        fileExistsBtn = app.AddButton(lay, "Check existence of appFile in Uri folder (always false)", -1, -1, "Monospace");
        fileExistsBtn.SetTextColor("red")
        fileExistsBtn.SetOnTouch(fileExistsBtnOnTouch)
        folderExistsBtn = app.AddButton(lay, "Check existence of Uri folder (always false)", -1, -1, "Monospace");
        folderExistsBtn.SetTextColor("red")
        folderExistsBtn.SetOnTouch(folderExistsBtnOnTouch)
        delFileBtn = app.AddButton(lay, "Delete appFile in Uri folder", -1, -1, "Monospace");
        delFileBtn.SetOnTouch(delFileBtnOnTouch)
        delFolderBtn = app.AddButton(lay, "Delete Uri folder (no indication)", -1, -1, "Monospace");
        delFolderBtn.SetTextColor("red")
        delFolderBtn.SetOnTouch(delFolderBtnOnTouch)
        readFileBtn = app.AddButton(lay, "Read content of appFile in Uri folder", -1, -1, "Monospace");
        readFileBtn.SetOnTouch(readFileBtnOnTouch)
        writeFileBtn = app.AddButton(lay, "Write 'myFile.js' to Uri folder", -1, -1, "Monospace");
        writeFileBtn.SetOnTouch(writeFileBtnOnTouch)
        renFileBtn = app.AddButton(lay, "Rename newly written 'myFile.js' in Uri folder to 'renamedFile.js'", -1, -1, "Monospace");
        renFileBtn.SetOnTouch(renFileBtnOnTouch)
        zipFileBtn = app.AddButton(lay, "Zip newly written 'myFile.js' in Uri folder to 'myFile.js.zip' (warning)", -1, -1, "Monospace");
        zipFileBtn.SetTextColor("red")
        zipFileBtn.SetOnTouch(zipFileBtnOnTouch)
        zipFolderBtn = app.AddButton(lay, "Zip Uri folder to 'zippedFolder' in Uri folder (warning)", -1, -1, "Monospace");
        zipFolderBtn.SetTextColor("red")
        zipFolderBtn.SetOnTouch(zipFolderBtnOnTouch)
       
        permUri = uri
        txtUri.SetText(permUri)
        listUriFolder()
    }
}

function copyFileBtnOnTouch() {
    app.CopyFile(appFilename, permUri + "/" + appFilename)
    listUriFolder()
}

function copyFolderBtnOnTouch() {
    app.CopyFolder(permUri, permUri + "/" + "copiedFolder")
    listUriFolder()
}

function fileExistsBtnOnTouch() {
    app.Alert("File exists: " + app.FileExists(permUri + "/" + appFilename))
}

function folderExistsBtnOnTouch() {
    app.Alert("Folder exists: " + app.FolderExists(permUri))
}

function delFileBtnOnTouch() {
    app.DeleteFile(permUri + "/" + appFilename)
    listUriFolder()
}

function delFolderBtnOnTouch() {
    app.DeleteFolder(permUri)
    listUriFolder()
}

function readFileBtnOnTouch() {
    app.Alert("File read: " + app.ReadFile(permUri + "/" + appFilename))
}

function writeFileBtnOnTouch() {
    app.WriteFile(permUri + "/" + myFilename, "//My file")
    listUriFolder()
}

function renFileBtnOnTouch() {
    app.WriteFile(permUri + "/" + myFilename, "//My file")
    app.RenameFile(permUri + "/" + myFilename, permUri + "/" + "renamedFile.js")
    listUriFolder()
}

function zipFileBtnOnTouch() {
    app.WriteFile(permUri + "/" + myFilename, "//My file")
    app.ZipFile(permUri + "/" + myFilename, permUri + "/" + myFilename + ".zip")
    listUriFolder()
}

function zipFolderBtnOnTouch() {
    app.ZipFolder(permUri, permUri + "/" + "zippedFolder")
    listUriFolder()
}

function listUriFolder() {
    txtListUriFolder.SetText(app.ListFolder(permUri).join("\n"))
}

Ferhat

unread,
Jul 10, 2026, 1:45:37 AM (7 days ago) Jul 10
to DroidScript
I've used this before, but ListFolder doesn't work. It normally contains two files, but it returns an empty.
DS version: 2.79

SS:
Screenshot_20260709-203131.pngScreenshot_20260710-084308.png

9 Temmuz 2026 Perşembe tarihinde saat 11:34:16 UTC+3 itibarıyla hendr...@gmail.com şunları yazdı:

Alan Hendry

unread,
Jul 12, 2026, 10:45:52 AM (5 days ago) Jul 12
to DroidScript
HI,
If the first time a user runs an app, and we use GetPermissions,
can we store the uri, and on subsequent runs
just use the stored uri and skip the GetPermissions?
Whether I GetPermissions for ExtSDCard or Internal (or USB)
It seems to ask the user to navigate and choose a folder, 
the start point for the navigation doesn't seem to be predictable.
If we want permission to a specific folder (say documents/newapp)
then we seem to be dependent on the user navigating to documents,
creating folder newapp, navigating to newapp, then giving access.
Would it be possible to GetPermission for the specific folder from the start?
Regards, ah

Alan Hendry

unread,
Jul 13, 2026, 1:03:14 PM (3 days ago) Jul 13
to DroidScript

HI,
When running under DS ...
GetPermissions for ExtSDcard AND Storage
seem to show up in ListPermissions for Storage (only).
Also Remove Permission for a specific folder uri 
seems to remove permission for all uri's under that folder.
The good news us that the Permission is still there
after DS restarts.
Regards, ah

Alan Hendry

unread,
Jul 13, 2026, 1:05:28 PM (3 days ago) Jul 13
to DroidScript
var txt1, txt2, txt3, txt4, l1, l2
function OnStart() {
    lay = app.CreateLayout( "linear", "Top,Left,FillXY" )
    txt3 = app.AddText( lay,"Check Permission ExtSDCard " )
    txt4 = app.AddText( lay,"Check Permission Storage" )
    txt1 = app.AddText( lay,"List Permissions ExtSDCard (touch to remove)",-1,-1,"bold" )
    l1 = app.AddList( lay,app.ListPermissions( "ExtSDcard" ) )
    l1.SetOnTouch( function (t,b,ix) {alert(t);app.RemovePermission(t);lc();})
    txt2 = app.AddText( lay,"List Permissions Storage (touch to remove)" ,-1,-1,"bold")
    l2 = app.AddList( lay,app.ListPermissions( "Storage" ) )
    l2.SetOnTouch( function (t,b,ix) {alert(t);app.RemovePermission(t);lc();})
    permBtn = app.AddButton(lay, "GetPermission ExtSDcard - choose a folder without spaces", -1, -1);
    permBtn.SetOnTouch(function() {app.GetPermission("ExtSDcard",lc)})
    permBtn = app.AddButton(lay, "GetPermission Internal - choose a folder without spaces", -1, -1);
    permBtn.SetOnTouch(function() {app.GetPermission("Internal",lc)})
    permBtn = app.AddButton(lay, "GetPermission External - choose a folder without spaces", -1, -1);
    permBtn.SetOnTouch(function() {app.GetPermission("External",lc)})
    app.AddLayout( lay );
    lc()
}
function lc () {
    check = app.ListPermissions( "ExtSDcard" )
    l1.SetList( check.replaceAll(":","^c^" ))
    check = app.ListPermissions( "Storage" )
    l2.SetList( check.replaceAll(":","^c^" ))
    check = app.CheckPermission( "ExtSDcard" )
    txt3.SetText( "Check Permission ExtSDcard ungranted = " + check )
    check = app.CheckPermission( "Storage" )
    txt4.SetText("Check Permission Storage ungranted = " + check )
}

Jumar

unread,
Jul 14, 2026, 8:21:52 AM (3 days ago) Jul 14
to DroidScript
Hi Ferhat

Try this code:



var appFolder = "/Internal/Documents/MyNewFolder"
var count = 0


//Called when application is started.
function OnStart()
{
    //Create a layout with objects vertically centered.
    lay = app.CreateLayout( "Linear", "VCenter,FillXY" )
     
    filelist = app.CreateText( "Files: ",1,null )
    lay.AddChild( filelist )
   
    writeBtn = app.CreateButton( "Write file" )
    writeBtn.SetOnTouch( CreateFile )
    lay.AddChild( writeBtn )

   
    //Add layout to app.
    app.AddLayout( lay )
}

function CreateFile()
{
    var fileName = "demo_"+count+".txt"
    var fileContent = "This is the "+count+"file"
    WriteFile(fileName, fileContent)
    count++
}

function WriteFile(fileName, fileContent)
{
    if( !app.CheckPermission(appFolder) ) {
        app.GetPermission("internal", path => {
            if (!path) {
                app.ShowPopup("Permission not granted")
            }
            else {
                const folderName = path.split("tree/primary%3A")[1].replace("%2F", "/")
                appFolder = "/Internal/"+folderName+"/MyNewFolder"
                WriteFile(fileName, fileContent)
            }
        })
        return
    }
   
    app.MakeFolder(appFolder)
    const filePath = appFolder+"/"+fileName
    app.WriteFile(filePath, fileContent)
   
    var files = app.ListFolder(appFolder)
    filelist.SetText(files)

Ferhat

unread,
Jul 15, 2026, 5:01:22 AM (yesterday) Jul 15
to DroidScript
Hi Jumar,

I tested the code. ListFolder works correctly immediately after the user grants access to a folder. However, after restarting the app, it returns an empty list for the same folder, even though the permission was previously granted. It only lists the folder contents on the initial permission grant.

14 Temmuz 2026 Salı tarihinde saat 15:21:52 UTC+3 itibarıyla Jumar şunları yazdı:

Jumar

unread,
Jul 15, 2026, 6:11:36 AM (yesterday) Jul 15
to DroidScript
Make sure to save the appFolder path before closing the app.


var appFolder = "/Internal/Documents/MyNewFolder"
var count = 0

//Called when application is started.
function OnStart()
{
   appFolder = app.LoadText("appFolder", "/Internal/Documents/MyNewFolder")
    app.SaveText("appFolder", appFolder)
   
    var files = app.ListFolder(appFolder)
    filelist.SetText(files)

}

Reply all
Reply to author
Forward
0 new messages