17

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.

Jakob Harteg
  • 9,587
  • 15
  • 56
  • 78

4 Answers4

8

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"); 
                }

            }
        };
}
Jakob Harteg
  • 9,587
  • 15
  • 56
  • 78
  • Thanks for confirming the issue also reported here: http://stackoverflow.com/questions/22515028/deleted-files-status-unreliably-reported-in-the-new-google-drive-android-api-gd . Just for completeness. BTW, make sure you close the 'metadata' buffer with meatadata.close(), I'm getting resource leaks if I don't. – seanpj Mar 19 '14 at 23:11
  • huh, I find no such method (metadata.close()) - reference https://developer.android.com/reference/com/google/android/gms/drive/Metadata.html – Jakob Harteg Mar 19 '14 at 23:54
  • Neither did I. But ADT was nagging me about resource leaks, so I typed "metadata." and intellisense showed the .close() method. So I used it and it stopped complaining. But all 'metadata' taken from the buffer has to be copied / consumed before closing. Grep for anything with 'getMetadataBuffer' here: https://github.com/seanpjanson/140319-Accounts/blob/master/GAC.java and you'll see. I remember poking Burcu Dogan about it in one o her's demos, but I got no response. – seanpj Mar 20 '14 at 00:02
  • Oh, I was confused, tried to close the MetaData object which of course isn't possible. calling close() on the metadata buffer as you said gets rid of of resource leaks which I also got. – Jakob Harteg Mar 20 '14 at 15:09
  • @Jakob what is folder id which is passed to that method DriveId folderId = DriveId.decodeFromString(folderId); – rafeek Sep 24 '17 at 07:06
  • out of date Api see this sample https://stackoverflow.com/a/63922067/6352712 – Serg Burlaka Sep 16 '20 at 14:19
1

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.

Community
  • 1
  • 1
seanpj
  • 6,735
  • 2
  • 33
  • 54
  • This code was working fine with old Google Play Services library but when I updated to Google Play Services 7.0.0 its crashing in AbstractPendingResult class – AndroidDev Apr 21 '15 at 14:20
  • Welcome to the wonderful world of GDAA where "panta rhei" - everything flows. :-). You can look at this updated code that works on 7.00.+. https://github.com/seanpjanson/GDAADemo/blob/master/src/main/java/com/andyscan/gdaademo/GDAA.java. Actually build tree is here: https://github.com/seanpjanson/GDAADemo/blob/master/src/main/java/com/andyscan/gdaademo/MainActivity.java#L163 – seanpj Apr 21 '15 at 18:44
  • @seanpj, thanks for your long researches in GDAA (I think they change it every month). Pls edit links to smth like https://github.com/seanpjanson/GDAADemo/blob/master/src/main/java/com/andyscan/gdrtdemo/GDAA.java. – CoolMind Jun 03 '15 at 09:45
0

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"
Serg Burlaka
  • 2,351
  • 24
  • 35
-2

You can try to get the metadata for the folder. If the folder doesn't exist, this will fail.

Cheryl Simon
  • 46,552
  • 15
  • 93
  • 82
  • Good thinking! I will try that – Jakob Harteg Mar 05 '14 at 18:21
  • I wish it would work, but no luck there. I was able to read all the metadata of long-gone files. Like trashed or even completely deleted by 'empty trash'. And 'long-gone' like 10 hours ago. I guess that the web interface is just another plug into an internal system that has some latency/synchronization issues. – seanpj Mar 20 '14 at 12:15
  • Yes, I confirm with @seanpj – Jakob Harteg Mar 20 '14 at 15:07