3

So first of all I know that the DriveID for each file/folder is device specific, BUT the resourceId is constant and that's really what I'm using to test if the items are the same (besides the title of course).

Problem

I am trying to sync folders in my AppFolder across devices, however, I'm getting different files/folders on each device despite querying via the title before doing anything. So some code:

How I set up the api client

 mGoogleApiClient = new GoogleApiClient.Builder(context.getApplicationContext())
                .addApi(Drive.API)
                .addApi(Plus.API)//used for logout process
                .addScope(Drive.SCOPE_APPFOLDER)
                .addConnectionCallbacks(OnConnection)
                .addOnConnectionFailedListener(OnFailedListener)
                .build();

How I set up the appFolder

appFolder = Drive.DriveApi.getAppFolder(mGoogleApiClient);

I have verified that I'm getting the same resourceId for the AppFolder on both devices.

How I create/get the folders

private static DriveFolder CheckForOrCreateDriveFolder(DriveFolder appFolder, String folder) {
    MetadataBuffer metadataBuffer = appFolder.queryChildren(mGoogleApiClient, new Query.Builder().addFilter(Filters.eq(SearchableField.TITLE, folderName)).build()).await().getMetadataBuffer();
    if(metadataBuffer != null&&metadataBuffer.getCount()>0) {
        Log.v(TAG,"Found "+metadataBuffer.getCount()+" existing folders");
        Metadata metadata = metadataBuffer.get(0);
        if (metadata != null && metadata.isFolder()) {
            Log.v(TAG,"Returning existing folder");
            return Drive.DriveApi.getFolder(mGoogleApiClient, metadata.getDriveId());
        }else{
            Log.v(TAG,"Returning created folder even though we found meta data");
            return appFolder.createFolder(mGoogleApiClient, new MetadataChangeSet.Builder().setTitle(folderName).build()).await().getDriveFolder();
        }
    }else{
        Log.v(TAG,"Returning created folder");
        return appFolder.createFolder(mGoogleApiClient, new MetadataChangeSet.Builder().setTitle(folderName).build()).await().getDriveFolder();
    }
}

On this I always get the expected result. I.E. Found 1 existing folders and then Returning existing folder. Also, before you mention the use of await() I'm spinning off my own threads and this is NEVER called on UI thread, so no worries.

Test code

I noticed that sqlite db wasn't copying over which then let to me figuring out that not even the folders were crossing over so I wrote another test:

DriveApi.MetadataBufferResult metadataBufferResult = appFolder.listChildren(mGoogleApiClient).await();
Log.v(TAG,"AppFolder Contents:");
for (Metadata metadata : metadataBufferResult.getMetadataBuffer()) {
    Log.v(TAG,"MD:"+metadata.getTitle()+" : "+metadata.getDriveId().getResourceId()+" - "+(metadata.isFolder()?"is a folder":"is a file"));
}

Which for one device returns:

AppFolder Contents:
MD:itemImage : 1eFvDS6cWmCbgblahblahblahblahblah - is a folder
MD:itemAudio : 1c86N8AGRV8Bblahblahblahblahblah - is a folder
MD:userImage : 1LnsgFneT-l3ZYzMbblahblahblahblah - is a folder
MD:db.sqlite : 1DH4QfUfrsxDScblahblahblahblah - is a file

and for the other:

AppFolder Contents:
MD:itemAudio : 1CqeVASm_Gjjblahblahblahblahblah - is a folder
MD:itemImage : 1Djs059FYGSfOblahblahblahblahblah - is a folder
MD:userImage : 1U5c5yCw-XdfCblahblahblahblahblah - is a folder
MD:db.sqlite : 15qR3WLmvT3Nn2Ztblahblahblahblahblah - is a file

I've verified 4 times or more that these devices are using the same google account. If there is any other code or info I could give you to help please let me know! Any help finding an answer to this would be greatly appreciated^^ Thanks!

Matthew Clark
  • 571
  • 1
  • 9
  • 33
  • So your two devices have a different view of reality. What does Drive think? Try using https://developers.google.com/drive/v2/reference/files/get#try-it or http://www.clevernote.co/app/drivecrud.html or even drive.google.com to see what is on the server. The "resourceId" is what the rest of Drive calls the fileId. Bottom line is that if the file has two resource IDs, then it's actually two separate files. Is the bifurcation reproducible? I ask because it may be a side effect of the general Drive issues of late. – pinoyyid Oct 31 '14 at 11:22
  • @pinoyyid Thanks for the question! I think the main issue is that since I'm using the AppFolder only the app can see the contents... and the developer link you gave doesn't seem to see the appFolder(or anything below it). The Clevernote version would require me adding a public web api to it... and since this is an android only app at the moment I would like to steer clear of it as long as possible. And because it's an appFolder it can't be seen into via google drive by the user. This is why I made the test list function... any other sites/avenues? I love the idea of testing this! – Matthew Clark Nov 03 '14 at 20:14
  • when you say "only the app", that should be "only an app". So you should be able to query the contents of appfolder using Oauth Playground. Make sure you include the https://www.googleapis.com/auth/drive.appfolder scope – pinoyyid Nov 04 '14 at 06:56
  • I could be wrong, but I was under the impression that it required an app built with the correct package ID as well as signed with the proper codesign. I'm adding the Client init to the info above so you can see most of it^^ – Matthew Clark Nov 04 '14 at 18:38
  • You may well be correct. I've never used appdata for this very lack of transparency in the event of a problem. – pinoyyid Nov 05 '14 at 03:16

3 Answers3

4

Unfortunately, I got lost in your specific case explanation, but if I only read the headline, I can give you some clues, since I ran into the same problem.

There are 2 ID's for each object (folder, or file) in GDAA (Google Drive Android Api), the DRIVE_ID and the RESOURCE_ID.

The DRIVE_ID identifier is specific to your particular device, so if you use it on another device, it means nothing. The RESOURCE_ID is unique across devices, so that's the one you need to use for any type of synchronization. See SO 22841237.

If you create a file/folder using GDAA, the RESOURCE_ID may not be immediately available, since GDAA needs to 'commit' the object to get it. See SO 22432431.

DO NOT use file/folder title (name) at all, since it is not a primary ID in the Google Drive world. You can have multiple Drive objects in the same area with the same title (name).

Also, be aware of the fact, that adding GDAA to the equation (as opposed to using plain RESTful API), you are adding a layer that has its own latency you have no control over. The 'ChangeEvent Listener' and 'requestSync()' are no help either. See SO 23073474.

Just to give you a hint, after spending a few months fighting the same issues, I rolled back to the RESTful API, with data provider and sync adapter, polling Drive for changes. The next step will be to add GCM-based synchronization. I was hoping the sync (using GCM) would be built-in the GDAA. But it apparently is not. And you haven't tried to delete files/folders yet, that's where the real fun begins.

Good Luck.

Community
  • 1
  • 1
seanpj
  • 6,735
  • 2
  • 33
  • 54
  • Thanks for you comments! I figured out that the resourceId was the actual identify via trial and error (which was what I was trying to explain in my first sentence). I used DriveID in the title solely because if I was searching for this question it's the word I would use, but maybe I should put resourceId in the title as well. As for the rest of what you said... I had fun with the login/out, I'm hopeful I can get this working with not too much work... what you describe would be a pretty huge re-write. Fingers-crossed. Oh and I'm only querying with titles, not using them for anything else^^ – Matthew Clark Oct 31 '14 at 11:01
  • This doesn't answer your question, but picking up on "pretty huge re-write"... I would suggest that anything you do with Drive be totally encapsulated in a module with your own interfaces to it. There are currently 4 different ways that an app can talk to Drive, and who-knows how many in the future. There is no reason why a DropBox or even a Microsoft couldn't come up with a GDAA equivalent which you may want to switch to at some point. – pinoyyid Oct 31 '14 at 11:16
  • Yep, the "pretty huge re-write". That's what I'm in the middle of. I managed to catch all the GDAA quirks, but still, the random latency was killing me in the end. Even with everything else working. Google Play Services was just too unpredictable. GDAA may be good for some applications, but I believe synchronizing Drive objects between different devices is not one of them. As I commented elsewhere, I successfully managed to create a file in a folder that did not exist (trashed, trash emptied) for 3 hours. Try to build an app around that. – seanpj Oct 31 '14 at 11:36
  • Just as an example, if you delete anything on a device A (you must use RESTful, since ther's no delete in GDAA), device B will see that darn thing for hours through GDAA (even using RESOURCE_ID). So you can end up working with non-existent Drive object. And the GDAA 'delete' is 'coming in the next release' since February. – seanpj Oct 31 '14 at 11:41
  • Luckily I'm only using this for backup, so no need to delete. I'm hoping that it's just a matter of waiting long enough and find a way to not duplicate/overwrite in the meantime... in worst case. As for the re-write, the backup code is encapsulated, however it is written with GDAA as it's backbone under the understanding that Play services was something to be relied upon... we'll see how it holds out. – Matthew Clark Oct 31 '14 at 12:11
  • Still, even without DELETE, you will be fighting the random latency issues. For instance, if you write/update anything using device A, you may not see the change on device B for awhile, since GDAA will give you its cached version even if you use RESOURCE_ID. So, for polling, RESTful is more reliable (mind you, the ultimate solution is still a GCM based notification system). And about the encapsulation, the best place for it is the 'ContentProvider' (at least in my case). If you do it there, it is easy to employ 'AbstractThreadedSyncAdapter' to do the polling/sync. – seanpj Oct 31 '14 at 12:23
  • ... and I'm not bashing GDAA, the ability to write into it without network connection is great. But this ability with caching and latency makes it a poor choice for an app that needs to see what other devices do to the files in Drive. As I said, I was hoping they would build GCM-based sync into it, but there is none afaik. Anybody knows about the plans ??? – seanpj Oct 31 '14 at 12:31
0

Does setting up the appFolder like this help?

String appFolder = Drive.DriveApi.getAppFolder(mGoogleApiClient()).getDriveId().getResourceId().toString();
berkley
  • 73
  • 2
  • 6
0

I was having the same issue (duplicated folders) and I solved using requestSync BEFORE running the search query:

Drive.DriveApi.requestSync(getGoogleApiClient()).await();
Marco C.
  • 1,282
  • 2
  • 15
  • 19