Check if folder exists, and create it if not - android

I notice that there is not a conclusive answer to this on SO, so I am looking for a canonical answer to the question "How to check if a folder exists, and create it if it does not, using the Google Drive Android API?". Ideally showing examples of both the asynchronous approach using ResultCallback, and the synchronous approach using .await().
P.S. I am aware of this question with the same title, but the accepted answer is focussing on the known bug of lag on isTrashed(), and is not clear about at which point in the code you actually know the folder exists. Other answers seem out of date.

This question - while focussing on the laggy deletion status issue - does provide a pattern for testing if a folder exists.
Using an asynchronous callback:
Query query = new Query.Builder()
.addFilter(Filters.and(Filters.eq(
SearchableField.TITLE, "MyFolder"),
Filters.eq(SearchableField.TRASHED, false)))
.build();
Drive.DriveApi.query(getGoogleApiClient(), query)
.setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
#Override
public void onResult(DriveApi.MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Cannot create folder in the root.");
} else {
boolean isFound = false;
for(Metadata m : result.getMetadataBuffer()) {
if (m.getTitle().equals("MyFolder")) {
showMessage("Folder exists");
isFound = true;
break;
}
}
if(!isFound) {
showMessage("Folder not found; creating it.");
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("MyFolder")
.build();
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFolder(getGoogleApiClient(), changeSet)
.setResultCallback(new ResultCallback<DriveFolder.DriveFolderResult>() {
#Override
public void onResult(DriveFolder.DriveFolderResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the folder");
} else {
showMessage("Created a folder");
}
}
});
}
}
}
});
Using a synchronous .await()
Query query = new Query.Builder()
.addFilter(Filters.and(Filters.eq(
SearchableField.TITLE, "MyFolder"),
Filters.eq(SearchableField.TRASHED, false)))
.build();
DriveApi.MetadataBufferResult result = Drive.DriveApi.query(getGoogleApiClient(), query)
.await();
if (!result.getStatus().isSuccess()) {
showMessage("Cannot create folder in the root.");
} else {
boolean isFound = false;
for(Metadata m : result.getMetadataBuffer()) {
if (m.getTitle().equals("MyFolder")) {
showMessage("Folder exists");
isFound = true;
break;
}
}
if(!isFound) {
showMessage("Folder not found; creating it.");
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("MyFolder")
.build();
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFolder(googleApiClient, changeSet).await();
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the folder");
} else {
showMessage("Created a folder");
}
}

Your quote:
... is not clear about at which point in the code you actually know the
folder exists
In the REST Api, you wait for a response from the 'ecexute()' method. Straightforward, and you can time the response out. You get folder/file id (ResourceId) and you know it exists in the Drive.
In GDAA, look at this answer. Again, when you get completion notification with a valid ResourceId, you know that the folder/file is 'up-there'.
Good Luck

Related

Can't get uploading or downloading progress in Google Drive Android SDK

I am working on application where i take a backup of some user's data to Google drive and in a later time i restore them.
The problem is that the files created and restored with no problems except that i can't see any progress, if i am uploading a large file it keep doing that in the background and can't notify the user that there is some operation happening in the background.
here is a snippet from the method i am using
Drive.DriveApi.newDriveContents(client)
.setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() {
#Override
public void onResult(#NonNull DriveApi.DriveContentsResult driveContentsResult) {
final DriveContents driveContents = driveContentsResult.getDriveContents();
File file = new File(filesToUpload.get(0).getURI());
// write content to DriveContents
OutputStream outputStream = driveContentsResult.getDriveContents().getOutputStream();
try {
outputStream.write(FileManagerUtils.getBytes(file));
} catch (IOException e) {
e.printStackTrace();
NotificationManger.dismissUploadingNotification();
NotificationManger.showSucessNotification(getApplicationContext(), R.string.notification_uploading_success);
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(obj.getFileName())
.build();
DriveId folderID = null;
// create a file on root folder
Drive.DriveApi.getFolder(client, folderID)
.createFile(client, changeSet, driveContents)
.setResultCallback(new ResultCallbacks<DriveFolder.DriveFileResult>() {
#Override
public void onSuccess(#NonNull DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
Log.d(TAG, "Error while trying to create the file");
return;
}
Log.d(TAG, "Created a file with content: " + result.getDriveFile().getDriveId());
if (filesToUpload.size() > 0) {
filesToUpload.remove(0);
backup();
}
}
#Override
public void onFailure(#NonNull Status status) {
// show error
}
});
}
});
The problem is that if i'am uploading 3 files, the
Log.d(TAG,Log.d(TAG, "Created a file with content: " + result.getDriveFile().getDriveId()); is called very quickly after each others, and the actual files keep uploading in the background.
So can anyone tell me how to get real status of the uploading files in the background?
You need to add progress listeners which will listen to download or upload progress.
For uploading, you can use the MediaHttpUploaderProgressListener implementation given in Implementation details
public static class MyUploadProgressListener implements MediaHttpUploaderProgressListener {
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
System.out.println("Initiation Started");
break;
case INITIATION_COMPLETE:
System.out.println("Initiation Completed");
break;
case MEDIA_IN_PROGRESS:
System.out.println("Upload in progress");
System.out.println("Upload percentage: " + uploader.getProgress());
break;
case MEDIA_COMPLETE:
System.out.println("Upload Completed!");
break;
}
}
}
For downloading, you can attach a DownloadProgressListener to inform users of the download progress in a ProgressDialog. As shown in Opening the file contents specifically in listening to the download progress, open the file contents with a DownloadProgressListener.
file.open(mGoogleClientApi, DriveFile.MODE_READ_ONLY, new DownloadProgressListener() {
#Override
public void onProgress(long bytesDownloaded, long bytesExpected) {
// display the progress
}
});
Solutions given in these SO post - Check progress for Upload & Download (Google Drive API for Android or Java) and How to show uploading to Google Drive progress in my android App? might help too.

How can i get access token for google drive rest api call?

I want to pass access token for get the list of files from google drive.
What I have done :
Register app to Google dev console.
I have client id based on OAuth 2.0
What i want to do :
Call HttpCall for https://www.googleapis.com/drive/v2/files
I want to set access token in header like this :
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost post = new HttpPost("https://www.googleapis.com/drive/v2/files");
post.addHeader("Content-Type", "application/json");
post.addHeader("Authorization","Bearer XXXXXXXXXXXXXXXXXXXXXXXXX");
How can I get access token for Authorization by using OAuth2.0 Client Id
You should look at the Drive for Android documentation.
That makes the whole job a hell lot simpler. For example
Create a file:
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
IntentSender intentSender = Drive.DriveApi
.newOpenFileActivityBuilder()
.setMimeType(new String[] { "text/plain", "text/html" })
.build(getGoogleApiClient());
try {
startIntentSenderForResult(
intentSender, REQUEST_CODE_OPENER, null, 0, 0, 0);
} catch (SendIntentException e) {
Log.w(TAG, "Unable to send intent", e);
}
}
List files in a folder
To list all the files in a folder
/**
* An activity that illustrates how to list files in a folder.
*/
public class ListFilesInFolderActivity extends BaseDemoActivity implements
ResultCallback {
private static DriveId sFolderId = DriveId.decodeFromString("DriveId:0B2EEtIjPUdX6MERsWlYxN3J6RU0");
private ListView mResultsListView;
private ResultsAdapter mResultsAdapter;
#Override
public void onConnected(Bundle connectionHint) {
super.onCreate(connectionHint);
setContentView(R.layout.activity_listfiles);
mResultsListView = (ListView) findViewById(R.id.listViewResults);
mResultsAdapter = new ResultsAdapter(this);
mResultsListView.setAdapter(mResultsAdapter);
DriveFolder folder = Drive.DriveApi.getFolder(getGoogleApiClient(), sFolderId);
folder.listChildren(getGoogleApiClient()).setResultCallback(childrenRetrievedCallback);
}
ResultCallback<MetadataBufferResult> childrenRetrievedCallback = new
ResultCallback<MetadataBufferResult>() {
#Override
public void onResult(MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Problem while retrieving files");
return;
}
mResultsAdapter.clear();
mResultsAdapter.append(result.getMetadataBuffer());
showMessage("Successfully listed files.");
}
}
}
Query for a file
To query for a file say "HelloWorld.java":
Query query = new Query.Builder()
.addFilter(Filters.eq(SearchableField.TITLE, "HelloWorld.java"))
.build();
Drive.DriveApi.query(googleApiClient, query);
For more details on querying please look at this part of documentation.
If you are making calls with scopes that are available in GDAA then that would be your best bet.
Otherwise you could use the Google Drive Java client library. In this instance you can use the GoogleAccountCredential for Auth, which will contain the access token, however you will not need to use it directly. See an example here.

Upload file to google drive fails

Upload a file from android to Google drive is failing. Seems like the createFile is not working even the status returned is success. The actual status error code and message that I get from the Google API is error code 8, error message: Provided DriveId is not valid
I also cannot find the file on the related user Google drive (using web interface for the Google drive).
I have also tried using Drive.DriveApi.fetchDriveId(m_api, m_file.getDriveId().toString()) in the create file callback (instead of calling to openContents), and it has also failed.
Related code is below, any help would be appreciated!
public void createFile() {
Drive.DriveApi.newContents(m_api).setResultCallback(contentsCallback);
}
final private ResultCallback<ContentsResult> contentsCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (handleError(result.getStatus())) {
return;
}
Contents contents = result.getContents();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("aaab").setMimeType("text/plain")
.setStarred(true).build();
// create a file on root folder
Drive.DriveApi.getRootFolder(m_api)
.createFile(m_api, changeSet, contents)
.setResultCallback(fileCallback);
}
};
final private ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (handleError(result.getStatus())) {
return;
}
m_file = result.getDriveFile();
m_file.openContents(m_api, DriveFile.MODE_READ_ONLY, null)
.setResultCallback(updateCallback);
}
};
final private ResultCallback<ContentsResult> updateCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
// Getting error here
if (handleError(result.getStatus())) {
return;
}
...
OK problem is that new file is not created.
for creating new file on GoogleDrive i use this code and work fine, problem is in your googleapiclient or i don't know where and how you call your funct createFile(). You must call in onConnected metode.
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
// create new contents resource
Drive.DriveApi.newContents(m_api)
.setResultCallback(contentsCallback);
}
final private ResultCallback<ContentsResult> contentsCallback = new
ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create new file contents");
return;
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("New file")
.setMimeType("text/plain")
.setStarred(true).build();
// create a file on root folder
Drive.DriveApi.getRootFolder(m_api)
.createFile(m_api, changeSet, result.getContents())
.setResultCallback(fileCallback);
}
};
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: " + result.getDriveFile().getDriveId());
}
};
I've fixed it!
It had 2 problems:
First issue is my fault - my android manifest was missing APP_ID for drive services:
<meta-data
android:name="com.google.android.apps.drive.APP_ID"
android:value="#string/app_id" />
Second, the activity was extending predefined Google class: BaseGameActivity.
But this class assumes the I am not using Google drive files. To make it work, I had to add this in my Activity:
mRequestedClients = GameHelper.CLIENT_ALL;
and change Google predefined code. Actually it looks like a bug in google, assuming google APP folder is always used, and not Google drive files. In GameHelper.java, line 298, from:
builder.addScope(Drive.SCOPE_APPFOLDER);
to:
builder.addScope(Drive.SCOPE_FILE);

Google drive lists only 1 file

i am using this code to retrieve the list of files and folders on Android, this returns only one file :(
{
Query query = new Query.Builder().setPageToken(mNextPageToken).build();
Drive.DriveApi.query(getGoogleApiClient(), query).setResultCallback(
metadataBufferCallback);
}
private final ResultCallback<MetadataBufferResult> metadataBufferCallback = new ResultCallback<MetadataBufferResult>() {
#Override
public void onResult(MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Problem while retrieving files");
return;
}
mResultsAdapter.append(result.getMetadataBuffer());
mNextPageToken = result.getMetadataBuffer().getNextPageToken();
}
};
The Android API uses Drive.File scope, which gives your app access to the specific files created by it or explicitly opened with it by the user. The query will return the subset of files you have access to that match the query.

Create or Open native Google documents using GoogleDriveApi

I have been closely following the documentation for the Google Drive Android API and, all works great. I can create new text documents and read them back in using the mime type of text/plain.
What I cannot do is create a native Google "Document" or "Spreadsheet." Actually, I can create them by using the mime type to application/vnd.google-apps.document or application/vnd.google-apps.spreadsheet as per Supported MIME Types documentation.
If, however, I try to write content to these documents, the documents never get uploaded.
If I try to read documents that have content (content I created via a web browser) my openContents call fails.
Again, I can create text/plain documents and write to them, but they are not native Google Documents. I have scowered the documentation and sample files, but nothing describes what I'm looking for.
This seems so basic. Does the new GoogleApiClient not support doing this? What am I missing or doing wrong?
Here is the core code for creating. I have a similar issue when trying to read a application/vnd.google-apps.document but I'm sure the two issues are related. I'll spare the verbosity of "read" code.
private void exportToGDriveFile() {
Drive.DriveApi.newContents(getGoogleApiClient()).setResultCallback(createNewFileCallback);
}
final private ResultCallback<ContentsResult> createNewFileCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (!result.getStatus().isSuccess()) {
writeLog("Error while trying to create new file contents");
return;
}
String fileName = getIncrementedFileName();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(fileName)
.setMimeType("text/plain") // <-- This works! I can write and read back :)
//.setMimeType("application/vnd.google-apps.document") <-- can create if no contents are included.
//.setMimeType("application/vnd.google-apps.spreadsheet")
.setStarred(true)
.build();
writeLog("creating file: " + fileName);
// create a file on root folder
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFile(getGoogleApiClient(), changeSet, result.getContents())
.setResultCallback(afterCreateFileCallback);
}
};
private ResultCallback<DriveFileResult> afterCreateFileCallback = new ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
writeLog("Error while trying to create the file");
return;
}
DriveFile driveFile = result.getDriveFile();
writeLog("Created file " + driveFile.getDriveId());
new WriteFileAsyncTask().execute(driveFile);
}
};
private class WriteFileAsyncTask extends AsyncTask<DriveFile, Void, Boolean> {
#Override
protected Boolean doInBackground(DriveFile... args) {
DriveFile file = args[0];
try {
ContentsResult contentsResult = file.openContents(getGoogleApiClient(), DriveFile.MODE_WRITE_ONLY, null).await();
if (!contentsResult.getStatus().isSuccess()) {
return false;
}
/************************
If I try to write content here, `application/vnd.google-apps.document` files will not upload.
*************************/
String contents = "Hello World";
OutputStream outputStream = contentsResult.getContents().getOutputStream();
outputStream.write(contents.getBytes());
com.google.android.gms.common.api.Status status = file.commitAndCloseContents(
getGoogleApiClient(), contentsResult.getContents()).await();
return status.getStatus().isSuccess();
} catch (IOException e) {
// toast("IOException while appending to the output stream");
}
return false;
}
#Override
protected void onPostExecute(Boolean result) {
if (!result) {
// toast("Error while editing contents");
return;
}
// toast("Successfully uploaded Quizifications!");
}
}
It's not currently possible to read or edit the contents of Google Documents, Spreadsheets or Presentation files. They files are of a special type that don't have standard binary content, so you can't read and write from them in the same way you can from other files.
You can, however, interact with the metadata of existing files.
Sorry for the confusion, we should update the behavior so that its clear that its not possible.
Updating Google Docs with HTML is simple. Just make an api request with html-formatted text in the body (html tag is required) and content-type to be google docs, then your created/updated file will be available to the user as a Google Doc with all the formatting options.
request({
uri: 'https://www.googleapis.com/upload/drive/v2/files/'+fileId,
method: 'PUT',
qs: {
uploadType: 'media'
},
form: '<html> Hello <b>World!</b> </html>',
headers: {
'Content-Type': 'application/vnd.google-apps.document',
'Authorization': 'Bearer ' + access_token
}}, function (error, response, body){
})

Categories

Resources