I'm using SqliteImportExporter library to import and export my database file to public folder "Documents". I don't think the file has a MIME type, it's just created from a stream of bytes and has a .db file extension.
After targeting Android API 30, if I uninstall and reinstall my app, it cannot access the file it created earlier, to either import it or overwrite it.
The Android documentation talks about using ACTION_OPEN_DOCUMENT_TREE
intents to give access to the entire directory and it's contents. After selecting the directory to give permission, it still have no access. Is there something magic about the URI returned by this process, or can I still use the library after getting permission? I am providing the library with an absolute path to the file - which works until I target API 30 and run the app on an API 30 AVD.
Can someone point me in the right direction on how to proceed please?
EDIT: My question simply now boils down to, How can one simply copy a file to/from the shared folders (e.g. documents folder) when targeting API 30, and still maintain support for API 21 onwards.
Note that this library does not appear to be being maintained.
Yes. For example, you could wrap it in a
DocumentFile
usingDocumentFile.fromTreeUri()
, then use thatDocumentFile
to getDocumentFile
objects representing the documents directly in the root of that tree, etc.Personally, I used
ACTION_CREATE_DOCUMENT
/ActivityResultContracts.CreateDocument()
andACTION_OPEN_DOCUMENT
/ActivityResultContracts.OpenDocument()
in this sample project demonstrating importing and exporting databases (covered in this book, FWIW).You already have code for this. Your issue is with an uninstall/reinstall cycle and losing permissions to that file. You have a few choices for dealing with that.
First, you could ask the user to remove that file created by your old app installation, so you can write your hard-coded(?) filename to your hard-coded destination again. The user could do this with a pre-installed file manager, for example, if the user has one.
Second, you could let the user choose a filename. If they choose one that you cannot use due to this permissions issue, tell the user and ask the user to give you another filename. Or, you could automatically adjust the filename, the way that Web browsers do when downloading files (e.g., add
(1)
to the name).Third, you could stop writing to
Documents/
and instead write togetExternalFilesDir()
(method onContext
). The two main downsides is that this directory is not as convenient and that its contents get removed when your app is uninstalled.Fourth, you could do what I do in that sample: use
ACTION_CREATE_DOCUMENT
/ActivityResultContracts.CreateDocument()
to let the user choose where to create the exported database and what to call it. You can then useContentResolver
andopenOutputStream()
to get anOutputStream
for theUri
that you get back, and you can copy the bytes of the database to thatOutputStream
. For a later import, useACTION_OPEN_DOCUMENT
/ActivityResultContracts.OpenDocument()
to let the user choose an exported database for you to use, and useContentResolver
andopenInputStream()
to reverse the process. This gives the user maximum flexibility, as you are not locking them into a particular location and a particular filename. It does mean that the user will need to choose a location and confirm your suggestion of a filename.There are other possibilities, such as fussing with
ACTION_OPEN_DOCUMENT_TREE
, but that one in particular is cumbersome IMHO.