Updating an SQLite database using the Google Drive Android API

2.2k Views Asked by At

Using the Google Drive API for Android and some answers on Stack Overflow, I have managed to implement Google Sign-In into my app, and backup the SQLite database stored on the user's device to Google Drive.

For reference, here is how I save the database to Google Drive (this is done in a final class called DriveDbHandler):

private static final String LOG_TAG = "DriveDbHandler";

private static final String PACKAGE_NAME = "com.package.example";

private static final String DATABASE_PATH =
        "/data/data/" + PACKAGE_NAME + "/databases/" + DbHelper.DATABASE_NAME;

private static final String FILE_NAME = DbHelper.DATABASE_NAME;
private static final String MIME_TYPE = "application/x-sqlite-3";

private DriveDbHandler() {
}


public static void tryCreatingDbOnDrive(final GoogleApiClient googleApiClient) {
    // We need to check if the database already exists on Google Drive. If so, we won't create
    // it again.

    Query query = new Query.Builder()
            .addFilter(Filters.and(
                    Filters.eq(SearchableField.TITLE, FILE_NAME),
                    Filters.eq(SearchableField.MIME_TYPE, MIME_TYPE)))
            .build();
    DriveFolder appFolder = Drive.DriveApi.getAppFolder(googleApiClient);

    appFolder.queryChildren(googleApiClient, query).setResultCallback(
            new ResultCallback<DriveApi.MetadataBufferResult>() {
                @Override
                public void onResult(@NonNull DriveApi.MetadataBufferResult metadataBufferResult) {
                    if (!metadataBufferResult.getStatus().isSuccess()) {
                        Log.e(LOG_TAG, "Query for " + FILE_NAME + " unsuccessful!");
                        return;
                    }

                    int count = metadataBufferResult.getMetadataBuffer().getCount();

                    Log.d(LOG_TAG, "Successfully ran query for " + FILE_NAME + " and found " +
                            count + " results");

                    if (count > 1) {
                        Log.e(LOG_TAG, "App folder contains more than one database file! " +
                                "Found " + count + " matching results.");
                        return;
                    }

                    // Create the database on Google Drive if it doesn't exist already
                    if (count == 0) {
                        Log.d(LOG_TAG, "No existing database found on Google Drive");
                        saveToDrive(googleApiClient);
                    }
                }
            });
}

private static void saveToDrive(final GoogleApiClient googleApiClient) {
    Log.d(LOG_TAG, "Starting to save to drive...");

    // Create content from file
    Drive.DriveApi.newDriveContents(googleApiClient).setResultCallback(
            new ResultCallback<DriveApi.DriveContentsResult>() {
                @Override
                public void onResult(@NonNull DriveApi.DriveContentsResult driveContentsResult) {
                    if (!driveContentsResult.getStatus().isSuccess()) {
                        Log.w(LOG_TAG, "Drive contents result not a success! " +
                                "Not saving data to drive.");
                        return;
                    }

                    Log.d(LOG_TAG, "Created drive contents for file");
                    createNewFile(googleApiClient, driveContentsResult.getDriveContents());
                }
            });
}

private static void createNewFile(GoogleApiClient googleApiClient, DriveContents driveContents) {
    // Write file to contents (see http://stackoverflow.com/a/33610727/4230345)
    File file = new File(DATABASE_PATH);
    OutputStream outputStream = driveContents.getOutputStream();
    try {
        InputStream inputStream = new FileInputStream(file);
        byte[] buf = new byte[4096];
        int c;
        while ((c = inputStream.read(buf, 0, buf.length)) > 0) {
            outputStream.write(buf, 0, c);
            outputStream.flush();
        }
        outputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    Log.d(LOG_TAG, "Written file to output stream of drive contents");

    // Create metadata
    MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
            .setTitle(FILE_NAME)
            .setMimeType(MIME_TYPE)
            .build();

    // Create the file on Google Drive
    DriveFolder folder = Drive.DriveApi.getAppFolder(googleApiClient);
    folder.createFile(googleApiClient, metadataChangeSet, driveContents).setResultCallback(
            new ResultCallback<DriveFolder.DriveFileResult>() {
        @Override
        public void onResult(@NonNull DriveFolder.DriveFileResult driveFileResult) {
            if (!driveFileResult.getStatus().isSuccess()) {
                Log.w(LOG_TAG, "File did not get created in Google Drive!");
                return;
            }

            Log.i(LOG_TAG, "Successfully created file in Google Drive");
        }
    });
}

So here's my issue:

I can save the database to Google Drive, but how do I update the Google Drive version with any changes made locally?

For example, I can delete 3 rows from table A and then add 5 rows to table B locally (to the device's SQLite database), but how do I update the Google Drive version with this change?

I have considered deleting the entire Drive file and re-uploading it, but this would result in a different DriveId for that file, to my understanding.

I'm wondering if I would be able to take advantage of the API's modification handling (explained here), where changes are queued for upload if the device has no internet connection.

1

There are 1 best solutions below

3
On BEST ANSWER

According to this answer, the android API for Google Drive already handles the difference calculation for you. So there is no need to do anything complicated, just use the API as if you were completely rewriting that file.

You might also want to take advantage of the APIs transparent offline syncing functionality.