Android Question Permissions not sufficient , still can't access external files correctly.

Adamdam

Active Member
Licensed User
Longtime User
Dear all,
Greetings,
I try to access external files from my app (i.e. File.DirRootExternal) but access get error.
I used this code to grant Permissions :



</

Sub File_Permission
Dim PermissionsGoodFlag As Boolean = True
If PermissionsGoodFlag Then
rp.CheckAndRequest(rp.PERMISSION_READ_EXTERNAL_STORAGE)
Wait For Activity_PermissionResult (Permission As String, YaeOrNay As Boolean)
If YaeOrNay = False Then
PermissionsGoodFlag = False
End If
End If

PermissionsGoodFlag = True

If PermissionsGoodFlag Then
rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
Wait For Activity_PermissionResult (Permission As String, YaeOrNay As Boolean)
If YaeOrNay = False Then
PermissionsGoodFlag = False
End If
End If
End Sub
>

and even this is the content of manifest file:

</
AddManifestText(
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="31"/>
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
CreateResourceFromFile(Macro, Themes.DarkTheme)
AddPermission("android.permission.READ_EXTERNAL_STORAGE")
AddPermission("android.permission.WRITE_EXTERNAL_STORAGE")
'End of default text.
>

What I must add to get a full Permissions to access external files

Best regards
 

zed

Active Member
Licensed User
Use the code tag </>

I try to access external files from my app (i.e. File.DirRootExternal) but access get error.

File.DirRootExternal is broken. It should no longer be used.

Watch this
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
Recently I am also facing the same issue with new Android which requires new targetSDK version.

The workaround is:
 
Upvote 0

Adamdam

Active Member
Licensed User
Longtime User
Use the code tag </>



File.DirRootExternal is broken. It should no longer be used.

Watch this
Many thanks Mr. Zed.
How hac I replace "File.DirRootExternal by other "GetSafeDirDefaultExternal" by simple way, please.
Kindly are you have any example code, please ?
 
Upvote 0

Alex_197

Well-Known Member
Licensed User
Longtime User
Many thanks Mr. Zed.
How hac I replace "File.DirRootExternal by other "GetSafeDirDefaultExternal" by simple way, please.
Kindly are you have any example code, please ?
B4X:
dim rp as RuntimePermissions
dim DirName as String
DirName=rp.GetSafeDirDefaultExternal("")
 
Upvote 0

Adamdam

Active Member
Licensed User
Longtime User
B4X:
dim rp as RuntimePermissions
dim DirName as String
DirName=rp.GetSafeDirDefaultExternal("")
Many thanks Mr Alex.
I tried this code :
Please correct it,


B4X:
            DirName = rp.GetSafeDirDefaultExternal("")

            If File.Exists(DirName & "/Abc", "As_15.vx") = False Then
                File.Copy(File.DirAssets,"As_15.vx", DirName & "/Abc","As_15.vx")
            End If
            
            SQL2.Initialize ( DirName & "/Abc" ,"As_15.vx", False)
            Curser2 = SQL2.ExecQuery("SELECT * FROM tabel1 order by id" )
            

            For i = 1 To Curser2.RowCount
                  .
                  .
 
Upvote 0

Computersmith64

Well-Known Member
Licensed User
Longtime User
This question has been asked multiple times in the forum & the answer is always the same: If you want to access device storage that isn't app specific, you need to use a ContentResolver. You can no longer access non app specific storage without giving the user the user the opportunity to interact with the process of saving or loading files. Search the forum for ContentResolver...

- Colin.
 
  • Like
Reactions: zed
Upvote 0

Alex_197

Well-Known Member
Licensed User
Longtime User
Many thanks Mr Alex.
I tried this code :
Please correct it,


B4X:
            DirName = rp.GetSafeDirDefaultExternal("")

            If File.Exists(DirName & "/Abc", "As_15.vx") = False Then
                File.Copy(File.DirAssets,"As_15.vx", DirName & "/Abc","As_15.vx")
            End If
           
            SQL2.Initialize ( DirName & "/Abc" ,"As_15.vx", False)
            Curser2 = SQL2.ExecQuery("SELECT * FROM tabel1 order by id" )
           

            For i = 1 To Curser2.RowCount
                  .
                  .
should be like this


B4X:
dim rp as RuntimePermissions
dim DirName as String
DirName=rp.GetSafeDirDefaultExternal("abc")
 
Upvote 0

Computersmith64

Well-Known Member
Licensed User
Longtime User
should be like this


B4X:
dim rp as RuntimePermissions
dim DirName as String
DirName=rp.GetSafeDirDefaultExternal("abc")
This does not answer the original question - which was "I try to access external files from my app (i.e. File.DirRootExternal) but access get error."

GetSafeDirDefaultExternal does not get you access to DirRootExternal.

- Colin.
 
Upvote 0

Adamdam

Active Member
Licensed User
Longtime User
This does not answer the original question - which was "I try to access external files from my app (i.e. File.DirRootExternal) but access get error."

GetSafeDirDefaultExternal does not get you access to DirRootExternal.

- Colin.
Thanks Mr. Smith,
So, what is the solution?
 
Upvote 0

Computersmith64

Well-Known Member
Licensed User
Longtime User
Thanks Mr. Smith,
So, what is the solution?
You can (in theory) also use Environment.getExternalStoragePublicDirectory - but you have to pass it one of DIRECTORY_MUSIC, DIRECTORY_PODCASTS, DIRECTORY_RINGTONES, DIRECTORY_ALARMS, DIRECTORY_NOTIFICATIONS, DIRECTORY_PICTURES, DIRECTORY_MOVIES, DIRECTORY_DOWNLOADS, DIRECTORY_DCIM, or DIRECTORY_DOCUMENTS - so you're limited to using one of those. Also, I don't think there's a B4X implementation of it, so you'd have to use JavaObject to call it. Oh - & it was deprecated in Android API level 29 (Android 10), so assuming it's even still working now, it may not work for much longer &/or it might not work on later Android versions that have clamped down on accessing shared storage.

Which brings us back to ContentResolver... :)

- Colin.
 
Upvote 0

Alex_197

Well-Known Member
Licensed User
Longtime User
This does not answer the original question - which was "I try to access external files from my app (i.e. File.DirRootExternal) but access get error."

GetSafeDirDefaultExternal does not get you access to DirRootExternal.

- Colin.
Just checked what File.DirRootExternal) will return me - it was /storage/emulated/0 on my Android 14
 
Upvote 0

Alex_197

Well-Known Member
Licensed User
Longtime User
& what was the result of trying to save a file to it?

Here is my example

B4X:
Private Sub Test
    Try
        Dim DirTo,DirTo1,DirTo2 As String
        
        Dim FileName,str,html As String
            
        FileName="Test.html"
        
    
                
        DirTo1=File.DirDefaultExternal       
        DirTo2=rp.GetSafeDirDefaultExternal("")

        If DirTo1=DirTo2 Then
            Log("AAA")
        Else
            Log("BBB")
        End If
        
        Log(DirTo1 & CRLF & DirTo2)
    
        DirTo=DirTo1
        
        File.Delete(DirTo,FileName)
    
    
        str="<html>        <body>        <h1>Hello World</h1>"       
        str=str & "</body>        </html>"
        
        File.WriteString(DirTo,FileName,str)
        
        If File.Exists(DirTo,FileName) Then       
            html=File.ReadString(DirTo,FileName)
            Log(html)
            WebView1.LoadHtml(html)
        End If
        
    Catch
        Log(LastException)
    End Try
End Sub

and what is interesting that DirTo1 and DirTo2 are identical

B4X:
AAA
/storage/emulated/0/Android/data/b4a.TestExtDir/files
/storage/emulated/0/Android/data/b4a.TestExtDir/files
<html>        <body>        <h1>Hello World</h1></body>        </html>
 
Upvote 0

Computersmith64

Well-Known Member
Licensed User
Longtime User
Here is my example

B4X:
Private Sub Test
    Try
        Dim DirTo,DirTo1,DirTo2 As String
       
        Dim FileName,str,html As String
           
        FileName="Test.html"
       
   
               
        DirTo1=File.DirDefaultExternal      
        DirTo2=rp.GetSafeDirDefaultExternal("")

        If DirTo1=DirTo2 Then
            Log("AAA")
        Else
            Log("BBB")
        End If
       
        Log(DirTo1 & CRLF & DirTo2)
   
        DirTo=DirTo1
       
        File.Delete(DirTo,FileName)
   
   
        str="<html>        <body>        <h1>Hello World</h1>"      
        str=str & "</body>        </html>"
       
        File.WriteString(DirTo,FileName,str)
       
        If File.Exists(DirTo,FileName) Then      
            html=File.ReadString(DirTo,FileName)
            Log(html)
            WebView1.LoadHtml(html)
        End If
       
    Catch
        Log(LastException)
    End Try
End Sub

and what is interesting that DirTo1 and DirTo2 are identical

B4X:
AAA
/storage/emulated/0/Android/data/b4a.TestExtDir/files
/storage/emulated/0/Android/data/b4a.TestExtDir/files
<html>        <body>        <h1>Hello World</h1></body>        </html>

The original question was "I try to access external files from my app (i.e. File.DirRootExternal) but access get error."

Your example is writing to the app specific storage. DirDefaultExternal & GetSafeDirDefaultExternal("") return the exact same directory - ie: the place that the app stores it's app-specific data. DirRootExternal returns the root of external shared storage - which you now cannot write to / read from without user interaction since SDK 30.

- Colin.
 
Upvote 0

Alex_197

Well-Known Member
Licensed User
Longtime User
The original question was "I try to access external files from my app (i.e. File.DirRootExternal) but access get error."

Your example is writing to the app specific storage. DirDefaultExternal & GetSafeDirDefaultExternal("") return the exact same directory - ie: the place that the app stores it's app-specific data. DirRootExternal returns the root of external shared storage - which you now cannot write to / read from without user interaction since SDK 30.

- Colin.
Yes, you're right. But why don't use rp.GetSafeDirDefaultExternal("") instead of DirRootExternal?
 
Upvote 0

Computersmith64

Well-Known Member
Licensed User
Longtime User
Yes, you're right. But why don't use rp.GetSafeDirDefaultExternal("") instead of DirRootExternal?
Perhaps he wants the data to persist after the app is uninstalled? Or perhaps he's trying to import data that was downloaded to the Downloads directory? In my case, I have an app that allows the user to create a backup of their SQLite database & I don't want them to accidentally remove the backups if they uninstall the app, so I create the backup in app-specific storage, then copy it to shared external storage just in case. If they decide to restore a backup, it's restored from the external storage.

Either way it's a bit of a PITA because it's definitely not as easy as it used to be, but still achievable. According to this -> https://developer.android.com/training/data-storage/shared/documents-files there are a few different ways you can do it, but I prefer to use a ContentResolver because you can ask the user once where they want to save the file & then persist the permission so you don't have to ask them again.

- Colin.
 
Upvote 0

Adamdam

Active Member
Licensed User
Longtime User
You can (in theory) also use Environment.getExternalStoragePublicDirectory - but you have to pass it one of DIRECTORY_MUSIC, DIRECTORY_PODCASTS, DIRECTORY_RINGTONES, DIRECTORY_ALARMS, DIRECTORY_NOTIFICATIONS, DIRECTORY_PICTURES, DIRECTORY_MOVIES, DIRECTORY_DOWNLOADS, DIRECTORY_DCIM, or DIRECTORY_DOCUMENTS - so you're limited to using one of those. Also, I don't think there's a B4X implementation of it, so you'd have to use JavaObject to call it. Oh - & it was deprecated in Android API level 29 (Android 10), so assuming it's even still working now, it may not work for much longer &/or it might not work on later Android versions that have clamped down on accessing shared storage.

Which brings us back to ContentResolver... :)

- Colin.
Many thanks for your attention.
Kindly how can I use ContentResolver instead of File.DirRootExternal are you have any example, please?
for very simple following code :

B4X:
            If File.Exists(File.DirRootExternal * "\Abc", "As_16.vx") = False Then
                File.Copy(File.DirAssets, "As_16.vx", File.DirRootExternal * "\Abc", "As_16.vx")
            End If
            
            SQL2.Initialize(File.DirRootExternal * "\Abc", "As_16.vx", False)
            Curser2 = SQL2.ExecQuery("SELECT * FROM Tabel1 order by id" )
            
            For i = 1 To Curser2.RowCount
                .
                .
                .
Thanks on advance for you.
 
Upvote 0
Top