Android Question File.delete (result.dir,result.filename) not working when obtained from 'FileHandler'

Cliff McKibbin

Member
Licensed User
My database 'OurBooks.DB' is stored and accessed using rp.getsafeDirDefaultExternal("OurBooks"),"OurBooks.DB". My App includes an ability to send this DB to another device using email. It could then be loaded on that device or even sent back to the original device and restored onto that device. The restore involved manually moving the file from the device Download directory to the Android Internal storage/Android/data/b4a.OurBookClub/files/OurBooks.

This worked fine until Android 11 took away user access to the Android/data files.

A previous forum question resulted in my using Erel's FileHandler to allow the user to access the 'Download' directory.
I use it to choose the backup of their '.DB' file and then do a file.copy of result.dir,result.filename to the rp. structure. The routines below work just fine for the restore on Android 7, 8, and 11 on my test devices.

I then wanted to add a file.delete (result.dir,result.filename). Deletion of the file would prevent a later mistake in menu selection from wiping over the user's updated DB. I also found that if the file is left in the Download Dir, a subsequent 'save' of the attachment of an email will create a modified name 'OurBooks(1).DB. As you will note in my logic, I check the result.RealName to make sure the user doesn't choose the wrong file and the (1) file fails. I could fix that problem but I would prefer to delete the file.

This file.delete command fails to delete the file in the 'Download' directory and I get the MsgboxAsync("The restore DB was not deleted " , "Delete Check") as shown in the logic below.

My question are:
1. 'Should this file.Delete work or is there some prohibition in Android that does not allow us to delete the file.
2. or, should I be giving some other 'Dir' and 'Filename' to accomplish the delete.

I added the following routine to Erel's FileHandler to find the '.DB' file.
It is modeled on the original 'public sub Load' in the forum explanation for FileHandler.
It uses */* to find all files as I couldn't come up with a meme to just show the .DB files.
If anyone knows the correct meme, I would appreciate that as well.

B4X:
Public Sub LoadDB As ResumableSub
    Dim cc As ContentChooser
    cc.Initialize("cc")
    ' this worked to expose our .db file
    cc.Show ("*/*", "Choose your DB file")
    Wait For CC_Result (Success As Boolean, Dir As String, FileName As String)
    Dim res As LoadResult = CreateLoadResult(Success, Dir, FileName)
    If res.Success Then ExtractInformationFromURI(res.FileName, res)
    Return res
End Sub

The following routines are in b4XMainPage
This is the routine to find the file in the 'Download' Dir.
It is modeled on the original forum article and works with no problem.

B4X:
Private Sub RestoreDB
    #if B4A
    Wait For (FileHandler1.LoadDB) Complete (Result As LoadResult)
    #else if B4i
    Wait For (FileHandler1.LoadDB(Me, B4XPages.GetNativeParent(Me).TopRightButtons.Get(1))) Complete (Result As LoadResult)
    #end if
    ProcessRestore(Result)
End Sub

This is the routine to process the 'Restore' by using the file.copy as shown.
The file.delete fails.

B4X:
Private Sub ProcessRestore(Result As LoadResult)
    If Result.Success Then
        ' check for db name
        x=Result.RealName
        If x.Contains ("OurBooks.DB") Then
            ' ok
        Else
            MsgboxAsync("You must select the file OurBooks.DB" , "File Check")
            Return
        End If
        
        ' close the DB-note it must be re-opened below
        Starter.CloseDB
        Try
            Log (Result.Dir & " " & Result.FileName)
            ' manipulate the filename into a dir and filename
            File.Copy(Result.dir,Result.FileName,rp.getsafeDirDefaultExternal("OurBooks"),"OurBooks.DB" )
            MsgboxAsync("Your database has been restored " , "Restore Complete")
        Catch
            MsgboxAsync("The file: " & X1 & " " & X2 & " was not found", "Restore File Check")
        End Try
        ' reopen the db in either case (ok or error)
        Starter.LoadDB
        ' delete the input file
        If File.Delete(Result.Dir,Result.filename)=False Then
            MsgboxAsync("The restore DB was not deleted " , "Delete Check")
        End If
    End If
End Sub
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Generally speaking, you cannot delete the files returned from ContentChooser.

The code to try is:
B4X:
Private Sub Button1_Click
    Dim cc As ContentChooser
    cc.Initialize("cc")
    cc.Show("*/*", "choose file to delete")
    Wait For CC_Result (Success As Boolean, Dir As String, FileName As String)
    If Success Then
        Log(Dir & ", " & FileName)
        Msgbox2Async("Delete file?", "Title", "Yes", "Cancel", "No", Null, False)
        Wait For Msgbox_Result (Result As Int)
        If Result = DialogResponse.POSITIVE Then
            Dim cr As ContentResolver 'ContentResolver library
            cr.Initialize("")
            Dim u As Uri
            u.Parse(FileName)
            Log(cr.Delete(u, "", Null))
        End If
    End If
End Sub

However it is likely to fail as the third party app doesn't set the required permission for your app to be able to delete the file.
 
Last edited:
Upvote 0

Cliff McKibbin

Member
Licensed User
Thanks Erel.
I did test your routine and it gave the following error for needing permission:

java.lang.SecurityException: Permission Denial: writing com.android.providers.downloads.DownloadStorageProvider uri content://com.android.providers.downloads.documents/document/8226 from pid=25712, uid=10078 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()

I looked for an ability to get these permissions but didn't find much in the forum.
For now, I am going to remind the user to delete the file in the 'Download' Dir.

For now, I am continuing to use '.getsafeDirDefaultExternal' for my file storage, but I will begin experimenting with file.dirinternal since that is the direction you suggest in 'your features to avoid' article.
 
Upvote 0
Top