SAF: App Inventor implementation of Storage Access Framework

1.Introduction

Description: Basically AI2 wrapper of Storage Access Framework. Using this extension, you can get read and/or write access to a single document or tree document (and all of its children recursively).
Latest Version: 1
Released: 2021-09-16T18:30:00Z
Last Updated: 2021-09-16T18:30:00Z

2.Blocks

image
image
image
image
image
image
image

3.Documentation

Docs for: SAF

Events

DocumentCreated

Event invoked after creating document.Returns document's uri if operation was successful else returns error message
Params

uriString | text


GotCopyResult

Event invoked after getting copy document result.Response will be target document's uri if operation was successful else returns error message
Params

successful | boolean
response | text


GotFilesList

Event invoked after getting files list
Params

filesList | list


GotMoveResult

Event invoked after getting move document result.Response will be target document's uri if operation was successful else returns error message
Params

successful | boolean
response | text


GotReadResult

Event invoked after reading from document.Returns content if operation was successful else returns error message
Params

result | text


GotUri

Event invoked when user selects a document or tree from SAF file picker
Params

uri | any
uriString | text


GotWriteResult

Event invoked after writing to document.Returns document's uri if operation was successful else returns error message
Params

response | text


Methods

BuildChildDocumentsUriUsingTree

Builds child documents id using tree (documents which is child of parent document) uri and its parent document id
Params

treeUri | text

parentDocumentId | text

Return type : text


BuildDocumentUriUsingTree

Builds document uri using tree uri and document id
Params

treeUri | text

documentId | text

Return type : text


CopyDocument

Tries to copy document from source uri to target dir
Params

sourceUri | text

targetParentUri | text


CreateDocument

Creates a new and empty document.If document already exists then an incremental value will be suffixed.
Params

parentDocumentUri | text

fileName | text

mimeType | text


CreateFlags

Combines two flags and returns resulting flag
Params

f1 | number

f2 | number

Return type : number


DeleteDocument

Tries to delete document from given uri and returns result
Params

uriString | text

Return type : boolean


GetDisplayName

Returns display name of given document uri
Params

documentUri | text

Return type : text


GetDocumentId

Returns document id of an uri (should only be grand child)
Params

uriString | text

Return type : text


GetLastModifiedTime

Returns last modified time (epoch) of given document uri
Params

documentUri | text

Return type : text


GetMimeType

Returns mime type of given document uri
Params

documentUri | text

Return type : text


GetSize

Returns size (in bytes) of given document uri
Params

documentUri | text

Return type : text


GetTreeDocumentId

Returns document id of tree uri (should be either tree uri itself or a direct child uri)
Params

uriString | text

Return type : text


IsChildDocumentUri

Returns whether second uri is child of first uri
Params

parentUri | text

childUri | text

Return type : boolean


IsDocumentUri

Returns whether given uri is a document uri
Params

uriString | text

Return type : boolean


IsReadGranted

Returns whether read is available for given uri
Params

uri | text

Return type : boolean


IsTreeUri

Returns whether given uri is a tree uri
Params

uriString | text

Return type : boolean


IsWriteGranted

Returns whether write is available for given uri
Params

uri | text

Return type : boolean


ListFiles

Tries to list files from given dir
Params

dirUri | text

dirDocumentId | text


MoveDocument

Tries to move document from source uri to target dir
Params

sourceUri | text

sourceParentUri | text

targetParentUri | text


OpenDocumentTree

Prompts user to select a document tree
Params

title | text

initialDir | text


OpenSingleDocument

Prompts user to select a single file
Params

title | text

category | text

type | text

extraMimeTypes | list


ReadFromFile

Reads from given uri
Params

uriString | text


ReleasePermission

Relinquish a persisted URI permission grant
Params

uri | text

flags | number


RenameDocument

Tries to rename a document and returns updated uri
Params

documentUri | text

displayName | text

Return type : text


StringFromUriObject

Convert uri to string
Params

uri | any

Return type : text


StringToUriObject

Converts string to uri
Params

uriString | text

Return type : any


TakePersistableUriPermission

Take a persistable URI permission grant that has been offered. Once taken, the permission grant will be remembered across device reboots.
Params

uri | any

flags | number


WriteToFile

Writes to given uri
Params

uriString | text

content | text


Properties

DocumentDirMimeType

Returns mime type of document dir

Property Type : read-only
Accepts : text


FlagGrantReadPermission

Flag to get write permission

Property Type : read-only
Accepts : number


FlagGrantWritePermission

Flag to get read permission

Property Type : read-only
Accepts : number


Docs for: SAF

Events

DocumentCreated

Event invoked after creating document.Returns document's uri if operation was successful else returns error message
Params

uriString | text


GotCopyResult

Event invoked after getting copy document result.Response will be target document's uri if operation was successful else returns error message
Params

successful | boolean
response | text


GotFilesList

Event invoked after getting files list
Params

filesList | list


GotMoveResult

Event invoked after getting move document result.Response will be target document's uri if operation was successful else returns error message
Params

successful | boolean
response | text


GotReadResult

Event invoked after reading from document.Returns content if operation was successful else returns error message
Params

result | text


GotUri

Event invoked when user selects a document or tree from SAF file picker
Params

uri | any
uriString | text


GotWriteResult

Event invoked after writing to document.Returns document's uri if operation was successful else returns error message
Params

response | text


Methods

BuildChildDocumentsUriUsingTree

Builds child documents id using tree (documents which is child of parent document) uri and its parent document id
Params

treeUri | text

parentDocumentId | text

Return type : text


BuildDocumentUriUsingTree

Builds document uri using tree uri and document id
Params

treeUri | text

documentId | text

Return type : text


CopyDocument

Tries to copy document from source uri to target dir
Params

sourceUri | text

targetParentUri | text


CreateDocument

Creates a new and empty document.If document already exists then an incremental value will be suffixed.
Params

parentDocumentUri | text

fileName | text

mimeType | text


CreateFlags

Combines two flags and returns resulting flag
Params

f1 | number

f2 | number

Return type : number


DeleteDocument

Tries to delete document from given uri and returns result
Params

uriString | text

Return type : boolean


GetDisplayName

Returns display name of given document uri
Params

documentUri | text

Return type : text


GetDocumentId

Returns document id of an uri (should only be grand child)
Params

uriString | text

Return type : text


GetLastModifiedTime

Returns last modified time (epoch) of given document uri
Params

documentUri | text

Return type : text


GetMimeType

Returns mime type of given document uri
Params

documentUri | text

Return type : text


GetSize

Returns size (in bytes) of given document uri
Params

documentUri | text

Return type : text


GetTreeDocumentId

Returns document id of tree uri (should be either tree uri itself or a direct child uri)
Params

uriString | text

Return type : text


IsChildDocumentUri

Returns whether second uri is child of first uri
Params

parentUri | text

childUri | text

Return type : boolean


IsDocumentUri

Returns whether given uri is a document uri
Params

uriString | text

Return type : boolean


IsReadGranted

Returns whether read is available for given uri
Params

uri | text

Return type : boolean


IsTreeUri

Returns whether given uri is a tree uri
Params

uriString | text

Return type : boolean


IsWriteGranted

Returns whether write is available for given uri
Params

uri | text

Return type : boolean


ListFiles

Tries to list files from given dir
Params

dirUri | text

dirDocumentId | text


MoveDocument

Tries to move document from source uri to target dir
Params

sourceUri | text

sourceParentUri | text

targetParentUri | text


OpenDocumentTree

Prompts user to select a document tree
Params

title | text

initialDir | text


OpenSingleDocument

Prompts user to select a single file
Params

title | text

category | text

type | text

extraMimeTypes | list


ReadFromFile

Reads from given uri
Params

uriString | text


ReleasePermission

Relinquish a persisted URI permission grant
Params

uri | text

flags | number


RenameDocument

Tries to rename a document and returns updated uri
Params

documentUri | text

displayName | text

Return type : text


StringFromUriObject

Convert uri to string
Params

uri | any

Return type : text


StringToUriObject

Converts string to uri
Params

uriString | text

Return type : any


TakePersistableUriPermission

Take a persistable URI permission grant that has been offered. Once taken, the permission grant will be remembered across device reboots.
Params

uri | any

flags | number


WriteToFile

Writes to given uri
Params

uriString | text

content | text


Properties

DocumentDirMimeType

Returns mime type of document dir

Property Type : read-only
Accepts : text


FlagGrantReadPermission

Flag to get write permission

Property Type : read-only
Accepts : number


FlagGrantWritePermission

Flag to get read permission

Property Type : read-only
Accepts : number


4.Usages

will be updated frequently

1.Open document tree
image

2.Get access (not necessary for one-time usage)

image

3.Build child document uri

  1. if document is direct child

  1. if document is grandchild

4.Read file/Set picture
image

5.Download

Aix: com.sunny.saf.aix (25.5 KB)

6.Liked my work/Want to donate :heart:

Thank you :hugs:

Hope it helps! :slightly_smiling_face:

15 Likes

What an amazing extension ! Great job!
I’ve only done a few tests and it seems to be working so far.

And it's a shame that your extensions need approval in AI2.
We should change that as soon as possible!

6 Likes

This looks important.

But I am not sure exactly what constitutes a document here.

  • Surely not a Microsoft .doc file?
  • A .html file with links to other .html files nearby?
  • an in-memory runtime structure ?
5 Likes

We can consider document as strictive version of normal files which are accessible only via URIs.
File class is part of java.io while Document/ Document File is implementation of Google for File System to give user more control over his device , files and privacy.

4 Likes

That java.io link sent me into some kind of UAE discussion group.
I was expecting a documentation page for java, maybe under oracle.com?

4 Likes

I guess he did not mean to link any resource but just referring the java.io system.

5 Likes

For Real :star_struck:

That is the Best Extension, Good Job :sunglasses: :+1:

2 Likes

Epic work @vknow360 :slight_smile:

2 Likes

As @ABG says, this looks important, but I am guessing that most developers (which includes me) are not in the lucky 10,000 on this one, and therefore haven't got a clue as to what this is about our how they might use it in their apps. The example blocks give an inkling, but don't really help. Could a better description be provided, in layman's terms, of What this extension does, Why/When/Where one might use it, and How it works in more detail?

I am certain the work adds value to the AI2 development experience, especially in these times of Google privacy concerns and file location lockdown, but there is little point saying "great extension" in a post, if I have no understanding of what it does.

7 Likes

Yes, SAF is quite complicated to understand because Google had no time to explain why they decided to strict app's access to file system. Most developers felt homely by using legacy "File" approach, but launching of Android 10 started causing troubles for them and SAF is the solution to most of their problems.
Using this, you can-

  1. access phone storage*
  2. access SD card*
  3. read from/write to files created by other apps
  4. help user in protecting his/her data and restrict Storage consumed by apps
  5. get access to files and folders which are important to your app.
    In this way, you needn't ask for MANAGE_EXTERNAL_STORAGE permission.
  6. work with non-media files too, such as creating a CSV file of user's data or accessing a PDF file

*: Android/data and Android/obb folders are exception

One major advantage I would like to emphasize is usage of URIs instead of paths.

6 Likes

Example 2

  1. Open document picker and create document


    (Since you now know how to take persistable permission, so I'll skip that part)

  2. Read and write to document

  3. List files

3 Likes

Developers might also find these links about SAF useful in addition to your nice explanation.

3 Likes

Thanks @vknow360 for your explanations, making more sense now as to what one can do with your extension.

4 Likes

Well I have fallen at the first hurdle....

Following your first image, when I click the button I am taken to the device's default file manager (or what looks like it), I went to the root folder, the file manager asked me to give permission to this area (to MIT Companion App), then returns me to the project with this error:

image

3 Likes

I forget to mention that direct child's id should be obtained using GetTreeDocumentId method.

3 Likes

Sorry, I used the wrong block :upside_down_face:

2 Likes

OK that all works. :+1:

Is there no way then, in order to get started, other than the user going into their file manager and granting a permission, to get the root or other folder (tree)? (Open Document Tree)

Perhaps a uri for the ASD ?

I can see how one can traverse through a file list to find sub (folders) trees, and then list files again and so on. An image component accepts a content uri to display an image. Is this going to be the same for other components needing a file?

3 Likes

No, user must grant access through Documents Picker.

I am not sure about this.
Image component works fine.
Same should be correct for audio and video player.

3 Likes

Understood :slight_smile:

3 Likes

SAF is only needed for → non-media files. Media files (jpg, png, mp3, m4a, mp4,...) can be accessed from all paths (outside of the ASD or privateDir) with READ permission.

4 Likes