62

I've spent the last six hours pouring over documents from Google and I still don't know how to get started with this. All I want to do is make it so my existing Android app can read files from Google Drive, upload new files to Google Drive, and edit existing files on Google Drive.

I've read that Drive SDK v2 was focused solely on making it easy for Android (and mobile in general) developers to use it, and yet there seems to be virtually nothing in their documentation about it.

Ideally, I'd like someone to point at some decent documentation, example, or tutorial covering how to do this (keep in mind I'm using Android. They have plenty of stuff on how to use Drive with the Google App Engine; I have already looked at it and I have no idea how to go from that to an Android app.)

I need to know which libraries I need to download and add to my project, what I need to add to my manifest, and how I can ultimately get a list of files from Google Drive, download one, and then upload a modified version.

Ideally, I'd like it to handle accounts automatically, the way that the officially Google Drive app does.

ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193

4 Answers4

82

Edit: Claudio Cherubino says that Google Play Services is now available and will make this process a lot easier. However, there's no sample code available (yet, he says it's coming soon... they said Google Play Services was "coming soon" 4 months ago, so there's a good chance this answer will continue to be the only completely working example of accessing Google Drive from your Android application into 2013.)

Edit 2X: Looks like I was off by about a month when I said Google wouldn't have a working example until next year. The official guide from Google is over here:

https://developers.google.com/drive/quickstart-android

I haven't tested their methods yet, so it's possible that my solutions from September 2012 (below) are still the best:

Google Play Services is NOT REQUIRED for this. It's a pain in the butt, and I spent well over 50 hours (edit: 100+ hours) figuring it all out, but here's a lot of things that it'll help to know:

THE LIBRARIES

For Google's online services in general you'll need these libraries in your project: (Instructions and Download Link)

  • google-api-client-1.11.0-beta.jar
  • google-api-client-android-1.11.0-beta.jar
  • google-http-client-1.11.0-beta.jar
  • google-http-client-android-1.11.0-beta.jar
  • google-http-client-jackson-1.11.0-beta.jar
  • google-oauth-client-1.11.0-beta.jar
  • guava-11.0.1.jar
  • jackson-core-asl-1.9.9.jar
  • jsr305-1.3.9.jar

For Google Drive in particular you'll also need this:

  • google-api-services-drive-v2-rev9-1.8.0-beta.jar (Download Link)

SETTING UP THE CONSOLE

Next, go to Google Console. Make a new project. Under Services, you'll need to turn on two things: DRIVE API and DRIVE SDK! They are separate, one does not automatically turn the other on, and YOU MUST TURN BOTH ON! (Figuring this out wasted at least 20 hours of my time alone.)

Still on the console, go to API Access. Create a client, make it an Android app. Give it your bundle ID. I don't think the fingerprints thing is actually important, as I'm pretty sure I used the wrong one, but try to get that right anyways (Google provides instructions for it.)

It'll generate a Client ID. You're going to need that. Hold onto it.

Edit: I've been told that I'm mistaken and that you only need to turn on Drive API, Drive SDK doesn't need to be turned on at all, and that you just need to use the Simple API Key, not set up something for Android. I'm looking into that right now and will probably edit this answer in a few minutes if i figure it out...

THE ANDROID CODE - Set Up and Uploading

First, get an auth token:

AccountManager am = AccountManager.get(activity);
am.getAuthToken(am.getAccounts())[0],
    "oauth2:" + DriveScopes.DRIVE,
    new Bundle(),
    true,
    new OnTokenAcquired(),
    null);

Next, OnTokenAcquired() needs to be set up something like this:

private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
    @Override
    public void run(AccountManagerFuture<Bundle> result) {
        try {
            final String token = result.getResult().getString(AccountManager.KEY_AUTHTOKEN);
            HttpTransport httpTransport = new NetHttpTransport();
            JacksonFactory jsonFactory = new JacksonFactory();
            Drive.Builder b = new Drive.Builder(httpTransport, jsonFactory, null);
            b.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
                @Override
                public void initialize(JSonHttpRequest request) throws IOException {
                    DriveRequest driveRequest = (DriveRequest) request;
                    driveRequest.setPrettyPrint(true);
                    driveRequest.setKey(CLIENT ID YOU GOT WHEN SETTING UP THE CONSOLE BEFORE YOU STARTED CODING)
                    driveRequest.setOauthToken(token);
                }
            });

            final Drive drive = b.build();

            final com.google.api.services.drive.model.File body = new com.google.api.services.drive.model.File();
            body.setTitle("My Test File");
    body.setDescription("A Test File");
    body.setMimeType("text/plain");

            final FileContent mediaContent = new FileContent("text/plain", an ordinary java.io.File you'd like to upload. Make it using a FileWriter or something, that's really outside the scope of this answer.)
            new Thread(new Runnable() {
                public void run() {
                    try {
                        com.google.api.services.drive.model.File file = drive.files().insert(body, mediaContent).execute();
                        alreadyTriedAgain = false; // Global boolean to make sure you don't repeatedly try too many times when the server is down or your code is faulty... they'll block requests until the next day if you make 10 bad requests, I found.
                    } catch (IOException e) {
                        if (!alreadyTriedAgain) {
                            alreadyTriedAgain = true;
                            AccountManager am = AccountManager.get(activity);
                            am.invalidateAuthToken(am.getAccounts()[0].type, null); // Requires the permissions MANAGE_ACCOUNTS & USE_CREDENTIALS in the Manifest
                            am.getAuthToken (same as before...)
                        } else {
                            // Give up. Crash or log an error or whatever you want.
                        }
                    }
                }
            }).start();
            Intent launch = (Intent)result.getResult().get(AccountManager.KEY_INTENT);
            if (launch != null) {
                startActivityForResult(launch, 3025);
                return; // Not sure why... I wrote it here for some reason. Might not actually be necessary.
            }
        } catch (OperationCanceledException e) {
            // Handle it...
        } catch (AuthenticatorException e) {
            // Handle it...
        } catch (IOException e) {
            // Handle it...
        }
    }
}

THE ANDROID CODE - Downloading

private java.io.File downloadGFileToJFolder(Drive drive, String token, File gFile, java.io.File jFolder) throws IOException {
    if (gFile.getDownloadUrl() != null && gFile.getDownloadUrl().length() > 0 ) {
        if (jFolder == null) {
            jFolder = Environment.getExternalStorageDirectory();
            jFolder.mkdirs();
        }
        try {

            HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(gFile.getDownloadUrl());
            get.setHeader("Authorization", "Bearer " + token);
            HttpResponse response = client.execute(get);

            InputStream inputStream = response.getEntity().getContent();
            jFolder.mkdirs();
            java.io.File jFile = new java.io.File(jFolder.getAbsolutePath() + "/" + getGFileName(gFile)); // getGFileName() is my own method... it just grabs originalFilename if it exists or title if it doesn't.
            FileOutputStream fileStream = new FileOutputStream(jFile);
            byte buffer[] = new byte[1024];
            int length;
            while ((length=inputStream.read(buffer))>0) {
                fileStream.write(buffer, 0, length);
            }
            fileStream.close();
            inputStream.close();
            return jFile;
        } catch (IOException e) {        
            // Handle IOExceptions here...
            return null;
        }
    } else {
        // Handle the case where the file on Google Drive has no length here.
        return null;
    }
}

One last thing... if that intent gets sent off, you'll need to handle when it returns with a result.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 3025) {
        switch (resultCode) {
            case RESULT_OK:
                AccountManager am = AccountManager.get(activity);
                am.getAuthToken(Same as the other two times... it should work this time though, because now the user is actually logged in.)
                break;
            case RESULT_CANCELED:
                // This probably means the user refused to log in. Explain to them why they need to log in.
                break;
            default:
                // This isn't expected... maybe just log whatever code was returned.
                break;
        }
    } else {
        // Your application has other intents that it fires off besides the one for Drive's log in if it ever reaches this spot. Handle it here however you'd like.
    }
}

THE ANDROID CODE - Updating

Two quick notes on updating the last modified date of a file on Google Drive:

  1. You must provide a fully initialized DateTime. If you do not, you'll get a response of "Bad Request" from Google Drive.
  2. You must use both setModifiedDate() on the File from Google Drive and setSetModifiedDate(true) on the update request itself. (Fun name, huh? "setSet[...]", there's no way people could mistype that one...)

Here's some brief sample code showing how to do an update, including updating the file time:

public void updateGFileFromJFile(Drive drive, File gFile, java.io.File jFile) throws IOException {
    FileContent gContent = new FileContent("text/csv", jFile);
    gFile.setModifiedDate(new DateTime(false, jFile.lastModified(), 0));
    gFile = drive.files().update(gFile.getId(), gFile, gContent).setSetModifiedDate(true).execute();
}

THE MANIFEST

You'll need the following permissions: GET_ACCOUNTS, USE_CREDENTIALS, MANAGE_ACCOUNTS, INTERNET, and there's a good chance you'll need WRITE_EXTERNAL_STORAGE as well, depending on where you'd like to store the local copies of your files.

YOUR BUILD TARGET

Right click your project, go into it's properties, and under Android change the build target to Google APIs if you must. If they aren't there, download them from the android download manager.

If you're testing on an emulator, make sure its target is Google APIs, not generic Android.

You'll need a Google Account set up on your test device. The code as written will automatically use the first Google Account it finds (that's what the [0] is.) IDK if you need to have downloaded the Google Drive app for this to have worked. I was using API Level 15, I don't know how far back this code will work.

THE REST

The above should get you started and hopefully you can figure your way out from there... honestly, this is just about as far as I've gotten so far. I hope this helps A LOT of people and saves them A LOT of time. I'm fairly certain I've just written the most comprehensive set up guide to setting up an Android app to use Google Drive. Shame on Google for spreading the necessary material across at least 6 different pages that don't link to each other at all.

ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193
  • +1 for the excellent answer man. I've been struggling with accessing Google Drive via Google Play for three weeks now (I keep getting a 403 Forbidden error). I would now like to try your method instead. Would you happen to have a small working sample app which I could download to get me on the right track? I would really appreciate it. – John Roberts Nov 25 '12 at 20:18
  • 2
    Glad to hear that I could help. The app I've used this code in is covered by my company's NDA... I was given the OK to share what I have in the post (we posted a bounty to try and see if that would help speed up making the sync service work,) but I'm pretty certain a request to share enough code to have it actually compile and work would be shot down... so I'm afraid that I can't share more code than I have already. I'm happy to answer any specific questions you have. – ArtOfWarfare Nov 26 '12 at 05:44
  • Thanks for your prompt response. I understand and respect your NDA situation. I will do my best to follow your instructions above, and will let you know if I have any questions. – John Roberts Nov 26 '12 at 22:19
  • Hey man, I've run into a bit of a problem. Could you help me out?:http://stackoverflow.com/questions/13577100/google-drive-sdk-error – John Roberts Nov 27 '12 at 03:48
  • @ArtOfWarfare can you help me a little. I'm having some problem in setJsonHttpRequestInitializer, JsonHttpRequestInitializer and JSonHttpRequest that these are not resolved to a type. – Ranjit Jan 27 '14 at 12:20
  • @RanjitPati - Those should be in the libraries I mention near the start of the answer. Did you download and add all of them to your project? If you right click the identifiers that aren't resolving to types, I think Eclipse will offer to automatically add the proper import statements. – ArtOfWarfare Jan 27 '14 at 14:46
  • @ArtOfWarfare, Ranjit is right, I have the same problem, I checked libs many times and have all jar files. But version is 16.0, maybe this is problem? I do not have com.google.api.client.http.json.JsonHttpRequestInitializer , instead of it have only com.google.api.client.http.json.JsonHttpContent – ertan2002 Jan 28 '14 at 15:51
  • And instance of the Drive.Builder hasnt 'setJsonHttpRequestInitializer method'. this is api service driver name -> google-api-services-drive-v2-rev111-1.16.0-rc.jar – ertan2002 Jan 28 '14 at 15:55
  • I am use quickstart sample i have some error please see this....http://stackoverflow.com/questions/27636659/upload-file-to-google-drive-using-google-play-serivces-libwithout-supporting-ja i want to upload file to google drive using only google play services lib(without any jar files) – Dinesh R Rajput Dec 25 '14 at 09:52
  • What does this mean? __"If you're testing on an emulator, make sure its target is Google APIs, not generic Android."__ I guess understanding this will help me. Please reply. Thank you – Nabin Feb 24 '15 at 10:18
  • @Nabin: I haven't used Google Drive in over 2 years, so I wouldn't know the answer. I suggest you post a new, separate question. – ArtOfWarfare Feb 24 '15 at 17:27
  • @ArtOfWarfare please have a look at this http://stackoverflow.com/questions/28690401/cannot-find-driveid-are-you-authorized-to-view-this-file-even-if-existing-f/28694943#28694943 – Nabin Feb 25 '15 at 04:00
  • Iam integrating above upload code to my project. I put all jars needed. In jsonHttpRequestInitializer i get an issues. [GoogleClient$Builder cannot be resolved. It is indirectly referenced from required .class files] please suggest me what i have to do.... – Manoj Mar 31 '15 at 12:00
  • @Manoj: It sounds like you, Ranjit Pati, and ertan2002 all have the same problem. I'd suggest posting a new, separate question and including a link to it here (so it's easy for anyone else with the same problem to find it.) If there's a mistake in my answer, feel free to edit and correct my answer. – ArtOfWarfare Mar 31 '15 at 13:11
  • @ArtOfWarfare I post question from this link http://stackoverflow.com/questions/29383533/how-to-upload-a-file-to-googledrive-in-android – Manoj Apr 01 '15 at 05:39
  • Should I still include in the Android dependency the `google-play-services_lib`? – mr5 Aug 04 '15 at 08:46
  • @ArtOfWarfare Thanks buddy, your answer is awesome. Google should leave writing their docs up to you. ;) – Martin Pfeffer Sep 22 '15 at 22:30
  • @ArtOfWarfare This looks incredibly helpful - but before I try and create a new app using this guide - is it still current? – ChrisUK Jul 04 '19 at 18:52
  • 1
    @ChrisUK - I haven’t touched Android or Google Drive since early 2013 so I can’t say for sure, but probably not. I’d probably try the answer starting with “It’s 2015, things have changed!” – ArtOfWarfare Jul 04 '19 at 23:30
5

Check this video from Google I/O to learn how to integrate your Android app with Drive:

http://www.youtube.com/watch?v=xRGyzqD-vRg

Please be aware that what you see in the video is based on Google Play Services:

https://developers.google.com/android/google-play-services/

Community
  • 1
  • 1
Claudio Cherubino
  • 14,896
  • 1
  • 35
  • 42
  • Thank you for that video! I haven't yet watched it, but the title suggests that it'll be very useful. What are the implications of the fact that Google Play Services hasn't launched to the general public yet? For the time being, and possibly forever, the app is for internal use only... does that change anything? – ArtOfWarfare Aug 28 '12 at 17:49
  • The implications are you won't be able to use the authorization mechanism that is described in the video, but don't worry, it should launch soon. – Claudio Cherubino Aug 28 '12 at 20:26
  • Is there a sample project that I can open up and take apart in Eclipse? With the Google Play Services not yet launched, how can I test whether the application works? – ArtOfWarfare Aug 28 '12 at 20:39
  • The sample will be released when Google Play Service is launched – Claudio Cherubino Aug 28 '12 at 20:45
  • 2
    The Google Play Services are now available, the sample will be coming soon – Claudio Cherubino Sep 26 '12 at 21:18
  • I somehow knew that you'd be choosing to launch it the same day I'd finally have code that works perfectly without it. – ArtOfWarfare Sep 26 '12 at 21:42
  • Thanks for the link to the video. It provided the missing piece of information that had me struggling to get this working with the Play Services. When you are specifying scopes, you need to add the "oauth2:" to the scope, e.g. `oauth2:https://www.googleapis.com/auth/drive.file` – SoftWyer Sep 30 '12 at 10:28
  • FYI, an Android quickstart sample is available at https://developers.google.com/drive/quickstart-android – Claudio Cherubino Nov 27 '12 at 21:52
  • @ClaudioCherubino with quickstart sample i have some error please see this....http://stackoverflow.com/questions/27636659/upload-file-to-google-drive-using-google-play-serivces-libwithout-supporting-ja i want use this without any jar file please help me – Dinesh R Rajput Dec 25 '14 at 09:51
5

It's 2015, things have changed!

Get the 'Drive API for Android' with gradle:

compile 'com.google.android.gms:play-services-drive:7.8.0'

There's some new doco (although still lackluster IMO):

https://developers.google.com/drive/web/quickstart/android

And for those about to go caving...the biggest problem I encountered thus far is that there is absolutely no way of distinguishing folders that have been permanently deleted from folders that are normal...you can find them, you can create folders and files within them, only writing to the file DriveContents will always fail.

straya
  • 5,002
  • 1
  • 28
  • 35
  • I am trying to download a file from Google Drive using the Google Drive Android API in my Android app, but it just doesn't work right. Shall I use the REST API or is it something else? How do I take it from here? – Bot Oct 14 '16 at 05:59
  • @Bot I suggest you create a new question about your particular case. – straya Oct 15 '16 at 08:45
3

Take a look at Google's DrEdit Example, which has a folder called android/. Copy it, follow the readme, and it should work (works for me on an Android emulator with KitKat).

.

P.S.

Sorry for reviving this, but the new Google Drive Android API doesn't support full Drive access, only drive.file and drive.appdata authorization scopes, so if you need full access you have to go back to the good 'ol Google API's Client for Java (which the DrEdit example uses).

woojoo666
  • 7,801
  • 7
  • 45
  • 57