Test "copy file from assets to shared directory and read it from there" in ai-test

@ewpatton
trying to copy a file from the assets to the shared directory and read it from there
(on Samsung Galaxy A51 running Android 11)

a) using the latest companion app V2.60t7u

  1. copying works, but reading the file not... see logcat output
    read
    Default scope of the app is Shared
    shared
    the correct path to read from would be /storage/emulated/0/Download/WooHoo.txt

  2. trying to copy the file again... and if the file exists, then it should be overwritten... the Exists block returns true, but the Delete method fails to delete the file in the shared directory, logcat output:
    2
    as it seems to be, the Delete method tries to delete the file in the ASD but not in the Shared directory...
    then the Copy method copies the file correctly to the shared directory, however because the file already exists, file WooHoo (1).txt will be created

Download

b) using the apk file
using the Exists method for the Shared storage results in the error message
3

Test project:
copyToShared.aia (2.8 KB)

Taifun

after adding an AskForPermission block
blocks2

and testing the new apk file results in the following error message during Screen.Initialize
4

EDIT: after examining the manifest using APK Editor Studio we can see, that the permission READ_EXTERNAL_STORAGE is missing in the manifest...

manifest <?xml version="1.0" encoding="utf-8" standalone="no"?>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<application android:debuggable="false" android:icon="@mipmap/ic_launcher" android:label="copyToShared" android:name="com.google.appinventor.components.runtime.multidex.MultiDexApplication" android:networkSecurityConfig="@xml/network_security_config" android:preserveLegacyExternalStorage="true" android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher" android:theme="@style/AppTheme">
    <uses-library android:name="org.apache.http.legacy" android:required="false"/>
    <activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize" android:name=".Screen1" android:windowSoftInputMode="stateHidden">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <provider android:authorities="appinventor.ai_taifunbaer.copyToShared.provider" android:exported="false" android:grantUriPermissions="true" android:name="androidx.core.content.FileProvider">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
    </provider>
</application>

copyToShared2.aia (2.9 KB)

Taifun

going further I created a small extension, which adds READ_EXTERNAL STORAGE into the manifest...
this helped for the Screen.Iniitialize event, but now the Copy method failed.because of missing WRITE permissions...

I created another small extension, which adds WRITE_EXTERNAL STORAGE into the manifest...
blocks3
this unfortunately did not help too much, because after examining the manifest with APK Editor Studio we get a restricted permission like this
<uses-permission android:maxSdkVersion="29" android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
remember, I'm using an Android 11 device for my tests...

but after modifying the manifest and changing the permission to only
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
the apk was able to copy the file from the assets to the shared directory
5

which means, if the user likes to copy something to a shared directory, WRITE_EXTERNAL_STORAGE permission still is required without maxSdkVersion="29" restriction!

however reading the file from there also failed, same as in the companion app... tries again to read from ASD rather than the shared directory...

copyToShared3.aia (7.2 KB)
Taifun

Try this:
copyToShared_2.aia (3.0 KB)

1 Like

READ / WRITE permissions are only declared in the Manifest (for devices with Android 4.4 up / API 19+) if DefaultFileScope is set to Legacy. I already mentioned this several times. See e.g.: → here or here.

grafik

1 Like

yes, I have seen your contributions... but I did not understand, what you want to say... are the permissions ok as they are from your point of view or is there still some bug?

ok, my error previously was the scope of the file component... I have overseen, that this exists, too... :sweat_smile: setting it to Lecacy now...

  1. copying to Download and reading from there works now...

  2. the Delete method fails to delete the file in the shared directory if it already exists... probably this is not possible anymore? Because as the file already exists, file WooHoo (1).txt will be created

  3. trying to copy to Documents fails with the following error
    1
    2
    as you can see, a Documents folder exists:
    Screenshot_20210820-132337_My Files
    probably it would make sense to provide a method, which returns all available shared directories?

  4. trying to copy to a shared folder, which does not exist results in the companion app to crash, logcat output:
    3

copyToShared.aia (3.1 KB)

Taifun

Yes, I already tested it on the AI2 test server and with Niotron. It only works with Niotron.

See also here:

I built the same project on the AI2 test server and it works with the folder /Download, but e.g. not with /Documents. (No issues with that on Niotron.)

Tested with APK on a Pixel 2XL (Android 11).

And to display the image READ permission must be granted.
I think this should work without READ permission.
→ @ewpatton

Blocks

Hi @Atom_Developer, it seems to be, you fixed another bug in the new file component...
what about sharing your solution to the MIT App Inventor team?
also in case you were able to fix other bugs in the file component, it would be great to share them...

Taifun

I had given that solution there

If they want to support most available paths like ‘Documents’,
instead of returning ‘null’ in method getContentUriForPath, this code should be added :

else {
        return MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
  }

Full Method Code :

  private static Uri getContentUriForPath(String path) {
    if ("DCIM".equals(path) || "Pictures".equals(path) || "Screenshots".equals(path)) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        return MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
      }
      return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    } else if ("Videos".equals(path) || "Movies".equals(path)) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        return MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
      }
      return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    } else if ("Audio".equals(path) || "Music".equals(path)) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        return MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
      }
      return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
        && ("Download".equals(path) || "Downloads".equals(path))) {
      return MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL);
    } else {
      return MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
    }
  }

It should automatically throw a IOException if the path is not supported for files.

4 Likes

thank you... it seems to be, that @ewpatton forgot to include this in the latest release?
Taifun

I'll take a look, but I thought I had read somewhere that the files table was a read-only view of all files, so I wasn't expecting to be able to update it.

3 Likes

2 posts were split to a new topic: How to copy a text file from root directory to ASD?