Google drive to back up and restore database and shared preferences of Android application

sb_269 picture sb_269 · Feb 24, 2014 · Viewed 10.5k times · Source

I need to create a backup of my application which would include creating a backup of 2 databases and also the shared preferences using Google drive API. I was able to get the authentication done for the app and also create a new folder in Drive using the following code:

public class MainActivity2 extends BaseDemoActivity {

DriveId folderId;

@Override
public void onConnected(Bundle connectionHint) {
    super.onConnected(connectionHint);
    MetadataChangeSet changeSet = new MetadataChangeSet.Builder().setTitle("New folder").build();
    Drive.DriveApi.getRootFolder(getGoogleApiClient())
    .createFolder(getGoogleApiClient(), changeSet)
    .setResultCallback(folderCreatedCallback);
}

ResultCallback<DriveFolderResult> folderCreatedCallback = new ResultCallback<DriveFolderResult>() {
    @Override
    public void onResult(DriveFolderResult result) {
        if (!result.getStatus().isSuccess()) {
            showMessage("Error while trying to create the folder");
            return;
        }
        folderId = result.getDriveFolder().getDriveId();
        showMessage("Created a folder: " + result.getDriveFolder().getDriveId());
    }
};
}

I insert the file using

fileUri = Uri.fromFile(new java.io.File(Environment.getDataDirectory().getPath()
                        + "/data/com.example.myapp/databases/mydb.db"));

                java.io.File fileContent = new java.io.File(fileUri.getPath());

                FileContent mediaContent = new FileContent(getMimeType("db"), fileContent);
                File body = new com.google.api.services.drive.model.File();
                body.setTitle(fileContent.getName());
                body.setMimeType(getMimeType("db"));

                File file = service.files().insert(body, mediaContent).execute();

This works fine.

I restore using

AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
        @Override
        protected String doInBackground(Void... params) {
            try {

                fileId="0B7Gol85MIbTJLTRxX1hZdDJjaEE";
                credential.setSelectedAccountName("the same drive account");
                service = getDriveService(credential);
                if (fileId != null) {
                    File file = service.files().get(fileId).execute();
                    downloadFile(service, file);
                }
                else return null;
            } catch (Throwable tr) {

            }
            return fileId;
        }

private void downloadFile(Drive service, File file) {
    InputStream mInput=null;
    FileOutputStream mOutput=null;
    if (file.getDownloadUrl() != null && file.getDownloadUrl().length() > 0) {
        try {
            HttpResponse resp = service.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl())).execute();

            mInput = resp.getContent();
            String outFileName = "file://"+Environment.getDataDirectory().getPath() + "/data/com.example.myapp/databases/InvoiceDataBase.db";
            Log.e("com.example.myapp", "getDatabasePath="+ getDatabasePath("")); 
            Log.e("com.example.myapp", "outFileName="+outFileName);
//              String outFileName = "../databases/" + "Quickpay.db";
            mOutput = new FileOutputStream(outFileName);
            byte[] mBuffer = new byte[1024];
            int mLength;
            while ((mLength = mInput.read(mBuffer)) > 0) {
                mOutput.write(mBuffer, 0, mLength);
            }
            mOutput.flush();
        } catch (IOException e) {
            // An error occurred.
            e.printStackTrace();
            // return null;
        }
        finally {
            try {
                    //Close the streams
                    if(mOutput != null){
                        mOutput.close();
                    }
                    if(mInput != null){
                        mInput.close();
                    }
            } catch (IOException e) {
                    Log.e("com.example.myapp", "failed to close databases");
            }
    }
    } else {
        // The file doesn't have any content stored on Drive.
        // return null;
        Log.e("com.example.myapp", "No content on Drive");
    }
}

When I backup again after a restore, I get

" /data/data/com.example.myapp/databases/mydb: open failed: ENOENT (No such file or directory)"

Now I need to upload my ".db" file in this folder and also upload shared preferences as an XML file. So my questions are:

  1. How do I upload my application database to this folder? solved.
  2. How do i RESTORE the database file back to the application?
  3. Can the user have full access to the files when he logs into his Google Drive account? Is there any other way to backup and restore ? I cannot use "Android Backup services" as that cannot be a forced backup.

TIA.

Answer

seanpj picture seanpj · Feb 25, 2014

The answer applies to both your SQLite dbase file and to your Shared Prefs.

First, turn anything you want to save into byte[] buffer. Here is an example for SQLite dbase:

  byte[] getDBBytes() {
   Context ctx = getApplicationContext();
    byte[] out = null;
    try {
      File from = ctx.getDatabasePath(ctx.getString(R.string.app_name));
      if (from.exists()) 
        out = strm2Bytes(new FileInputStream(from));
    } catch (Exception e) {}
    return out;
  }

(strm2Bytes() is a primitive that copies file to byte[] buffer).

Then use the DEMO ('Create a file in a folder', 'Edit contents') to put the data in a Drive file. Keep these two processes separate, since you'll be only updating contents if the file exists.

To get it back, start with 'Retrieve contents' just replace the BufferBuilder, StringBuilder with your own InputStream reader that produces the data you place back into your SQLite file. Something similar to :

  public static void setDB(InputStream is) {
    Context ctx = getApplicationContext();
    try {
      File to = ctx.getDatabasePath(ctx.getString(R.string.app_name));
      if (to.exists()) 
        to.delete(); 
      strm2File(is, to);
    } catch (Exception e) {}
  }

(again, strm2FIle() is a primitive that copies stream into a file)

Sorry, I'm giving you only high level ideas, I tried to pull functional snippets from my code, but it is so tangled that I would have to copy half of my stuff here.