0

Problem: I cannot seem to understand why, targeting API 30 (Android 11), my app can still upload and download from the storage\emulated...\download folder for a variety of files all day long, but it fails every time on *.db files... and I was able to on API 29 without issue.

Being a new app, and my first, I don't really want to depend on legacy workarounds. I would rather this work correctly from the start. This app needs to be capable of allowing the user to import and export file attachments to this app's database.

So, back to my original issue... I don't understand why using new FileInputStream(filename) has no issue with using the same path for one file and not another? This is the only shared folder the app and user needs, it caches everything else.

Works with this file and same path

xml file

Doesn't with this file and same path

enter image description here

UPDATE: Added code to clarify intentions to copy.

InputStream source = new FileInputStream(sourcePath);
OutputStream destination = new FileOutputStream(destinationPath);
DatabaseManager.copyDatabase(dbSource, destination);

I can update and replace using the getContentResolver but the same issue still persists -- working for one device and not the other.

InputStream source = getContentResolver().openInputStream(uri);
svstackoverflow
  • 665
  • 6
  • 19
  • 1
    Have you read this documentation around opening documents from shared storage that you don't own : https://developer.android.com/training/data-storage/shared/documents-files Specfically : https://developer.android.com/training/data-storage/shared/documents-files#open-file . Also this table should help when deciding storage apis based on ue case https://developer.android.com/training/data-storage – Mark Jan 16 '22 at 00:54
  • @Mark - Thanks, I did not see this. I'm OOO,. but skimming over this looks like what I need. I will post back results or questions. Again, thanks! – svstackoverflow Jan 16 '22 at 01:44
  • 1
    @Mark - The links you "specifically" emphasized was my solution. This is the first time a solution worked from me that didn't require compounded changes to make it work. I simply and literally copy and pasted the sample code and changed the `setType` and it worked.... that simple. Thanks for taking the time and helping with your suggestion. – svstackoverflow Jan 16 '22 at 04:56
  • @Mark = any thoughts why this works with emulator (Pixel 4 XL API 28) and not with an Android device (Samsung A32 Android 10)? I'm receiving the permission denied again: `E/DB failing to import: java.io.FileNotFoundException: /storage/emulated/0/Download/apologetic.db: open failed: EACCES (Permission denied)` – svstackoverflow Jan 16 '22 at 12:12
  • 1
    Once you gain access to the returned Uri you should be using that via a content resolver to access the contents -https://developer.android.com/training/data-storage/shared/documents-files#input_stream at this point with files your app doesn't own using the file API directly will pretty much fail on Android 11/SDK 30 in shared storage. – Mark Jan 16 '22 at 12:26
  • @Mark - My intention is to copy the database file from the downloads location to the database location managed by Android. I can use `getContentResolver.openInputStream(uri)` and it works for one device but not the other. Do you think I still need to fully recreate the file through a buffer / string? I may be a bit naive at this point... still learning some of this Android development. I updated my post to demonstrate the intentions. – svstackoverflow Jan 16 '22 at 13:25
  • I updated the post with some code to show my intention... which is to copy the file and use the `DatabaseManager` to move it accordingly. – svstackoverflow Jan 16 '22 at 13:28
  • 1
    I don't understand how you get a file path from the uri? The Uri should be abstract path from Storage Access Framework i.e. `content://com.android.providers.media.documents/document/xyz:10` .. or something like this. The fact you are trying to use a file path directly means something is wrong in th code. Also note that you also check the first 16bytes bytes of the file to make sure it matches a sql lite db file : `SQLite format 3\000` https://www.sqlite.org/fileformat.html - example of file copy : https://stackoverflow.com/a/65699427/4252352 – Mark Jan 16 '22 at 15:28

1 Answers1

2

With help from Mark in the comments above this solved my problem. Since the .db extension is not recognizable, I had to use intent.setType("*/*") and validate the file and extension at the resultLauncher (i.e., ActivityResultLauncher).

https://developer.android.com/training/data-storage/shared/documents-files#open-file

private void setupImportDBButton(){
    mImportDB.setOnClickListener(v -> {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        resultLauncher.launch(intent);
    });
}

Included within the ActivityResultLauncher: Basics of the functionality that worked to resolve the multi-device use forced by Android 11 (API 30) requirements. The key was the use of the getContentResolver().

Uri uri = result.getData().getData();
String destinationPath = this.getDatabasePath(strDBName).getPath();

    InputStream source = this.getContentResolver().openInputStream(uri);
    OutputStream destination = this.getContentResolver().openOutputStream(Uri.fromFile(new File(destinationPath)));
    DatabaseManager.copyDatabase(source, destination);
svstackoverflow
  • 665
  • 6
  • 19