API 30 workarounds v2

API 30 introduced some new limitations. These limitations have not yet been completely implemented in AI2, so many components/extensions don't work as expected, or at all.

Contents:

  1. API 30 doesn't support WRITE_EXTERNAL_STORAGE permission.
    If you need to write to the external storage, you must use a shared folder.

    Affected components/extensions:

    Camera component: doesn't work (relies on WRITE_EXTERNAL_STORAGE).
    File component: doesn't work for writing to external storage (same reason).
    Any other extension/component that relies on WRITE_EXTERNAL_STORAGE (same reason).

  2. API 30 introduced Package Visibility limitations.
    Apps can't see/interact with other installed apps. This can have severe implications if your app relies on interacting or checking another's apps info, version, etc.

    Affected components/extensions:

    Taifun Package Manager extension: works only with some basic apps like settings, calculator, etc.
    PkgUtils extension (same problem).
    Any other query on other installed packages either through components, extensions, or activity starters (same problem).

Luckily there are workarounds:

1. Writing to external storage without write permissions

1-1. Use the Shared Folders

The right way to write to external storage is to use specific directories, known as shared folders. These folders don't require WRITE_EXTERNAL_STORAGE permission, therefore the new API limitation has almost no effect on them. You can write freely inside them. These are the directories:

  • /storage/emulated/0/Documents
  • /storage/emulated/0/Download
  • /storage/emulated/0/Pictures
  • /storage/emulated/0/DCIM
  • /storage/emulated/0/Music
  • /storage/emulated/0/Movies

There may be more (for example /Ringtones or /Alarms).
You can also create subdirectories in these. No write permissions required.

The file component doesn't work for writing to external storage, luckily you can use the TaifunFile extension, which doesn't rely on WRITE_EXTERNAL_STORAGE permission. Just follow those rules:

Rule 1: Use the Android File API:

Try with file:// first, as it invokes the File API, which is supported in API 30.

Use whole paths (no relative dirs) when writing to external storage:

file:///storage/emulated/0/Music/song.mp3 :white_check_mark: (works with Taifun File extension)

Note that other extensions/components work without file://

//storage/emulated/0/Pictures/my-photo.jpg :white_check_mark: (works with Pro Camera extension)

Rule 2: You can only write in the Shared Folders:

file:///storage/emulated/0/RandomFolder/song.mp3 :x: (doesn't work in non-shared folders)

Components & extensions that don't rely on WRITE_EXTERNAL_STORAGE will work if you follow these two rules.

Note that because AI2 has not yet implemented the necessary changes, some problems exist even with shared folders. Check out this if you want to work with shared folders. Also this. Then check my guide Working with shared folders to understand this one (important):

1-2. The camera component

The camera component doesn't work (it relies on WRITE_EXTERNAL_STORAGE), but luckily there are some alternatives:

2. Query installed packages

Due to Package Visibility changes in API 30, applications can't see other applications without specific permissions. Many apps rely on checking for other installed apps though.

There are two ways to overcome this;

  • Queries

    This means you have to explicitly define all the packages that your app is going to check/communicate with, in your app's manifest.
    If you know how to do this, go ahead. Get more information on queries here.

  • QUERY_ALL_PACKAGES

    The other way is to add the QUERY_ALL_PACKAGES permission in your app's manifest. This will make all apps visible to your app. Note that Google Play doesn't like QUERY_ALL_PACKAGES and recommends using queries instead. But if there is good reason for using it (for something that's counter-intuitive to do with queries, like an app manager, or an app store), they will let you use it.

    Add this line in your app's manifest:
    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

    Or import my extension, tsf.QueryAllPackages.aix which does this automatically.
    You will find info on how to use (or build your own) on github.

    This is definitely useful for those who don't know how to edit their manifest. I used to be one of them. More info on github.

    With this workaround you can use PkgUtils and TaifunPM extensions, without getting limited results. Just make sure to ask for the permission android.permission.QUERY_ALL_PACKAGES in your Screen1 too.


That's it for now. Feel free to mention other temporary workarounds that may help troubleshooting/development. Thank you and have a good day :sunglasses: :v:

7 Likes

Check this APK (ā†’ Legacy version) and you will understand:

This folder doesn't work at all (neither for non-media files nor for other files (like .txt, .pdf, ...).
But this folder works (as shared storage):
/storage/emulated/0/Download

For non-media files only these folders work:

  • /storage/emulated/0/Documents
  • /storage/emulated/0/Download
2 Likes

did you use the DefaultFileScope "App" in the Screen properties for your tests?
defaultScope

did you test this only in the companion app or also after building using the apk file?
which Android version did you use for your tests?

Taifun


Trying to push the limits! Snippets, Tutorials and Extensions from Pura Vida Apps by icon24 Taifun.

Screen DefaultFileScope = App
Tested on apk (works)
Android 11 :white_check_mark:

1 Like

Okay, fixed, it was a typo. Thanks for pointing out :v:

You can create the sub directories, but it seems like you cannot do anything with them at least within /Download

I am unable to write to or read from them using the standard file components.

I started off with @Anke's test code but added a couple more features to try to test read or write from a sub directory.

I have attached it here.

filestoragetest.aia (4.8 KB)

When you compile the app and install, the phone will give you Error 908 permission_ WRITE_EXTERNAL_STORAGE has been denied. This is with or without the two permission boxes checked in the properties page.

I currently have Android 11.

The file component relies on WRITE_EXTERNAL_STORAGE to write stuff, but TaifunFile doesn't.
And because WRITE_EXTERNAL_STORAGE doesn't exist in Android 11, the file component does not work for writing to external storage, even on shared folders.

Ok, I see, I will go back to that then. I was thinking in the new update Taifun's Taifun File work would have been rolled into the the FileStorage block stuff, but maybe that is future.

1 Like

Wait a minute here, TaifunFile, does not have a read file block? How can you use it then on subdirectories within /Download? That is broken with the built in FileStorage blocks!

Thanks,
Jonathan

Maybe you need to ask for READ permissions:

But I would try without this first.

You can use the file component to save a file in ASD and then move it to a shared folder using taifun file.

The file component works ok in ASD.

Updated guide to version 2. :v:

Great idea! I will keep everything in the Shared storage, copy it into the ASD as a working copy to open it, then copy it back to Shared after saving and delete my working copy. I think that will work.

1 Like

I'm not sure that's needed. I'd try working directly with the file in the shared folder.

Yes, this another bug with the File component. This method (File.MakeDirectoy) requires WRITE permission (it must be declared in the Manifest).

But the storage permissions are declared this way:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:maxSdkVersion="29" android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

If you remove android:maxSdkVersion="29" from the Manifest, it will work.
But this doesn't make sense at all, because WRITE permission does no longer exists on Android 11.
@ewpatton

Yes, I actually just gave up on reading and writing anything to the shared folders currently including the workaround suggested by anonwins. I implemented the workaround suggested by anonwins above, but instead of using the ASD used the /Download, folder as my workspace as the read file block works great in /Download but no further. So I copied the file from /Download/Test/abc.txt to /Download/abc.txt, edited the text saved it, and then copied it back to /Download/Test/abc.txt. (I would delete the files once copied both directions). This works great in the companion. However, as soon as I install it as an app, I get the permission errors. This is even after checking their boxes, and explicitly asking for permission in the initialize block. I am going back to working just in the ASD.

If you remove android:maxSdkVersion="29" from the Manifest, it will work .
But this doesn't make sense at all, because WRITE permission does no longer exists on Android 11.

That is good to know, however, because of the whole bundling thing, I had to eliminate my manual APK edit. (The one I had been using was android:hasFragileUserData="true") As I haven't found a simple way to unpack and edit the manifest in a bundle. (If that is a quick win for anybody at MIT Appinventor, I can't think of anyone who would not want the option of keeping their data when uninstalling a app!)

I will attach the currentcopyToSharedTest2.aia (58.8 KB) .aia if anybody else is interested in playing with it. (It is still based off of yours Anke.)

@anonwins I now did some tests using /Download and /Documents.... while the following blocks work for me in the companion app it unfortunately does not work anymore after building the app on Samsung Galaxy A51 running Android 11


trying to copy a text file from the assets to /Download or /Documents...
the file will not be copied and the Exists method displays the following message

error
same for the /Documents folder

Attached the example project filetest.aia (34.2 KB)

Taifun
PS: my tests here are able to copy a file to /Download using the file component, but this still fails for /Documents, which is an already known bug to fix...

I just build and installed your filetest.aia, changed nothing, and it works without the slightest problem:

OK now I found what goes on. It's not something new. It's been mentioned by @Anke multiple times. Let me try to say it simply: files saved in shared folders, belong to the installation of the app that created them. other apps (or even a re-installation of the same app) cannot overwrite them. So it will give you these permission errors (EACCES) if the installation of the app is different than the one that created the files in the first place.

The problem is not only for overwriting/deleting the file though. I still haven't managed to read its content.

It appears that we cannot:

  1. read a saved file in shared folder (file component says not found)
  2. overwrite/delete a saved file in shared folder if A) we reinstall the app, B) using another app

I haven't tested with DefaultFileScope:Legacy though. If I understand right, these problems just go away when you switch to legacy. Haven't tested. (edit: they don't. see the following link)

Check my latest guide on working with shared folders (written today).

you mentioned, you are using an Android 11 device for your tests, which one?

yes, I'm aware of that, but it looks like I forgot to delete it from the Download folder before starting the test...which means, copying to /Download works for me now...

however copying to /Documents still fails


error2
I would like to invite others to test, too... what do you get?
here is the apk file to download https://drive.google.com/file/d/1a98oQiPNIoYijpkjLpkc48jwu0iR8e9X/view?usp=sharing

Taifun

For me they always work the same (documents and download).

I have a Redmi 7, LineageOS 18.1, Android 11

In other news, I made a TaifunFile/FileComponent front-end myself today: TSF File Operations


edit: screenshots v1.1

If you give it a try, I'm sure it can help you figure out things quickly :smiley:

Also there are a few things missing, like file listing, perhaps I'll make a new version soon lol.

Fun fact: the name of the project is still filetest_copy so technically this is the app that you started :stuck_out_tongue:

If you want to inspect/edit it: filetest_copy(1).aia (148.1 KB)
edit: get version 1.1 from google drive