How to save app data on Google Drive? - android

I want to use Google Drive as server to store app data. So, I have written all the data to a file and then upload that file to Drive.
Code:
try {
// outputStream.write(bitmapStream.toByteArray());
outputStream.write(text.getBytes());
}
catch (IOException e1) {
Log.i(TAG, "Unable to write file contents.");
}
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setMimeType("text/txt").setTitle("testfile.txt").build();
IntentSender intentSender = Drive.DriveApi
.newCreateFileActivityBuilder()
.setInitialMetadata(metadataChangeSet)
.setInitialDriveContents(result.getDriveContents())
.build(mGoogleApiClient);
try {
startIntentSenderForResult(intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
}
catch (SendIntentException e) {
Log.i(TAG, "Failed to launch file chooser.");
}
Is it possible that whenever the device is connected to internet, the app data will get synchronized with data available on Google Drive?
I read about Google Drive API, but was unable to understand:
1) how will the synchronization happen?
2) can we synchronize with the file stored in App folder of Google Drive?
3) do I need to write the file to Google Drive or I'll have to save the file to some container and Google will update itself when connected to internet(as in ios)?
Please guide me.
EDIT
Before creating a file in drive, I have done a check.
Query query = new Query.Builder()
.addFilter(Filters.eq(SearchableField.MIME_TYPE, "text/plain"))
.addFilter(Filters.eq(SearchableField.TITLE, "appdata.txt")).build();
Drive.DriveApi.query(getGoogleApiClient(), query).setResultCallback(
metadataCallback);
final private ResultCallback<MetadataBufferResult> metadataCallback = new ResultCallback<MetadataBufferResult>() {
#Override
public void onResult(MetadataBufferResult result) {
metadata = result.getMetadataBuffer();
for (int i = 0; i < metadata.getCount(); i++) {
DriveFile file = Drive.DriveApi.getFile(getGoogleApiClient(),
metadata.get(i).getDriveId());
file.trash(getGoogleApiClient());
}
Is it a wrong way to proceed?

newCreateFileActivityBuilder() will start an activity to let the user choose a location in their Drive to create the file.
If you want to use the App folder you'll need to follow the instructions in https://developers.google.com/drive/android/appfolder

Related

Android - Google Drive SDK - Open file

I am used to opening my files in my apps using the next code:
public void openFile(#NonNull String uri) {
checkNotNull(uri);
File file = new File(uri);
String dataType = null;
if (ContentTypeUtils.isPdf(uri)) dataType = "application/pdf";
else if (ContentTypeUtils.isImage(uri)) dataType = "image/*";
if (file.exists() && dataType != null) {
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(Uri.fromFile(file), dataType);
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
Intent intent = Intent.createChooser(target, "Open file");
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "There is a problem when opening the file :(");
}
} else {
Toast.makeText(getContext(), "Invalido", Toast.LENGTH_LONG).show();
}
}
I had always used static files so this was enough, but now I am using the Google Drive SDK for Android. I possess the driveId of the file I want to open but the problem is I cannot find a clean way to open the file contents I obtain by doing this:
Drive.DriveApi.fetchDriveId(mGoogleApiClient, documentFile.getDriveId())
.setResultCallback(driveIdResult -> {
PendingResult<DriveApi.DriveContentsResult> open =
driveIdResult.getDriveId().asDriveFile().open(
mGoogleApiClient,
DriveFile.MODE_READ_ONLY,
null);
open.setResultCallback(result -> {
DriveContents contents = result.getDriveContents();
InputStream inputStream = contents.getInputStream();
// I know I can get the input stream, and use it to write a new file.
});
});
So the only thing that comes to my mind is creating a static route to create a file every time I have to open it, and erasing it every time I have to open a new file.
What I have understood up until now is that the Google Drive API for Android already saves an instance of the file so what I have in mind sounds unnecessary, I would like to know if there is a better way to achieve this. Is there a way I can open the file and do something similar to what I do with the Intent.ACTION_VIEW in a cleaner way?
Thanks in advance.
Well since it seems this will not be answered I will post what I did. All I did was create a temp file where I put my contents to be read. I still don't know if it was the best choice so this question will still be opened for a better answer.
open.setResultCallback(result -> {
DriveContents contents = result.getDriveContents();
InputStream inputStream = contents.getInputStream();
writeTempFile(inputStream);
});
And here the implementation of the `writeTempFile`:
private synchronized File writeTempFile(#NonNull InputStream inputStream) {
checkNotNull(inputStream);
File filePath = new File(mActivity.getFilesDir(), "TempFiles");
if (!filePath.exists()) filePath.mkdirs();
File file = new File(filePath, TEMP_FILE);
try {
OutputStream outputStream = new FileOutputStream(file);
IOUtils.copyLarge(inputStream, outputStream);
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
return file;
}

Google Drive Sqlite db file upload from android app

I'm able to upload database to Drive using the following post.
Drive API - Download/upload sql database
But I'm not able to access it directly offline without using app.
Aim: Use the db file further in different application so I want it to be in a usable format whenever I download the content directly from google drive.
I am using MODE_WRITE_ONLY to upload the file to drive from within app
mfile.open(api, DriveFile.MODE_WRITE_ONLY, new DriveFile.DownloadProgressListener()
And mime type as this String mimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType("db");
My db size is 44kb when I access from external sd card on phone, however it shows 40kb when I see on drive. Please suggest what can I do to make it readable so that I can directly open it in an sqlite browser because when I open it shows "File not recognized".
Do I have to make changes in the WRITE only part or mime type for db file. Please suggest what could be the problem.
Since I've successfully tested an SQLite file upload to GooDrive, I can post a piece of code that does it:
Let's assume, there is a SQLite file on your android device:
java.io.File dbFile = Context.getDatabasePath([YOUR_DB_NAME])
Then you can call this method:
upload("temp.db", dbFile, "application/x-sqlite3")
com.google.android.gms.common.api.GoogleApiClient GAC;
///...
void upload(final String titl, final File file, final String mime) {
if (GAC != null && GAC.isConnected() && titl != null && file != null) try {
Drive.DriveApi.newDriveContents(GAC).setResultCallback(new ResultCallback<DriveContentsResult>() {
#Override
public void onResult(#NonNull DriveContentsResult contRslt) {
if (contRslt.getStatus().isSuccess()){
DriveContents cont = contRslt.getDriveContents();
if (cont != null && file2Os(cont.getOutputStream(), file)) {
MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(mime).build();
Drive.DriveApi.getRootFolder(GAC).createFile(GAC, meta, cont).setResultCallback(
new ResultCallback<DriveFileResult>() {
#Override
public void onResult(#NonNull DriveFileResult fileRslt) {
if (fileRslt.getStatus().isSuccess()) {
// fileRslt.getDriveFile(); BINGO !!!
}
}
}
);
}
}
}
});
} catch (Exception e) { e.printStackTrace(); }
}
static boolean file2Os(OutputStream os, File file) {
boolean bOK = false;
InputStream is = null;
if (file != null && os != null) try {
byte[] buf = new byte[4096];
is = new FileInputStream(file);
int c;
while ((c = is.read(buf, 0, buf.length)) > 0)
os.write(buf, 0, c);
bOK = true;
} catch (Exception e) {e.printStackTrace();}
finally {
try {
os.flush(); os.close();
if (is != null )is.close();
} catch (Exception e) {e.printStackTrace();}
}
return bOK;
}
To create a "temp.db" SQLite file in the root of your GooDrive.
You can certainly supply a different parent folder (instead of Drive.DriveApi.getRootFolder(GAC)) if you need to place your file in a different location.

How should I define the callbacks in saveFileToDrive() of the Google Drive API example. + Retrieve the URL of an uploaded image

I manager to run the example provided by Google for uploading a file directly to your Drive via an android App. (https://github.com/googledrive/android-quickstart ) I have been reading the documentation for how to get a call-back when the photo is uploaded but I haven't found how to connect it with the creation of the object which handles everything for the upload.
My main target is to retrieve the public URL of the uploaded picture
// Create the initial metadata - MIME type and title.
// Note that the user will be able to change the title later.
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setMimeType("image/jpeg").setTitle("Android Photo.png").build();
// Create an intent for the file chooser, and start it.
IntentSender intentSender = Drive.DriveApi
.newCreateFileActivityBuilder()
.setInitialMetadata(metadataChangeSet)
.setInitialDriveContents(result.getDriveContents())
.build(mGoogleApiClient);
try {
// (???????) how I should handle the callback for the metadata here
startIntentSenderForResult(
intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
} catch (SendIntentException e) {
Log.i(TAG, "Failed to launch file chooser.");
}
If you want to get notified when the file is created on the server, I think you will need to create file programmatically (instead of using the CreateFileActivityBuilder) and request for Completion Events. See https://developers.google.com/drive/android/completion for deatils.

Uploading and downloading multiple files back to back - Android Google Drive api

In my app, I need to upload multiple files (1 sqlite db file and multiple image files) for backup purpose to user's google drive.
I am using android google drive api, but not sure, how to do back to back file uploads and then later on downloads like this.
The db file obviously comes from /data/data//databases kind of directory whereas images are stored in pictures directory. I need to grab all of these one by one and upload to drive.
Also, I have seen that if a given file (with the same title) already exists, even then, a new file with the same title is created on drive (obviously has diff DriveId but same title). I would like to check, if the file exists and only upload if it doesn't, else skip that file.
Please help.. I have been trying to refer the android demos on github by google, but have been only able to do bits and pieces using that.
File title is not unique in Drive API. However you can save the IDs of your newly created files in your app's local storage, so you can check against the IDs in the Drive side when you want to upload the file again.
You can use the CreateFileActvity.java from the Google Drive Demo GitHub page. It will return a file ID after you create a file successfully, so you can store the ID in your local storage.
Sample code from CreateFileActivity.java:
final private ResultCallback<DriveFileResult> fileCallback = new
ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the file");
return;
}
showMessage("Created a file with content: " + result.getDriveFile().getDriveId());
}
};
Just in case somebody is looking how to upload multiple files to Drive, here is solution that worked for me:
for(String fileName: fileNameArrayList){backupImage(fileName);}
private void backupImage(String fileName) {
Drive.DriveApi.newDriveContents(mGoogleApiClient).setResultCallback(
new BackupImagesContentsCallback(mContext, mGoogleApiClient, fileName));
}
Backup callback:
public class BackupImagesContentsCallback implements ResultCallback<DriveApi.DriveContentsResult> {
#Override
public void onResult(#NonNull DriveApi.DriveContentsResult driveContentsResult) {
if (!driveContentsResult.getStatus().isSuccess()) {
Log.v(TAG, "Error while trying to backup images");
return;
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(mFileName) // Google Drive File name
.setMimeType("image/jpeg")
.setStarred(true).build();
Drive.DriveApi.getAppFolder(mGoogleApiClient)
.createFile(mGoogleApiClient, changeSet, driveContentsResult.getDriveContents())
.setResultCallback(backupImageFileCallback);
}
final private ResultCallback<DriveFolder.DriveFileResult> backupImageFileCallback = new ResultCallback<DriveFolder.DriveFileResult>() {
#Override
public void onResult(#NonNull DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
Log.v(TAG, "Error while trying to backup images");
return;
}
DriveFile mImageFile;
mImageFile = result.getDriveFile();
mImageId = result.getDriveFile().getDriveId();
mImageFile.open(mGoogleApiClient, DriveFile.MODE_WRITE_ONLY, (bytesDownloaded, bytesExpected) -> {
}).setResultCallback(backupImagesContentsOpenedCallback);
}
};
final private ResultCallback<DriveApi.DriveContentsResult> backupImagesContentsOpenedCallback =
new ResultCallback<DriveApi.DriveContentsResult>() {
#Override
public void onResult(#NonNull DriveApi.DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
return;
}
DriveContents contents = result.getDriveContents();
BufferedOutputStream bos = new BufferedOutputStream(contents.getOutputStream());
byte[] buffer = new byte[1024];
int n;
File imageDirectory = new File(mContext.getFilesDir(),
Constants.IMAGE_DIRECTORY_NAME);
try {
FileInputStream is = new FileInputStream(new File(imageDirectory,
mFileName));
BufferedInputStream bis = new BufferedInputStream(is);
while ((n = bis.read(buffer)) > 0) {
bos.write(buffer, 0, n);
}
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
contents.commit(mGoogleApiClient, null);
}
};
}
This is not perfect solution, just a working code.

Synchronize user data to Google account

I want to synchronize my app's data between several Android devices. To make it simple for user, I want to somehow use Google account.
So my question is - does Google provide some free cloud storage? My app's data shouldn't be bigger than 50MB. If not, is there any free alternative or workaround (for example saving data to user's Drive)?
It sounds like you are looking for the App Folder. This folder belongs to the user's Google Drive, but is hidden.
You can save to this folder using something like this (taken from the documentation):
final private ResultCallback<DriveContentsResult> contentsCallback =
new ResultCallback<DriveContentsResult>() {
#Override
public void onResult(DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create new file contents");
return;
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("appconfig.txt")
.setMimeType("text/plain")
.build();
Drive.DriveApi.getAppFolder(getGoogleApiClient())
.createFile(getGoogleApiClient(), changeSet, result.getDriveContents())
.setResultCallback(fileCallback);
}
};

Categories

Resources