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);
}
};
Related
I am trying to upload files from the internal storage to a user's Google Drive through the API, to back up user data. What I have done so far works on occasion, but it often creates two or three copies of the files as well and I have no idea why.
As far as I know there is no way to upload entire folders and their contents at once, so instead I first create an empty backup folder, then iterate through the user data and copy the files into the folder one by one.
Note: for now I am using the root folder of the Google Drive instead of the App folder that is dedicated for this purpose. This makes it easier to see the files that are created. When everything works, I'll swap the root folder with the app folder.
Creating a new backup first starts with looking for existing backup folders and deleting them if there are any:
private void createNewBackup(Context context, GoogleSignInAccount googleSignInAccount) {
// Create query to search for existing backup folder
Query query = new Query.Builder()
.addFilter(Filters.eq(SearchableField.TITLE, "Backup"))
.build();
Drive.getDriveResourceClient(context, googleSignInAccount).query(query)
// if a backup folder is found, delete it:
.addOnSuccessListener(new OnSuccessListener<MetadataBuffer>() {
#Override
public void onSuccess(MetadataBuffer metadata) {
// there are never 2 backup folders, so it is always the first set of metadata, hence get(0).
DriveFolder backupfolder = metadata.get(0).getDriveId().asDriveFolder();
Drive.getDriveResourceClient(context, googleSignInAccount).delete(backupfolder)
// when the folder is deleted, create a new backup folder
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
createBackupFolder(context, googleSignInAccount);
}
});
}
})
// if no backup folder is found, create a new backup folder
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
createBackupFolder(context, googleSignInAccount);
}
});
}
When the old backup folder is deleted, this is the code that creates the new backup folder
public void createBackupFolder(Context context, GoogleSignInAccount googleSignInAccount) {
// Get the root folder of the drive:
Drive.getDriveResourceClient(context, googleSignInAccount).getRootFolder().addOnSuccessListener(new OnSuccessListener<DriveFolder>() {
#Override
public void onSuccess(DriveFolder driveFolder) {
// create backup folder in root folder:
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("Backup")
.setMimeType(DriveFolder.MIME_TYPE)
.setStarred(true)
.build();
Log.d("Test", "Creating backup folder");
Drive.getDriveResourceClient(context, googleSignInAccount).createFolder(driveFolder, changeSet)
.addOnSuccessListener(new OnSuccessListener<DriveFolder>() {
#Override
public void onSuccess(DriveFolder backupFolder) {
Log.d("Test", "Created backup folder");
writeDataToBackupFolder(context, googleSignInAccount, backupFolder);
}
})
// if the folder couldn't be created:
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("Test", "failed to create backup folder");
}
});
}
});}
And then finally the files in the subfolders "premium" and "shifts" are copied to the backup folder on the drive with the writeDataToBackupFolder method:
public void writeDataToBackupFolder(Context context, GoogleSignInAccount googleSignInAccount, DriveFolder backupFolder) {
// iterate over the files in the subfolders
File[] subfolders = new File(context.getFilesDir().getPath()).listFiles();
for (int i = 0; i < subfolders.length; i++) {
if (subfolders[i].getName().equals("premium") || subfolders[i].getName().equals("shifts")) {
File[] filesInSubfolder = new File(subfolders[i].getPath()).listFiles();
for (int j = 0; j < filesInSubfolder.length; j++) {
// for every file, get its contents and write them to a file and upload it to the drive
String fileName = subfolders[i].getName() + "/" + filesInSubfolder[j].getName();
List<String> content = readFromFile(context, fileName);
Drive.getDriveResourceClient(context, googleSignInAccount).createContents().addOnSuccessListener(new OnSuccessListener<DriveContents>() {
#Override
public void onSuccess(DriveContents driveContents) {
OutputStream outputStream = driveContents.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
try {
for (int k = 0; k < content.size(); k++) {
writer.write(content.get(k));
writer.write("\n");
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(fileName)
.setMimeType("text/plain")
.setStarred(true)
.build();
Log.d("Test", "Creating file "+fileName);
Drive.getDriveResourceClient(context, googleSignInAccount).createFile(backupFolder, changeSet, driveContents)
.addOnSuccessListener(new OnSuccessListener<DriveFile>() {
#Override
public void onSuccess(DriveFile driveFile) {
Log.d("Test", "Created file "+fileName);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("Test", "Failed to create "+fileName);
}
});
}
});
}
}
}}
It deletes the old backup folder and creates the new backup folder just fine and it always copies the files to the Google Drive. They are all text files, but sometimes (more often than not) duplicates are created. For example, the folder 'premium' has files 1.txt, 2.txt, 3.txt, etc., but on the drive they would appear as premium/1.txt, premium/1.txt, premium/2.txt, premium/3.txt, premium/3.txt, and I have no idea why. Which files become duplicates is random and can change everytime I call the createNewBackup method. As you can see in the code, I register created files in the log, but no duplicates show up there, only on the Drive. Is it a problem with the Google Drive API? Am I calling everything too quickly? How could I stop this from happening?
Never mind. Even though the duplicates problem has not been solved yet, it does not really pose a problem, because when I restore a backup, all the user files are overwritten, so it does not matter if that happens once or twice extra.
I have saved a bunch of videos in an internal storage folder. Afterwards, I want the user to be able to select one of these videos in this specific folder. I tried using ACTION_GET_CONTENT in an attempt to let another app do this for me, without any success, as it just opens up a file browser in some other directory.
What I have now is:
public static File getOwnVideosDirectory(Context context) {
String ownVideosDirPath =
context.getFilesDir().getAbsolutePath() + File.separator + "OwnVideos";
File ownVideosDir = new File(ownVideosDirPath);
if (!ownVideosDir.exists()) {
ownVideosDir.mkdirs();
}
return ownVideosDir;
}
private void dispatchExistingVideo() {
Log.d(LOG_TAG, "> dispatchExistingVideo");
Intent videoPicker = new Intent(Intent.ACTION_GET_CONTENT);
File ownVideosDir = Utility.getOwnVideosDirectory(getContext());
videoPicker.setDataAndType(Uri.fromFile(ownVideosDir), "video/*");
if (videoPicker.resolveActivity(getContext().getPackageManager()) != null) {
startActivityForResult(videoPicker, REQUEST_EXISTING_VIDEO);
}
}
So I'm wondering, am I doing something wrong or is it impossible like this. If impossible: is there any library,... available that would allow me to do what I want, or any direction on how I could implement this myself as a last resort?
Thanks in advance
Please take a look at that library - Material File Picker
It allows to show a dialog with the specified path using .withPath(Utility.getOwnVideosDirectory(getContext()).getAbsolutePath()).
The whole creation code:
new MaterialFilePicker()
.withActivity(this)
.withRequestCode(1)
.withFilter(Pattern.compile(".*\\.txt$")) // Filtering files and directories by file name using regexp
.withFilterDirectories(true) // Set directories filterable (false by default)
.withHiddenFiles(true) // Show hidden files and folders
.withPath(Utility.getOwnVideosDirectory(getContext()).getAbsolutePath())
.start();
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
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.
I develop an app which collects some data from internet. Then save it to a temporary folder. To build this app I need to create and access a folder ( just for the purpose of app, not for the user). How can I do it?
this code is to create folder:
File direct = new File(Environment.getExternalStorageDirectory() + "/New Folder");
if(!direct.exists())
{
(direct.mkdir()) //directory is created;
}
try it may help you
File mFile;
onCreate()
mFile= new File(Environment.getExternalStorageDirectory()+"/temp/";
mFile.mkdir();
onDestroy();
mFile.delete();
try out this...
private void makeFolder(){
File root = new File(Environment.getExternalStorageDirectory()
+ File.separator + getString(R.string.folder_name));
boolean mainfolderexist = root.exists();
if (!mainfolderexist) {
try {
if (Environment.getExternalStorageDirectory().canWrite()) {
root.mkdirs();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
All The best
You should really check this other SO answer: https://stackoverflow.com/a/6485850/65716
Aside from the fact that you have to completely manage your use of the space, etc, caching on external storage requires more permission for your app.
See http://developer.android.com/reference/android/content/Context.html#getCacheDir()
"Apps require no extra permissions to read or write to the returned path, since this path lives in their private storage."
For app use only, I would recommend to use Context.getDir() for retrieving the directory if the files is used by our app only and don`t want to be visible to users by file browsers.
// No need to check if exist, created automatically.
File tempRoot = context.getDir("temp", Context.MODE_PRIVATE);
// do something