Creating a folder inside a folder in google drive android - android

I want to integrate Google drive with my app I have registered my app in Google Developers Console. I got a sample from https://github.com/googledrive/android-demos .By this i am able to create a file,folder in Google drive's root folder but the problem is I couldn't create a file or folder inside an existing folder. In such case i got a toast "Cannot find DriveId. Are you authorized to view this file?" ie I cannot get the driveID
public class CreateFolderInFolderActivity extends BaseDemoActivity {
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
Drive.DriveApi.fetchDriveId(getGoogleApiClient(), EXISTING_FOLDER_ID)
.setResultCallback(idCallback);
}
final ResultCallback<DriveIdResult> idCallback = new ResultCallback<DriveIdResult>() {
#Override
public void onResult(DriveIdResult result) {
if (!result.getStatus().isSuccess()) {
showMessage(result.getStatus().toString());
showMessage("Cannot find DriveId. Are you authorized to view this file?");
return;
}
DriveFolder folder = Drive.DriveApi
.getFolder(getGoogleApiClient(), result.getDriveId());
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("MyNewFolder").build();
folder.createFolder(getGoogleApiClient(), changeSet)
.setResultCallback(createFolderCallback);
}
};
final ResultCallback<DriveFolderResult> createFolderCallback = new
ResultCallback<DriveFolderResult>() {
#Override
public void onResult(DriveFolderResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Problem while trying to create a folder");
return;
}
showMessage("Folder successfully created");
}
};
}
I cannot find any proper documentation for this.
Plz help me where i am going wrong or Whether I Have to include any other permissions

You can take a peek here: in the 'createTree()' method, there is a creation of folder within a folder.
There are 3 different drive ID entities in the new Google Drive Android API (GDAA)
the object of type DriveID - the one you get from methods and use in your code
a string you get from encodeToString() and pass to decodeFromString() - used to save within the app (caching for instance)
a string you get from getResourceId() and pass to fetchDriveId() - the one you see in the html address of a file.
Both 2 and 3 identifiers are strings, so they may be confused. Identifier 2 is faster when retrieving Drive ID (via decodeFromString()). Identifier 3 is slower to retrieve (via fetchDriveId()), but usefull if you need to take your ID elsewhere (Apps Script, for instance).
Please see also: SO 21800257

What is EXISTING_FOLDER_ID? If you are trying to just run the sample straight out without having made any changes, this won't work.
You need to change EXISTING_FOLDER_ID to the resource id of a folder that your app has access to. This could be a folder that your app created in the root.

first create folder using creatreeTree()
then run a search query to get id of create public static ArrayList<ContentValues> search(String prnId, String titl, String mime) {
ArrayList<ContentValues> gfs = new ArrayList<>();
if (mGOOSvc != null && mConnected) try {
// add query conditions, build query
String qryClause = "'me' in owners and ";
if (prnId != null) qryClause += "'" + prnId + "' in parents and ";
if (titl != null) qryClause += "title = '" + titl + "' and ";
if (mime != null) qryClause += "mimeType = '" + mime + "' and ";
qryClause = qryClause.substring(0, qryClause.length() - " and ".length());
Drive.Files.List qry = mGOOSvc.files().list().setQ(qryClause)
.setFields("items(id,mimeType,labels/trashed,title),nextPageToken");
String npTok = null;
if (qry != null) do {
FileList gLst = qry.execute();
if (gLst != null) {
for (File gFl : gLst.getItems()) {
if (gFl.getLabels().getTrashed()) continue;
gfs.add( UT.newCVs(gFl.getTitle(), gFl.getId(), gFl.getMimeType()));
} //else UT.lg("failed " + gFl.getTitle());
npTok = gLst.getNextPageToken();
qry.setPageToken(npTok);
}
} while (npTok != null && npTok.length() > 0); //UT.lg("found " + vlss.size());
} catch (Exception e) { UT.le(e); }
return gfs;
}
when you get folder id use this code to create folder in folder ` public static ParentReference insertFileIntoFolder(Drive service, String folderId,
String folderName) throws IOException {
// Log.e("founddd",id);
File fileMetadata = new File();
fileMetadata.setParents(Collections.singletonList(new ParentReference().setId(folderId == null ? "root" : folderId)));
fileMetadata.setTitle(folderName);
fileMetadata.setMimeType("application/vnd.google-apps.folder");
File file = mGOOSvc.files().insert(fileMetadata).execute();
System.out.println("Folder ID: " + file.getId());
strChildFolder = file.getId();
return null;
}`

Related

How to read list of 3rd party resources held in jar files from Android Application at runtime

Im investigating the use of citeproc in my current Android application
api 'de.undercouch:citeproc-java:2.0.0'
implementation 'org.citationstyles:styles:20.11'
implementation 'org.citationstyles:locales:20.11'
Its working fine using
// https://repo1.maven.org/maven2/com/eclipsesource/j2v8/j2v8/6.2.0/
implementation(name: 'j2v8-6.2.0', ext: 'aar')
However as Im running on Android OS the CSL static method
CSL.getSupportedStyles()
returns an empty list.
the underlying code in this method is as follows:-
private static Set<String> getAvailableFiles(String prefix,
String knownName, String extension) throws IOException {
Set<String> result = new LinkedHashSet<>();
// first load a file that is known to exist
String name = prefix + knownName + "." + extension;
URL knownUrl = CSL.class.getResource("/" + name);
if (knownUrl != null) {
String path = knownUrl.getPath();
// get the jar file containing the file
if (path.endsWith(".jar!/" + name)) {
String jarPath = path.substring(0, path.length() - name.length() - 2);
URI jarUri;
try {
jarUri = new URI(jarPath);
} catch (URISyntaxException e) {
// ignore
return result;
}
try (ZipFile zip = new ZipFile(new File(jarUri))) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
if (e.getName().endsWith("." + extension) &&
(prefix.isEmpty() || e.getName().startsWith(prefix))) {
result.add(e.getName().substring(
prefix.length(), e.getName().length() - 4));
}
}
}
}
}
return result;
}
Is it possible to list get getAvailableFiles method to work on Android OS?
The following aproach does not work
val name: String = "$prefix$knownName.$extension"
val knownUrl: URL? = CSL::class.java.classLoader.getResource("$name")
All I require is the list of ".csl" files that in an Eclipse java project show as residing in styles-20.11.jar file
When I extract my Application APK file the ".csl" files are all listed seperately
Where am I going wrong?
How can I get a list of all ".csl" files available to CSL?
Your provided detail guides me to find the solution. That's enough to define apk in the checker condition.
class CLSHelper {
public static Set<String> getSupportedStyles() throws IOException {
return getAvailableFiles("", "ieee", "csl");
}
/**
* Customizing this function to able run in Android environment
*/
private static Set<String> getAvailableFiles(String prefix,
String knownName, String extension) throws IOException {
Set<String> result = new LinkedHashSet<>();
// first load a file that is known to exist
String name = prefix + knownName + "." + extension;
URL knownUrl = CSL.class.getResource("/" + name);
if (knownUrl != null) {
String path = knownUrl.getPath();
// get the jar or apk file containing the file
if (path.endsWith(".jar!/" + name) || path.endsWith(".apk!/" + name)) { // changing this line
String jarPath = path.substring(0, path.length() - name.length() - 2);
URI jarUri;
try {
jarUri = new URI(jarPath);
} catch (URISyntaxException e) {
// ignore
return result;
}
try (ZipFile zip = new ZipFile(new File(jarUri))) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
if (e.getName().endsWith("." + extension) &&
(prefix.isEmpty() || e.getName().startsWith(prefix))) {
result.add(e.getName().substring(
prefix.length(), e.getName().length() - 4));
}
}
}
}
}
return result;
}
}
Output:
CSL.getSupportedStyles() // []
CLSHelper.getSupportedStyles() // [dependent/annals-of-occupational-and-environmental-medicine, dependent/photoacoustics, dependent/statistical-science, twentieth-century-music, dependent/aims-medical-science, dependent/cell-systems, dependent/nursingplus-open, dependent/computer-science-review,...]
P.S: To running on AndroidStudio I had to add these lines in build.gradle
android{
...
packagingOptions {
exclude 'META-INF/truffle/language'
}
}

Drive Api save to folder, if folder doesnt exist, create than save

im using the Google Drive API to save(use as backup) a database there, its working nice, but just if i use the ROOT
the Api Call:
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
......build();
Drive.DriveApi.getRootFolder(mGoogleApiClient)
.createFile(mGoogleApiClient, metadataChangeSet, result.getDriveContents())
.setResultCallback(fileCallback);
CallBack to Save the file:
final public ResultCallback < DriveFolder.DriveFileResult > fileCallback = new
ResultCallback < DriveFolder.DriveFileResult > () {
#Override
public void onResult(DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
return;
}
Log.i(TAG, "Successfull !");
}
};
i know that i must get the Folder, but if i do this, i need to do a CallBack to call another callback and then save?
isnt any way to directly do .createNewFile inside the FOLDER? without doing another Query for folder, check if the folder exist than create the folder, than use the DriveID, than create the file?
Remember, that in the GooDrive universe, the tree structure (folder, subfolder, ...) is a mirage. The Drive is a flat system of objects (files, folders) where one of the metadata fields is a 'set of parent IDs', that actually forms the notion of parentobject - childobject structure. Actually the classic tree (one parent many children) is not even enforced, so a child object can 'appear' in more that one parent.
This fact explains that you CAN NOT create an OS type of path in one shot. The objects (parents) must be created before their IDs can be plugged into child objects' metadata.
So the only way to do it, is to do what you say:
if folder exists
return it's ID
else
return ID of newly created one
create a child object with parent's ID
... and here is an example how I create a structure of type:
/ MYROOT / 2015 / 2015-12
(where MYROOT, 2015 , 2015-12 are subfloders the Drive root)
new Thread(new Runnable() {
#Override
public void run() {
DriveId Id = getFolder( getFolder( getFolder(
Drive.DriveApi.getRootFolder(mGAC).getDriveId(), "MYROOT"),
"2015",
"2015-12"
);
}
}).start();
GoogleApiClient mGAC;
DriveId getFolder(DriveId parentId, String titl) {
DriveId dId = null;
if (parentId != null && titl != null) try {
ArrayList<Filter> fltrs = new ArrayList<>();
fltrs.add(Filters.in(SearchableField.PARENTS, parentId));
fltrs.add(Filters.eq(SearchableField.TITLE, titl));
fltrs.add(Filters.eq(SearchableField.MIME_TYPE, "application/vnd.google-apps.folder"));
Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();
MetadataBuffer mdb = null;
DriveApi.MetadataBufferResult rslt = Drive.DriveApi.query(mGAC, qry).await();
if (rslt.getStatus().isSuccess()) try {
mdb = rslt.getMetadataBuffer();
if (mdb.getCount() > 0)
dId = mdb.get(0).getDriveId();
} catch (Exception ignore) {}
finally { if (mdb != null) mdb.close(); }
if (dId == null) {
MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(UT.MIME_FLDR).build();
DriveFolderResult r1 = parentId.asDriveFolder().createFolder(mGAC, meta).await();
DriveFolder dFld = (r1 != null) && r1.getStatus().isSuccess() ? r1.getDriveFolder() : null;
if (dFld != null) {
MetadataResult r2 = dFld.getMetadata(mGAC).await();
if ((r2 != null) && r2.getStatus().isSuccess()) {
dId = r2.getMetadata().getDriveId();
}
}
}
} catch (Exception e) { e.printStackTrace(); }
return dId;
}
In the 'mdb.get(0).getDriveId()' area, you can see how hacky it gets when you try to impose a classic tree structure on the Drive. The search here can return multiple objects with the same name, so I use the first one. There should be some kind of error reporting here.
As you can see it is possible to replace callbacks with the 'await()' method, flattening the code into a classic DOS style spaghetti code as long as you place the whole sequence off-UI thread (asynctask, thread, ....)
Still, more elegant (IMO) option to accomplish this is to use recursive call from the result callback.
fromPath(Drive.DriveApi.getRootFolder(mGAC).getDriveId(), "MYROOT/2015/2015-12/file.jpg");
....
void fromPath(final DriveId parentId, final String path) {
if (parentId != null && path != null) {
final int idx = path.indexOf('/');
if (idx < 0) {
// reached last path item - probably file name
// CREATE FILE WITH patentID AND QUIT
return; //--- DONE -------------------->>>
}
final String titl = path.substring(0, idx);
ArrayList<Filter> fltrs = new ArrayList<>();
fltrs.add(Filters.in(SearchableField.PARENTS, parentId));
fltrs.add(Filters.eq(SearchableField.TITLE, titl));
fltrs.add(Filters.eq(SearchableField.MIME_TYPE, UT.MIME_FLDR));
Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();
Drive.DriveApi.query(mGAC, qry).setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
#Override
public void onResult(DriveApi.MetadataBufferResult rslt) {
MetadataBuffer mdb = null;
if (rslt != null && rslt.getStatus().isSuccess()) {
try {
mdb = rslt.getMetadataBuffer();
for (Metadata md : mdb) {
if (md.isTrashed()) continue;
fromPath(md.getDriveId(), path.substring(idx + 1));
return; //+++ first found, NEXT +++++++>>>
}
} finally { if (mdb != null) mdb.close(); }
}
MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(UT.MIME_FLDR).build();
parentId.asDriveFolder().createFolder(mGAC, meta)
.setResultCallback(new ResultCallback<DriveFolderResult>() {
#Override
public void onResult(DriveFolderResult rslt) {
DriveFolder dFld = rslt != null && rslt.getStatus().isSuccess() ? rslt.getDriveFolder() : null;
if (dFld != null) {
dFld.getMetadata(mGAC).setResultCallback(new ResultCallback<MetadataResult>() {
#Override
public void onResult(MetadataResult rslt) {
if (rslt != null && rslt.getStatus().isSuccess()) {
fromPath(rslt.getMetadata().getDriveId(), path.substring(idx + 1));
return; //+++ created, NEXT +++++++>>>
}
}
});
}
}
});
}
});
}
}
A WORD OF CAUTION:
As I called this sequence repeatedly, using the last DriveId (like 2015-12) as a parent of a JPEG image file, I have experienced weird behavior, like suddenly getting a 'null' result from 'Drive.DriveApi.getRootFolder(mGAC).getDriveId()'. It shouldn't happen and I assume it is a bug in GDAA. I contribute this to the fact that the DriveId used inside GDAA is 'invalid' until the folder gets committed and the ResourceId is resolved in underlying REST Api. Unfortunately, there is no completion event available for folder creation, so I resolved this by calling this sequence only once in onConnected() and caching the '2015-12's DriveId for later use as a parent of the image JPEG files.
Actually you can see it here (createTree() method) with text file on the tail, but the moment I switched the TEXT to JPEG, all hell broke lose.
Good Luck

Android: Failed to ensure directory

I have been using "Environment.getExternalStorage()" to store and manage files. And there is no warning message from logcat with that method and works greatly fine.
But, My project needs to use method "Context.getExternalFilesDir(String type)" and there is a warning message
ContextImpl:Failed to ensure directory: /storage/external_SD/Android/data/(package name)/files
Fortunately that File object works fine(reading or write or making folder works, too).
But I want to know how to resolve that warning message. Do I miss something?
You should know how the warning message comes up.
The getExternalFilesDir(String type) will call getExternalFilesDirs(String type) (notice the 's' at the final of the second method name).
The getExternalFilesDirs(String type) will find all dirs of the type, and calls ensureDirsExistOrFilter() at the end to ensure the directories exist.
If the dir can't be reached, it will print a warning!
Log.w(TAG, "Failed to ensure directory: " + dir);
dir = null;
So, if your device has two sdcard paths, it will produce two dirs. If one is not available, the warning will come up.
The conclusion is the warning does not need to be fixed.
If you have code that is iterating files, calling this API many times, this warning can cause log pollution. To solve this (since the warning is actually benign) you can create a wrapper class that stores the result of calling getExternalFilesDir / getExternalCacheDir and subsequently returns the stored value instead of calling the API. In this way, at least you will only ever see this message once.
I follow the getExternalFilesDir() source
/**
* Ensure that given directories exist, trying to create them if missing. If
* unable to create, they are filtered by replacing with {#code null}.
*/
private File[] ensureExternalDirsExistOrFilter(File[] dirs) {
File[] result = new File[dirs.length];
for (int i = 0; i < dirs.length; i++) {
File dir = dirs[i];
if (!dir.exists()) {
if (!dir.mkdirs()) {
// recheck existence in case of cross-process race
if (!dir.exists()) {
// Failing to mkdir() may be okay, since we might not have
// enough permissions; ask vold to create on our behalf.
final IMountService mount = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
try {
final int res = mount.mkdirs(getPackageName(), dir.getAbsolutePath());
if (res != 0) {
Log.w(TAG, "Failed to ensure " + dir + ": " + res);
dir = null;
}
} catch (Exception e) {
Log.w(TAG, "Failed to ensure " + dir + ": " + e);
dir = null;
}
}
}
}
result[i] = dir;
}
return result;
}
immediate use Environment.getExternalStorageDirectory() get ExternalDirs
public final class StorageUtil {
public static final String DIR_ANDROID = "Android";
private static final String DIR_DATA = "data";
private static final String DIR_FILES = "files";
private static final String DIR_CACHE = "cache";
#Nullable
public static synchronized File getExternalStorageAppFilesFile(Context context, String fileName) {
if (context == null) return null;
if (fileName == null) return null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dirs = buildExternalStorageAppFilesDirs(Environment.getExternalStorageDirectory().getAbsolutePath(), context.getPackageName());
return new File(dirs, fileName);
}
return null;
}
public synchronized static File buildExternalStorageAppFilesDirs(String externalStoragePath, String packageName) {
return buildPath(externalStoragePath, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
}
public synchronized static File buildPath(String base, String... segments) {
File cur = new File(base);
for (String segment : segments) {
cur = new File(cur, segment);
}
return cur;
}
}

Not fully listing Google Drive folders and files in Android app

I am trying to access all files and folders from google drive to a arraya list. But I can get only one file from Drive. What to do get all files and folders from google drive. I am using the following code..
Thanks
Arun
public void onConnected(Bundle connectionHint) {
// Log.i(TAG, "API client connected.");
Toast.makeText(getActivity(), "Successfully logged in", Toast.LENGTH_LONG).show();
DriveFolder s = Drive.DriveApi.getRootFolder(mGoogleApiClient);
String s1 = (Drive.DriveApi.getRootFolder(mGoogleApiClient)).getDriveId().toString();
DriveId sFolderId2 = DriveId.decodeFromString(s1);
DriveId sFolderId = (Drive.DriveApi.getRootFolder(mGoogleApiClient)).getDriveId();
DriveFolder folder = Drive.DriveApi.getFolder(mGoogleApiClient, sFolderId);
folder.listChildren(mGoogleApiClient).setResultCallback(rootFolderCallback);
// findAll(folder);
}
public ResultCallback<DriveApi.MetadataBufferResult> rootFolderCallback = new
ResultCallback<DriveApi.MetadataBufferResult>() {
#Override
public void onResult(DriveApi.MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
return;
}
resultarray = new ArrayList<String>();
int hh = result.getMetadataBuffer().getCount();
for (int i = 0; i < result.getMetadataBuffer().getCount(); i++) {
resultarray.add(result.getMetadataBuffer().get(i).getTitle());
}
Toast.makeText(getActivity(), "Successfully listed files.", Toast.LENGTH_LONG).show();
}
};
UPDATE (Aug 25, 2015, 10:39 MST)
Based on your comment below, you have 2 options:
1/ Stay with the GDAA, use one of the INTENTS:
- Pick a file with opener activity
- Pick a folder with opener activity
See, GDAA does not let your app see anything it did not create (SCOPE_FILE only), but it still allows user to browse everything. If the user selects a file, it will become visible to you app. I don't know your app's intentions, so I can't say if this approach is usable.
2/ Switch to the REST with the DRIVE scope and your app will see everything (user has to approve up front). The basic CRUD implementation can be found here but make sure you change the scope in the init() method to 'DriveScopes.DRIVE'.
In case your app needs to iterate down the folder tree, collecting files in the process, both 'testTree()' and 'deleteTree()' methods in the MainActivity() do exactly that.
You may also stay with the GDAA and add REST functionality to it by adding
com.google.api.services.drive.Drive mGOOSvc = new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(),
GoogleAccountCredential.usingOAuth2(appContext, Collections.singletonList(DriveScopes.DRIVE))
.setSelectedAccountName(email)
but you will sooner or later run into problems caused by GDAA caching / latency.
ORIGINAL ANSWER
Try this approach:
private static GoogleApiClient mGAC;
/****************************************************************************
* find file/folder in GOODrive
* #param prnId parent ID (optional), null searches full drive, "root" searches Drive root
* #param titl file/folder name (optional)
* #param mime file/folder mime type (optional)
* #return arraylist of found objects
*/
static void search(String prnId, String titl, String mime) {
if (mGAC != null && mGAC.isConnected()) {
// add query conditions, build query
ArrayList<Filter> fltrs = new ArrayList<>();
if (prnId != null){
fltrs.add(Filters.in(SearchableField.PARENTS,
prnId.equalsIgnoreCase("root") ?
Drive.DriveApi.getRootFolder(mGAC).getDriveId() : DriveId.decodeFromString(prnId)));
}
if (titl != null) fltrs.add(Filters.eq(SearchableField.TITLE, titl));
if (mime != null) fltrs.add(Filters.eq(SearchableField.MIME_TYPE, mime));
Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();
// fire the query
Drive.DriveApi.query(mGAC, qry).setResultCallback(new ResultCallback<MetadataBufferResult>() {
#Override
public void onResult(MetadataBufferResult rslt) {
if (rslt != null && rslt.getStatus().isSuccess()) {
MetadataBuffer mdb = null;
try {
mdb = rslt.getMetadataBuffer();
if (mdb != null ) for (Metadata md : mdb) {
if (md == null || !md.isDataValid()) continue;
String title = md.getTitle();
DriveId driveId = md.getDriveId();
//.......
}
} finally { if (mdb != null) mdb.close(); }
}
}
});
}
}
Call it first with NULLs
search(null,null,null)
To list all the files in your Google Drive. You will see all the files your Android App created. But only those - FILE scope does not see anything else.
If you need to scan the directory tree, you may look closer at this GDAA demo, in MainActivity, there is are 'testTree()' / 'deleteTree() methods that recursively scan the directory tree structure.
Also, you may look at the answer here, it deals with a similar issue (especially the comments exchange under the answer).
Good Luck
Please note that you can use GDAA to retrieve the files and folder that you have either uploaded from the Android Device or downloaded via the drive app. This is to have more security (as quoted by Google).
In he code you need to ensure that you are trying all possible combinations for the files that may be present in your Google Drive account. For example, check if you are tracking the parent of a file or a folder. If this condition is not met your app wont be able to retrieve those specific files.
/** Get the list of parents Id in ascending order. */
private List<String> collectParents(String folderId, Map<String, String> folderIdToParentId){
String parentId = folderIdToParentId.get(folderId);
if (logger.isTraceEnabled()){
logger.trace("Direct parent of {} is {}", folderId, parentId);
}
List<String> ancestors = new ArrayList<String>();
ancestors.add(parentId);
if (folderIdToParentId.containsKey(parentId)){
ancestors.addAll(collectParents(parentId, folderIdToParentId));
return ancestors;
}
return ancestors;
}
See the full code here.

Google Drive Android API - Check if folder exists

I'm trying to figure out how to check if a folder exists in Google Drive using the new Google Drive Android API
I've tried the following, thinking that it would either crash or return null if the folder is not found, but it doesn't do that (just as long as it is a valid DriveId, even though the folder has been deleted).
DriveFolder folder = Drive.DriveApi.getFolder(getGoogleApiClient(), driveId));
If i try to create a file the folder I get from the above code, it does not crash either?
I'm clearly having a little hard time understanding how this new API works all to together, especially with the very limited tutorials and SO questions out there, and I'm really stuck on this one, so any input will be much appreciated.
Just to clarify my problem: I'm creating a file in a specified Google Drive folder, but if the folder does not exist (has been deleted by user), I want to create it first.
After a lot of research this is the code I ended up with. It works properly, but has an issue: When a folder is trashed in Google Drive it takes some time (hours) before the metadata I can fetch from my app is updated, meaning that this code can first detect if the folder has been trashed a couple of hours later the trashing event actually happened - further information and discussions can be found here.
public class checkFolderActivity extends BaseDemoActivity {
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
DriveId folderId = DriveId.decodeFromString(folderId);
DriveFolder folder = Drive.DriveApi.getFolder(mGoogleApiClient, folderId);
folder.getMetadata(mGoogleApiClient).setResultCallback(metadataRetrievedCallback);
}
final private ResultCallback<DriveResource.MetadataResult> metadataRetrievedCallback = new
ResultCallback<DriveResource.MetadataResult>() {
#Override
public void onResult(DriveResource.MetadataResult result) {
if (!result.getStatus().isSuccess()) {
Log.v(TAG, "Problem while trying to fetch metadata.");
return;
}
Metadata metadata = result.getMetadata();
if(metadata.isTrashed()){
Log.v(TAG, "Folder is trashed");
}else{
Log.v(TAG, "Folder is not trashed");
}
}
};
}
If you're creating a folder based on it's existence status, the 'createTree()' method here does just that.
The following 2 code snippets list files/folders based on arguments passed ( inside a folder, globally, based on MIME type ...). The line with md.getTitle() is the one that you can use to interrogate files/folders.
GoogleApiClient _gac;
void findAll(String title, String mime, DriveFolder fldr) {
ArrayList<Filter> fltrs = new ArrayList<Filter>();
fltrs.add(Filters.eq(SearchableField.TRASHED, false));
if (title != null) fltrs.add(Filters.eq(SearchableField.TITLE, title));
if (mime != null) fltrs.add(Filters.eq(SearchableField.MIME_TYPE, mime));
Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();
MetadataBufferResult rslt = (fldr == null) ? Drive.DriveApi.query(_gac, qry).await() :
fldr.queryChildren(_gac, qry).await();
if (rslt.getStatus().isSuccess()) {
MetadataBuffer mdb = null;
try {
mdb = rslt.getMetadataBuffer();
if (mdb == null) return null;
for (Metadata md : mdb) {
if ((md == null) || md.isTrashed()) continue;
--->>>> md.getTitle()
}
} finally { if (mdb != null) mdb.close(); }
}
}
void listAll(DriveFolder fldr) {
MetadataBufferResult rslt = fldr.listChildren(_gac).await();
if (rslt.getStatus().isSuccess()) {
MetadataBuffer mdb = null;
try {
mdb = rslt.getMetadataBuffer();
if (mdb == null) return null;
for (Metadata md : mdb) {
if ((md == null) || md.isTrashed()) continue;
--->>>> md.getTitle()
}
} finally { if (mdb != null) mdb.close(); }
}
}
The key is probably checking the "isTrashed()" status. Since 'remove' file on the web only moves it to TRASH. Also, deleting in general (on the website, since there is no 'DELETE' in the API) is a bit flaky. I was testing it for a while, and it may take hours, before the "isTrashed()" status is updated. And manually emptying the trash in Google Drive is also unreliable. See this issue on Github.
There is a bit more talk here, but probably unrelated to your problem.
So today the answer is out of date API.
So I have posted example of how to check the folder if exists with the new update of documentation:
fun checkFolder(name: String):Boolean {
check(googleDriveService != null) { "You have to init Google Drive Service first!" }
return search(name, FOLDER_MIME_TYPE)
}
private fun search(name: String, mimeType:String): Boolean {
var pageToken: String? = null
do {
val result: FileList =
googleDriveService!!
.files()
.list()
.setQ("mimeType='$FOLDER_MIME_TYPE'")
.setSpaces("drive")
.setFields("nextPageToken, files(id, name)")
.setPageToken(pageToken)
.execute()
for (file in result.files) {
Log.d(TAG_UPLOAD_FILE , "Found file: %s (%s)\n ${file.name}, ${file.id} ")
if (name == file.name) return true
}
pageToken = result.nextPageToken
} while (pageToken != null)
return false
}
private const val FOLDER_MIME_TYPE= "application/vnd.google-apps.folder"
You can try to get the metadata for the folder. If the folder doesn't exist, this will fail.

Categories

Resources