Android Question WriteINI and Android 10

AlpVir

Well-Known Member
Licensed User
Longtime User
I always used these 2 functions to write and read INI files (in the style of what I used with the old VB6)
B4X:
Sub WriteIni(mykey As String,myvalue As String,fPath As String,filename As String)
   Dim Map1 As Map
   Map1.Initialize
   If Not(File.Exists(fPath,filename)) Then
    File.WriteMap(fPath, filename, Map1)
   End If
   Map1 = File.ReadMap(fPath,filename)
   Map1.Put(mykey, myvalue)
   File.WriteMap(fPath, filename, Map1)
End Sub

Sub ReadIni(key As String,fPath As String,filename As String) As String
    Dim Map1 As Map
    Map1 = File.ReadMap(fPath,filename)
       For i = 0 To Map1.Size - 1
      If Map1.GetKeyAt(i) = key Then
         Return Map1.GetValueAt(i)
      End If
    Next
    Return ""
End Sub

But now with Android 10 these functions no longer work.
Here is the mistake :
java.io.FileNotFoundException: /storage/emulated/0/AlpVirData/WebCamAlpVir/WebCamAlpVir_Config.txt: open failed: ENOENT (No such file or directory)

I tried using FileProvider which I had already successfully used with this instruction, but the result was negative.
B4X:
File.Copy(File.DirRootExternal, FileName, Starter.Provider.SharedFolder, FileName) ' OK

With the succession of Android versions, unfortunately, a rewrite of part of the code is required !!!
Thanks in advance for some suggestions.
 

DonManfred

Expert
Licensed User
Longtime User
What is the error without using fileprovider?
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
a couple things are not clear from your post.
1) target sdk
2) permission to access external files
3) what are you passing as fPath and filename?
4) regarding fileprovider, is your ini file first copied to the shared folder before you try to read it?
(i appreciate the circular implication of that question, but in earlier posts of yours, you do mention
copying a file to the shared folder (apparently successfully), but you didn't state that this is the case
this time. just asking to be sure.)

are you familiar with "GetSafeDirDefaultExternal"?

there is something strange about your not being able to access a file of your own that you yourself
keep on DirExternal. if you can't read it, how do you manage to save it there in the first place?
it is there, right?
 
Upvote 0

AlpVir

Well-Known Member
Licensed User
Longtime User
@DonManfred : Without using FileProvider
CREO LE 2 CARTELLE
CREO FILE INI
Error occurred on line: 134 (FunzioniComuni)
java.io.FileNotFoundException: /storage/emulated/0/AlpVirData/WebCamAlpVir/WebCamAlpVir_Config.txt: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:496)
at java.io.FileOutputStream.<init>(FileOutputStream.java:235)
at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:448)
at anywheresoftware.b4a.objects.streams.File.WriteMap(File.java:290)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:777)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:354)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:176)
at anywheresoftware.b4a.shell.DebugResumableSub$RemoteResumableSub.resume(DebugResumableSub.java:22)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:250)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:137)
at anywheresoftware.b4a.BA$2.run(BA.java:370)
at anywheresoftware.b4a.BA.setActivityPaused(BA.java:442)
at alpvir.webcamalpvir.main$ResumeMessage.run(main.java:306)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
...............................

@drgottjr :
1) sdk 29
2) In the manifest :
AddPermission("android.permission.WRITE_EXTERNAL_STORAGE")
AddPermission("android.permission.READ_EXTERNAL_STORAGE")
......................
AddApplicationText(
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="$PACKAGE$.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
)
CreateResource(xml, provider_paths,
<files-path name="name" path="shared" />
)

2 bis) In the code
B4X:
Sub Activity_Create(FirstTime As Boolean)
    rp.CheckAndRequest(rp.PERMISSION_READ_EXTERNAL_STORAGE)
    Wait For Activity_PermissionResult (Permission As String, Result0 As Boolean)
    If Result0 = False Then
        Msgbox ("L'app deve poter leggere i risultati intermedi sul device.","INSTALLAZIONE INTERROTTA")
    ExitApplication
        Return
    Else
    rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
    Wait For Activity_PermissionResult (Permission As String, Result1 As Boolean)
    If Result1 = False Then
        Msgbox ("L'app deve poter scrivere i risultati intermedi sul device.","INSTALLAZIONE INTERROTTA")
        ExitApplication
            Return
        Else
            .....................
3)
mykey=Author
myvalue=https://www.lalpinistavirtuale.it
fpath=/storage/emulated/0
filename=AlpVirData/WebCamAlpVir/WebCamAlpVir_Config.txt

4) The row with
File.Copy (File.DirRootExternal, FileName, Starter.Provider.SharedFolder, FileName)
it's part of another app. I reported it only to clarify that I was able, in another app, to use the "FileProvider" technique.

5) I don't know GetSafeDirDefaultExternal, but I will document myself as soon as possible. Is it the technique that should solve the problem?
 
Last edited:
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
thanks for the clarifications.

erel has commented on sdk29.
https://www.b4x.com/android/forum/threads/targetsdkversion-29.110532/#post-689842

fpath=/storage/emulated/0
filename=AlpVirData/WebCamAlpVir/WebCamAlpVir_Config.txt
a little uncomfortable seeing the way you pass fpath and filename. but if it worked before, who's to say it's not right? if google changes its folder naming scheme, however, your code breaks.

i asked whether you had copied your ini file to the shared folder since i noticed you had done it (copy to shared folder) previously in another app. i was just wondering if you had missed that step in the current app. although there is still the matter of why you would be forced to use fileprovider to access your own files.

i use dirinternal since direxternal is mostly an illusion (it's all internal now). and accessing the sdcard (if any) is something else in any case. i see GetSafeDirDefaultExternal referenced here occasionally; it may have more to do with your app running on older devices. i think it starts here:
https://www.b4x.com/android/forum/threads/runtime-permissions-android-6-0-permissions.67689/#content
if you want to fill in the gaps.

so how do you manage to write your ini file to a place you can't read it from? you are saying that writeini works but readini doesn't, right?
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
1 other thing: since you have fileprovider in the manifest, is there a share intent there?
 
Upvote 0

AlpVir

Well-Known Member
Licensed User
Longtime User
I had read Erel's speech. It dates back to October 2019 and now we are in 2020 and the time has come to program for Android 10.
My app (Webcam & Nivometri & Meteo) works with Android 8.1 (tested with a smartphone) and Android 9 (tested with the Genymotion emulator)
It doesn't work with a couple of users (as reported by them) and it doesn't work in the Android Studio emulator (Google Pixel 2 API 29)
I'd like to correct this.
The WriteINI and ReadINI functions work perfectly with Android 8 and 9. All 2 do not work with Android 10.
My app should first create an INI file with default values. To do this, use the WriteINI sub. The sub ReadINI is not even touched (with Android 10) because the error occurs first.
Precisely to the bold instruction
'Dim Map1 As Map
'Map1.Initialize
'If Not (File.Exists (fPath, filename)) Then
'File.WriteMap (fPath, filename, Map1)
'End If
'Map1 = File.ReadMap (fPath, filename)
'Map1.Put (mykey, myvalue)
'File.WriteMap (fPath, filename, Map1)

I would solve everything if I found a way to record (and read) the configuration parameters of the app in a file (the author, the date of first installation, the last webcam used, latitude, longitude, etc.)
I come from the world of the old VB6 and the INI files were used here.
It was easy and intuitive for me to use the same system to record these parameters.
How could the same result be achieved in the B4A world?
Thanks for interesting.

1 other thing: since you have fileprovider in the manifest, is there a share intent there?
I don't understand what you're writing to me
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
i store my "ini" files in dirinternal. i read them in as needed and write them back. they serve the very purposes your ini files do. there is no reason why your readini and writeini subs wouldn't work in dirinternal. direxternal doesn't really exist, and, clearly, google is doing what it can to get us away from it.

regarding fileprovider, i believe you need to have something similar to this in the manifest:
B4X:
AddActivityText(Main,
<intent-filter>
   <action android:name="android.intent.action.SEND" />
   <category android:name="android.intent.category.DEFAULT" />
   <data android:mimeType="image/*" />
</intent-filter>)

it's been a while, but i believe my attempts to use fileprovider (for images) failed until i added the above to the manifest. your files being text, a couple of the statements would be different.

as for android 10, i am sure erel is doing what he can to ensure our current code will not break in android 10. and that he will provide the new code as needed, just as he always has. (i am not related to erel or any member of his family).

i just cobbled together a little test using sdk 29 on my pixel 3a android 10 device. i saved and read back a text file (dirinternal) without incident.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Several mistakes in this code:
B4X:
Sub ReadIni(key As String,fPath As String,filename As String) As String
    Dim Map1 As Map
    Map1 = File.ReadMap(fPath,filename)
       For i = 0 To Map1.Size - 1
      If Map1.GetKeyAt(i) = key Then
         Return Map1.GetValueAt(i)
      End If
    Next
    Return ""
End Sub
1. Reading the file whenever you need to get a value from the map is a bit inefficient.
2. Never use GetKeyAt or GetValueAt. They were useful in a different use case before the For Each iterator was available.
3. This is not the correct way to get values from maps.

Correct way:
B4X:
Sub ReadIni(key As String,fPath As String,filename As String) As String
    Dim Map1 As Map = File.ReadMap(fPath,filename)
    Return Map1.GetDefault(key, "")
End Sub
The whole point of maps is that you can access the keys directly.

About the error:
1. Don't set targetSdkVersion to 29. Set it to 28. It will solve the permission issue assuming that you are correctly requesting the permission at runtime.
2. It is probably better to store the settings file in File.DirInternal.
 
Last edited:
Upvote 0

AlpVir

Well-Known Member
Licensed User
Longtime User
Using sdk 28 and entering in the android manifest:
argetSdkVersion = "28"
I managed to compile the app in the Android version 10 emulator.
The app has been accepted by Google Play and now I await the feedback of those who actually own a device with Android 10.
Many thanks to @drgottjr and @Erel for their valuable support.
 
Upvote 0
Top