I am introducing in Google Drive Android Api as docs and examples show.
I created two activities which extend BaseDemoActivity of the example: the first one adds empty files to Drive customizing on each file some CustomProperties, the second one lists from Drive the files added grabbing the owned CustomProperties of each file.
first activity - code which adds files like this:
DriveFolder folder = Drive.DriveApi.getFolder(getGoogleApiClient(),
mFolderDriveId);
CustomPropertyKey customPropertyKeyExample = new CustomPropertyKey(
"custom", CustomPropertyKey.PRIVATE);
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("New empty file")
.setMimeType("text/plain")
.setCustomProperty(customPropertyKeyExample, "xyz")
.build();
folder.createFile(getGoogleApiClient(), changeSet, null)
.setResultCallback(fileCallback);
second activity - code which reads properties like this:
for (Iterator<Metadata> i = result.getMetadataBuffer().iterator(); i
.hasNext();) {
Metadata mChildren = ((Metadata) i.next());
if (!mChildren.isTrashed()) {
Map<CustomPropertyKey, String> mapProperties = mChildren
.getCustomProperties();
if (mapProperties.get(customPropertyKeyExample) == null)
// THIS TEST RETURNS TRUE UNTIL DRIVE SYNC EXECUTES
}
}
}
Them work, but i notice that the second activity, the list activity, must wait a Drive variable sync time to have the CustomProperties available.
Is there a way to get the CustomProperties from an activity immediately after them added by a different activity?
This isn't expected behavior. Custom file properties should be available locally without performing a sync.
I've created a bug on our issue tracker to discuss this further:
https://code.google.com/a/google.com/p/apps-api-issues/issues/detail?id=3848
Could you please respond on the bug, answering the following question:
Can you verify that the title is also changed immediately?
Which example class specifically are you using for your first and second activity?
How are you sharing the DriveId for the folder between the activities?
Are you using DriveFolder#listChildren or another query to get [result] in the second example?
Related
I am sure this is an easy question to answer, but I've been unable to find out how to instancate a DriveId and/or DriveFolder with the Drive resource id using the Google Drive Android API v 12.
I have read the Google Drive Android API documentation and have managed to create a file on my Google Drive from my Android app in the root folder, but now I want to create the file in a specific folder and I'm unsure how to go about this.
A lot of the code I've seen (such as this Stackoverflow answer) uses the deprecated Google DriveApi to get a DriveId from the resource id of the folder.
I have tried to use the DriveId method decodeFromString but when I ran the following code, I get an error saying the DriveId is invalid:
String googleDriveFolderId = "16TwNeDF9_inOK4X5AaGnVMNycNVxxMtd";
DriveFolder projectFolder = DriveId.decodeFromString(googleDriveFolderId).asDriveFolder();
What am I doing wrong?
Create a folder
To create a folder, call DriveResourceClient.createFolder(), passing
in a reference to the parent folder and the metadata object containing
the title and other attributes to set the values for the folder.
The following code sample demonstrates how to create a new folder in
the root folder:
private void createFolder() {
getDriveResourceClient()
.getRootFolder()
.continueWithTask(task -> {
DriveFolder parentFolder = task.getResult();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("New folder")
.setMimeType(DriveFolder.MIME_TYPE)
.setStarred(true)
.build();
return getDriveResourceClient().createFolder(parentFolder, changeSet);
})
.addOnSuccessListener(this,
driveFolder -> {
showMessage(getString(R.string.file_created,
driveFolder.getDriveId().encodeToString()));
finish();
})
.addOnFailureListener(this, e -> {
Log.e(TAG, "Unable to create file", e);
showMessage(getString(R.string.file_create_error));
finish();
});
}
on Success, try calling getDriveId().
What I am trying to do is simply not possible using the Google Drive Android API. I guess this is because the DriveResource does not get a resourceId until it has been uploaded.
See this SO answer which discusses how you aren't able to access any file or folder that isn't created by your app. I tested this and it's true - when I run a Query for a folder that I created manually in my root folder I cannot find it, but when I create a folder with the same name from my app, I get 1 query result.
Also see this SO thread which says you need to use another Google Drive API (they suggested the REST API) to be able to specify a folder programmatically (without using a popup where the user selects a folder). Unfortunately that won't work for me because I am building an offline app - precisely the reason I chose to work with Google Drive.
I ended up making a compromise and working with the root folder - luckily for me my project is to be used with very specific Google accounts so I am able to do this. My code looks something like this:
private void createFile() {
// [START create_file]
final Task<DriveFolder> rootFolderTask = getDriveResourceClient().getRootFolder();
final Task<DriveContents> createContentsTask = getDriveResourceClient().createContents();
Tasks.whenAll(rootFolderTask, createContentsTask)
.continueWithTask(task -> {
DriveFolder parent = rootFolderTask.getResult();
DriveContents contents = createContentsTask.getResult();
OutputStream outputStream = contents.getOutputStream();
try (Writer writer = new OutputStreamWriter(outputStream)) {
writer.write("Hello World!");
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("HelloWorld.txt")
.setMimeType("text/plain")
.setStarred(true)
.build();
return getDriveResourceClient().createFile(parent, changeSet, contents);
})
.addOnSuccessListener(this,
driveFile -> {
showMessage(getString(R.string.file_created,
driveFile.getDriveId().encodeToString()));
finish();
})
.addOnFailureListener(this, e -> {
Log.e(TAG, "Unable to create file", e);
showMessage(getString(R.string.file_create_error));
finish();
});
// [END create_file]
}
If you can't compromise and use the root folder, I would suggest that you create a folder from within your app into the root folder and then save the string representation of the DriveId which you can use the next time you run the code. I haven't yet tested if the folder could be used by another instance of the app running on a different device, but I would hope so (fingers crossed).
Another option is displaying a popup so the user can select the folder manually. See this demo example.
I'm running a query on the Android SDK for Google Drive to check if a directory with a specific name exists or creating it otherwise (directory name is the resource title on Google Drive).
The problem I'm having with the following code is that it never finds my folder and creates a new one every time and I'm not sure why. It successfully finds the directory if the SDK created it itself.
public static final String FOLDER_NAME_CORE = "My Core Folder";
MetadataBuffer meta = Drive.DriveApi.query(mGoogleApiClient, new Query.Builder()
.addFilter(Filters.eq(SearchableField.TRASHED, false))
.addFilter(Filters.eq(SearchableField.TITLE, FOLDER_NAME_CORE ))
.setSortOrder(new SortOrder.Builder().addSortDescending(SortableField.MODIFIED_DATE).build())
.build()).await().getMetadataBuffer();
if (metadataBufferResult.getCount() > 0) {
Log.d(TAG, "Creating new folder");
...
} else {
Log.d(TAG, "Using existing folder");
}
I've tried making the folder publicly shared but it didn't change anything (as expected). Does anyone know what I have to change to make it find the existing folder instead? As far as I know this list is the only possible search options.
I hope you are aware of the supported scopes.
I think there are 2 things that cause your problem and you will have to re-think your app's logic.
First, GDAA (unlike the REST Api), introduces some latency, so the file/folder may not exist on the Drive for awhile, even if you got your DriveId (see this).
Second, the fact that you use TITLE as an indicator of existence does not work in the GooDrive universe, since TITLE is not unique (you already know that).
I would recommend this approach:
when your (GDAA based) app creates a file/folder, wait until you get it's ResourceId. That confirms it's existence (again here).
always check for the existence of a file/folder in the Drive using it's ResourceId (turned into DriveId)
I have yet another hurdle to climb with my GOOGLE DRIVE SDK Android App. I am uploading scanned images with tightly controlled index fields - user defined 'tags' from local dictionary. For instance XXX.JPG has index words "car" + "insurance". Here is a simplified code snippet:
...
body.setTitle("XXX.JPG");
body.setDescription("car, insurance");
body.setIndexableText(new IndexableText().setText("car insurance"));
body.setMimeType("image/jpeg");
body.setParents(Arrays.asList(new ParentReference().setId(...)));
FileContent cont = new FileContent("image/jpeg", new java.io.File(fullPath("xxx.jpg")));
File gooFl = _svc.files().insert(body, cont).execute();
...
Again, everything works great, except when I start a search, I get results that apparently come from some OCR post process, thus rendering my system's DICTIONARY unusable. I assume I can use a custom MIME type, but then the JPEG images become invisible for users who use standard GOOGLE DRIVE application (local, browser-based ... ). So the question is: Can I upload MIME "image/jpeg" files with custom indexes (either Indexable, or Description fields) but stop GOOGLE from OCR-ing my files and adding indexes I did not intend to have?
Just to be more specific, I search for "car insurance" and instead of my 3 files I indexed this way, I get unmanageable pile of other results (JPEG scanned documents) that had "car" and "insurance" somewhere in them. Not what my app wants.
Thank you in advance, sean
...
Based on Burcu's advise below, I modified my code to something that looks like this (stripped to bare bones):
// define meta-data
File body = new File();
body.setTitle("xxx.jpg");
body.setDescription(tags);
body.setIndexableText(new IndexableText().setText(tags));
body.setMimeType("image/jpeg");
body.setParents(Arrays.asList(new ParentReference().setId(_ymID)));
body.setModifiedDate(DateTime.parseRfc3339(ymdGOO));
FileContent cont =
new FileContent("image/jpeg",new java.io.File(fullPath("xxx.jpg")));
String sID = findOnGOO(driveSvc, body.getTitle());
// file not found on gooDrive, upload and fix the date
if (sID == null) {
driveSvc.files().insert(body, cont).setOcr(false).execute();
driveSvc.files().patch(gooFl.getId(), body).setOcr(false).setSetModifiedDate(true).execute();
// file found on gooDrive - modify metadata and/or body
} else {
// modify content + metadata
if (contentModified) {
driveSvc.files().update(sID, body, cont).setOcr(false).setSetModifiedDate(true).execute();
// only metadata (tags,...)
} else {
driveSvc.files().patch(sID, body).setOcr(false).setSetModifiedDate(true).execute();
}
}
...
It is a block that uploads or modifies a Google Drive file. The two non-standard operations are:
1/ resetting the file's 'modified' date in order to force the date of file creation - tested, works OK
2/ stopping the OCR process that interferes with my apps indexing scheme - will test shortly and update here
For the sake of simplicity, I did not include the implementation of "findInGOO()" method. It is quite simple 2-liner and I can supply it upon request
sean
On insertion, set the ocr parameter to false:
service.files().update(body, content).setOcr(false).execute();
all you guys who know things I don't :-)
I've run into this problem that may not be actually a problem, only a revelation that I don't know what I'm doing. AGAIN!
I'm uploading a JPEG with some description and indexable keywords. Works like a charm. But I can't figure out how to add/modify meta data later, without creating another instance of the file. So, when I add a picture of my dog with description "dog", I end up with what I wanted. But if I try to modify the metadata by either using:
gooFl = drvSvc.files().insert(meta).execute();
or
gooFl = drvSvc.files().insert(meta,null).execute();
I end up with a new file (of the same name) on GOOGLE Drive.
See the code snippet below:
File meta = new File();
meta.setTitle("PicOfMyDog.jpg");
meta.setMimeType("image/jpeg");
meta.setParents(Arrays.asList(new ParentReference().setId(ymID)));
File gooFl = null;
if (bNewJPG == true) {
meta.setDescription("dog");
meta.setIndexableText(new IndexableText().setText("dog"));
gooFl = drvSvc.files().insert(meta,
new FileContent("image/jpeg", new java.io.File(fullPath("PicOfMyDog.jpg"))))
.execute();
} else {
meta.setDescription("dick");
meta.setIndexableText(new IndexableText().setText("dick"));
// gooFl = drvSvc.files().insert(meta).execute();
gooFl = drvSvc.files().insert(meta,null).execute();
}
if (gooFl != null)
Log.d("atn", "success " + gooFl.getTitle());
It is the "else" branch I'm asking about. First file one has meatadata "dog", second "dick".
So, what's the solution. Do I delete the previous instance (and how)? Is there another syntax / method I don't know about?
thank you, sean
If you need to modify the metadata, use files.patch.
drvSvc.files().patch(id, meta).execute();
In cases you need both modify the metadata and the file contents, use files.update.
drvSvc.files().update(id, meta, content).execute();
Insertions make POST requests that always create a new resource.
You need to use Files.Patch if you want to update only Metadata. files().insert always creates a new file.
A full list of File commands and what operations you need to use can be found in the API Reference
I have developed a Service which will start when I receive the "ON_BOOTUP_COMPLETED" intent,
in "onCreate" of my Service I wrote the logic to create a text file in SD card of the device.
Below is the logic I have used to do so:
File abc = new File(Environment.getExternalStorageDirectory()+"\abc.txt");
if(!abc .exists())
abc.createNewFile();
abcwriter = new FileWriter(abc);
I am using "abcwriter" in other methods to write some content in to text file.
So far it is working fine.
But when rebooted the device, I observed that "abc.txt" file is creating again.
but I put a check before creating file "if(!abc .exists())". But still new file is created.
I suspect that when I rebooted the device my files are deleted. Is this the android behaviour..??
If it is please help me what I can do to make sure my files not created again.
You have to use the constructor below and pass true as the second parameter if you want to append to the file. Otherwise it will just get overwritten each time your code runs (when you reboot).Also get rid of the createNewFile() call, you don't need it since the writer will create it.
FileWriter(File file, boolean append)
abcwriter = new FileWriter(abc); -> this line (re)creates the file.
Make sure it's called only when needed:
File abc = new File(Environment.getExternalStorageDirectory()+"\abc.txt");
if(!abc.exists()) {
// abc.createNewFile(); -> this is not needed since following line handles this
abcwriter = new FileWriter(abc);
}