Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to open an existing SQLite database with Uri object?

I'm using Intent.ACTION_GET_CONTENT to select a file.

In the method onActivityResult I get the Uri:

Uri selectedfile = data.getData();

How can I use the Uri object selectedfile to open a SQLite database? This does not work:

SQLiteDatabase database = SQLiteDatabase.openDatabase(selectedfile.getPath(), null, SQLiteDatabase.OPEN_READONLY);
like image 975
Stefan M. Avatar asked Jan 02 '19 14:01

Stefan M.


1 Answers

You should not try to open a SQLiteDatabase from a Uri.

SQLiteDatabase is designed to work directly with filesystem: it needs to lock and unlock the database file and manage separate write-ahead logging (-shm and -wal) files, while Uri does not necessarily reference any content in file:// schema. Instead, it can be anything, from https:// or content:// to custom app-defined schemas.

Especially since Android 7.0 Google enforces use of content:// uris for sharing files between apps (this is exactly your case). Taken from here: https://developer.android.com/about/versions/nougat/android-7.0-changes.html:

For apps targeting Android 7.0, the Android framework enforces the StrictMode API policy that prohibits exposing file:// URIs outside your app. If an intent containing a file URI leaves your app, the app fails with a FileUriExposedException exception.

To share files between applications, you should send a content:// URI and grant a temporary access permission on the URI. The easiest way to grant this permission is by using the FileProvider class. For more information on permissions and sharing files, see Sharing Files.

Instead, you should get ContentResolver, open the uri InputStream from it and save the stream content to a local temporary file, then use SQLiteDatabase.openDatabase(filePath) on it:

void openDatabaseFromFileDialog() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
    startActivityForResult(Intent.createChooser(intent, "Select a database"), DB_FILE_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if(requestCode == DB_FILE_REQUEST && resultCode == RESULT_OK) {
        Uri dataUri= data.getData();
        openDatabaseFromUri(dataUri);
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

void openDatabaseFromUri(Uri uri) {
    InputStream inputStream = application.getContentResolver().openInputStream(uri);

    File file = File.createTempFile("sqlite", "");

    FileOutputStream outputStream = new FileOutputStream(file);
    byte[] buff = new byte[1024];
    int read;
    while ((read = inputStream.read(buff, 0, buff.length)) > 0)
        outputStream.write(buff, 0, read);
    inputStream.close();
    outputStream.close();

    openDatabaseFromFile(file.getPath());
}

void openDatabaseFromFile(String filePath) {
    // your code here
}

Also, as you're getting an SQLite stream from another app (probably, third party), I strongly suggest you work with the database in a separate Thread/AsyncTask/etc.. You never know how many Petabytes you receive :)

like image 197
Anton Avatar answered Oct 12 '22 12:10

Anton