Free Android app covering all aspects of addiction, including prevention and treatment

Android apps and Google Drive: Picking files

  • Written by  Clive

Google Drive, file picking!

Google Drive file picker tutorial icon

Let us show you how easy it is to use Google Drive from within your apps.

In this tutorial we show you how to display a file picker showing the folders and files in the user’s Google Drive. The user can then select a folder or a file and some of its metadata is then displayed in the LogCat. (Have you seen our LogCat tutorial? Using Android’s Log class API to debug Android application code).

Our Google Drive file picker tutorial app

So this is what our tutorial app does:

  • Connects to the Google Play Services
  • Accesses the Google Drive API
  • Displays a file picker showing the user’s Google Drive folders and files
  • Enables the user to select a folder or a file
  • We then extract the selected item’s metadata and display it in the logCat. Here’s the metadata that we get:
    • Drive ID
    • Resource ID
    • title
    • file size
    • mime type

Google Drive file picker tutorial screenshot

The user’s Google Drive folders and files are displayed. Users can then select an item and the item’s metadata is then displayed in the logCat

Google Drive file picker tutorial logCat

Here’s the Logcat display showing the selected item’s metdata

Google Play Services

The device running your app must have Google Play Services installed. You can use isGooglePlayServicesAvailable() to check if the necessary version is installed. This method will then return a status code, such as:

  • 0 – SUCCESS
  • 1 – SERVICE_MISSING
  • 2 – SERVICE_VERSION_UPDATE_REQUIRED
  • 3 – SERVICE_DISABLED
  • 4 - SERVICE_INVALID

You’ll have to decide, based on the status code, what to do next.

You can also include the getErrorDialog() method in the onConnectionFailed() callback which is triggered when your app fails to connect to the Google Play Services. Pass the returned error code to getErrorDialog() and it will display an appropriate error dialog, like this:

Google Drive file picker tutorial error dialog

Here’s the error dialog that’s displayed if the user’s device does not have Google Play Services installed

Getting started: preparing the foundation

We’re assuming your device has Google Play Services installed and that you’ve registered your app with Google. If you haven’t already done so, you may want to check out our tutorial, Using Google Drive in your apps, where we discuss the foundations.

Our tutorial app has one activity

All the action happens in one activity which Implements two interfaces:

  • ConnectionCallbacks – an interface providing callbacks that are triggered when the client is connected to or disconnected from the Google Play Service
  • OnConnectionFailedListener – an interface providing a callback to handle connection failures

Let’s get started!

Step 1

Instantiate the GoogleApiClient in the activity’s onCreate() method.

We’ve put all the code needed to instantiate an instance of the GoogleApiClient in our buildGoogleApiClient() method.

Here’s a quick overview of what happens there:

  • We create a new Builder which we use to configure our GoogleApiClient
  • We add the Google Drive API which we’ll need to use in our app as we want to access the Google Drive
  • We add the OAuth 2.0 scope that we’ll need. Essentially this authorizes us to perform certain tasks such as reading from and writing to the Google Drive
  • We add the connection callbacks to handle connecting to and from the service
  • We add the connection failed listener to deal with when the client fails to connect to the service

Step 2

Once we’ve built our client, we’re ready to connect to the service.

We connect our client to the Google Play Services in the activity’s onStart() method.

Disconnect the client from the service in the activity’s onStop() method.

Monitoring the connection: The ConnectionCallbacks

onConnected()

This is where all the fun happens.

onConnected() is called asynchronously after a successful connection.

Here we build an IntentSender intent which we’ll use to start an Open File Activity.

The Open File Activity will list all the user’s Google Drive files and folders. They’re then able to make a selection from the list.

We specify which Drive items should be selectable by including their mime types in the setMimeType() method.

Once we’ve built the intent, we start the Open File Activity by calling startIntentSenderForResult(), passing the intent and a request code as parameters. This will identify the returned result as coming from the Open File Activity.

onConnectionSuspended()

This callback is triggered when the client is in a temporary disconnected state.

This could happen if there is a problem with the Google Play Services which runs as a background service – the system can kill it at any time.

Don’t worry, the GoogleApiClient will automatically try to reconnect the client to the service.

So what happens if we can’t connect to the service?

onConnectionFailed()

This callback is triggered if there’s an error trying to connect our client to the service.

The ConnectionResult parameter indicates what caused the error. Here are couple of examples:

  • API_UNAVAILABLE – one of the API components that you’re trying to connect to is unavailable
  • CANCELLED – the client called disconnect() to cancel the connection
  • DEVELOPER_ERROR – there’s a problem with the app’s configuration
  • INTERNAL_ERROR – an internal error occurred

There’s no way to resolve this problem

We call hasResolution() to check if startResolutionForResult() is able start an intent that the user can interact with to resolve the problem. If it can’t, then we display an error dialog.

There is a possibility of resolving the problem

If it’s a problem that the user can resolve, then we call the startResolutionForResult() method, passing a unique request code as a parameter. This will be used in the onActivityResult() method to identify where the result came from.

startResolutionForResult() starts an intent that presents a possible solution to the user, for example, a request to sign in.  Once the user signs in, our activity’s onActivityResult() callback is triggered  where we call connect() to connect to the service.

The onActivityResult() callback

Our activity’s onActivityResult() method:

  • receives the returned result from the Open File Activity which is closed when the user presses the Select button
  • also receives the result returned by the startResolutionForResult() method called in the onConnectionFailed() callback

If the request code is:

  • REQUEST_CODE_SELECT – and the resultCode is OK, then we can get the selected item’s information. First we:
    • Get the item’s DriveId which identifies the Drive resource
    • we use DriveId to get a DriveFile object ( a file in the Drive) which we use to get access to the file’s metadata
    • Calling getMetadata() gets the file’s metadata. A metadata object is returned in a PendingResult
    • We’re then able to use another PendingResult to fetch the metadata asynchronously
    • Finally we can extract the individual values (title, file size and mime type) from the Metadata object
  • REQUEST_CODE_RESOLUTION - and the resultCode is OK, means that the user has managed to resolve the connection failure and we can try and connect to the service

I hope that you found this tutorial helpful.

Here's the code for the activity:

package apps101.co.za.driveme;

import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.drive.Drive;
import com.google.android.gms.drive.DriveFile;
import com.google.android.gms.drive.DriveFolder;
import com.google.android.gms.drive.DriveId;
import com.google.android.gms.drive.DriveResource;
import com.google.android.gms.drive.Metadata;
import com.google.android.gms.drive.OpenFileActivityBuilder;

/*101apps.co.za*/
public class SelectDriveFolder extends Activity implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener{

    private static final String TAG = "drive";
    private static final int REQUEST_CODE_SELECT = 102;
    private static final int REQUEST_CODE_RESOLUTION = 103;
    private GoogleApiClient googleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

//        you can check if play services installed and up to date - returns integer value
        Log.i(TAG, "Is Google Play Services available and up to date? "
                + GooglePlayServicesUtil.isGooglePlayServicesAvailable(this));

        buildGoogleApiClient();

    }

    /*connect client to Google Play Services*/
    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "In onStart() - connecting...");
        googleApiClient.connect();
    }

    /*close connection to Google Play Services*/
    @Override
    protected void onStop() {
        super.onStop();
        if (googleApiClient != null) {
            Log.i(TAG, "In onStop() - disConnecting...");
            googleApiClient.disconnect();
        }
    }

    /*Connection callback - on successful connection*/
    @Override
    public void onConnected(Bundle bundle) {
        Log.i(TAG, "in onConnected() - we're connected, let's do the work in the background...");
//        build an intent that we'll use to start the open file activity
        IntentSender intentSender = Drive.DriveApi
                .newOpenFileActivityBuilder()
//                these mimetypes enable these folders/files types to be selected
                .setMimeType(new String[] { DriveFolder.MIME_TYPE, "text/plain", "image/png"})
                .build(googleApiClient);
        try {
            startIntentSenderForResult(
                    intentSender, REQUEST_CODE_SELECT, null, 0, 0, 0);
        } catch (IntentSender.SendIntentException e) {
            Log.i(TAG, "Unable to send intent", e);
        }
    }

    /*Connection callback - Called when the client is temporarily in a disconnected state*/
    @Override
    public void onConnectionSuspended(int i) {
        switch (i) {
            case 1:
                Log.i(TAG, "Connection suspended - Cause: " + "Service disconnected");
                break;
            case 2:
                Log.i(TAG, "Connection suspended - Cause: " + "Connection lost");
                break;
            default:
                Log.i(TAG, "Connection suspended - Cause: " + "Unknown");
                break;
        }
    }

    /*connection failed callback - Called when there was an error connecting the client to the service*/
    @Override
    public void onConnectionFailed(ConnectionResult result) {
        Log.i(TAG, "Connection failed - result: " + result.toString());
        if (!result.hasResolution()) {
//            display error dialog
            GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
            return;
        }

        try {
            Log.i(TAG, "trying to resolve the Connection failed error...");
//            tries to resolve the connection failure by trying to restart this activity
            result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
        } catch (IntentSender.SendIntentException e) {
            Log.i(TAG, "Exception while starting resolution activity", e);
        }
    }

    /*build the google api client*/
    private void buildGoogleApiClient() {
        Log.i(TAG, "Building the client");
        if (googleApiClient == null) {
            googleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(Drive.API)
                    .addScope(Drive.SCOPE_FILE)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();
        }
    }

   /* receives returned result - called by the open file activity when it's exited
   by user pressing Select. This passes the request code, result code and data back
   which is received here*/
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.i(TAG, "in onActivityResult() - triggered on pressing Select");
        switch (requestCode) {
            case REQUEST_CODE_SELECT:
                if (resultCode == RESULT_OK) {
                    /*get the selected item's ID*/
                    DriveId driveId = (DriveId) data.getParcelableExtra(
                            OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);//this extra contains the drive id of the selected file
                    Log.i(TAG, "Selected folder's ID: " + driveId.encodeToString());
                    Log.i(TAG, "Selected folder's Resource ID: " + driveId.getResourceId());

//                    selected file (can also be a folder)
                    DriveFile selectedFile = Drive.DriveApi.getFile(googleApiClient, driveId);
                    PendingResult selectedFileMetadata = selectedFile.getMetadata(googleApiClient);

//                    fetch the selected item's metadata asynchronously using a pending result
                    selectedFileMetadata.setResultCallback(new ResultCallback() {
                        @Override
                        public void onResult(DriveResource.MetadataResult metadataResult) {
//                            get the metadata out of the result
                            Metadata fileMetadata = metadataResult.getMetadata();
//                            get the details out of the metadata object
                            Log.i(TAG, "File title: " + fileMetadata.getTitle());
                            Log.i(TAG, "File size: " + fileMetadata.getFileSize());
                            Log.i(TAG, "File mime type: " + fileMetadata.getMimeType());
                        }
                    });
                }
                finish();
                break;
            case REQUEST_CODE_RESOLUTION:
                if (resultCode == RESULT_OK) {
                    Log.i(TAG, "in onActivityResult() - resolving connection, connecting...");
                    googleApiClient.connect();
                }
                break;

            default:
                super.onActivityResult(requestCode, resultCode, data);
                break;
        }
    }
}

You can download the rest of the code here download button