Integrating Google Drive in Android

See how to integrate the Google Drive SDK in order to let your users access and download their Drive files directly to your app. By Kevin D Moore.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Opening a Picked-File Dialog

You have created the methods to handle the result of signing in and picking a file, but you don’t yet have a method to initiate those actions. Create a method named pickFiles() to open the picked-file dialog:

/**
 * Prompts the user to select a text file using OpenFileActivity.
 *
 * @return Task that resolves with the selected item's ID.
 */
fun pickFiles(driveId: DriveId?) {
  val builder = OpenFileActivityOptions.Builder()
  if (config.mimeTypes != null) {
    builder.setMimeType(config.mimeTypes)
  } else {
    builder.setMimeType(documentMimeTypes)
  }
  if (config.activityTitle != null && config.activityTitle.isNotEmpty()) {
    builder.setActivityTitle(config.activityTitle)
  }
  if (driveId != null) {
    builder.setActivityStartFolder(driveId)
  }
  val openOptions = builder.build()
  pickItem(openOptions)
}

You set the mime type and title, and then set the starting folder if driveId is provided. Then call pickItem with those options.

Next add the pickItem method:

private fun pickItem(openOptions: OpenFileActivityOptions) {
  val openTask = driveClient?.newOpenFileActivityIntentSender(openOptions)
  openTask?.let {
    openTask.continueWith { task ->
      ActivityCompat.startIntentSenderForResult(activity, task.result, REQUEST_CODE_OPEN_ITEM,
          null, 0, 0, 0, null)
    }
  }
}

This will start Google Drive’s File Picker activity, which will call your onActivityResult with the user’s response.

Logging In and Out

Next, you add a method that can retrieve any account that has been signed in from previous launches:

fun checkLoginStatus() {
  val requiredScopes = HashSet<Scope>(2)
  requiredScopes.add(Drive.SCOPE_FILE)
  requiredScopes.add(Drive.SCOPE_APPFOLDER)
  signInAccount = GoogleSignIn.getLastSignedInAccount(activity)
  val containsScope = signInAccount?.grantedScopes?.containsAll(requiredScopes)
  val account = signInAccount
  if (account != null && containsScope == true) {
    initializeDriveClient(account)
  }
}

If a signed-in account is found and no scope has changed, you call initializeDriveClient() that you created earlier to handle the sign in. Add the following method to launch the Authentication dialog:

fun auth() {
  activity.startActivityForResult(googleSignInClient.signInIntent, REQUEST_CODE_SIGN_IN)
}

Finally, add a method to allow a user to log out.

fun logout() {
  googleSignInClient.signOut()
  signInAccount = null
}

Updating MainActivity

Now, you will turn your attention back to the MainActivity.
Above the onCreate() function, create a simple enum to keep track of the buttons state:

enum class ButtonState {
  LOGGED_OUT,
  LOGGED_IN
}

As mentioned earlier, the activity needs to be set as a serviceListener so that it can respond to the service. Implement the ServiceListener interface in the MainActivity:

class MainActivity : AppCompatActivity(), ServiceListener {

And add the interface methods:

override fun loggedIn() {
}

override fun fileDownloaded(file: File) {
}

override fun cancelled() {
}

override fun handleError(exception: Exception) {
}

Add properties for the service and button state:

private lateinit var googleDriveService: GoogleDriveService
private var state = ButtonState.LOGGED_OUT

You need to change the state of the buttons based on your logged-in or logged-out state. Consequently, you create a function named setButtons:

private fun setButtons() {
  when (state) {
    ButtonState.LOGGED_OUT -> {
      status.text = getString(R.string.status_logged_out)
      start.isEnabled = false
      logout.isEnabled = false
      login.isEnabled = true
    }

    else -> {
      status.text = getString(R.string.status_logged_in)
      start.isEnabled = true
      logout.isEnabled = true
      login.isEnabled = false
    }
  }
}

status, start, logout, and login are the ID of the views you created in activity_main.xml. You should be able to import them using Option+Return on macOS Alt+Enter on PC as long as you have apply plugin:'kotlin-android-extensions' in the app module build.gradle, which new projects do by default.

Update onCreate() to be:

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)

  //1
  val config = GoogleDriveConfig(
      getString(R.string.source_google_drive),
      GoogleDriveService.documentMimeTypes
  )
  googleDriveService = GoogleDriveService(this, config)

  //2
  googleDriveService.serviceListener = this

  //3
  googleDriveService.checkLoginStatus()

  //4
  login.setOnClickListener {
    googleDriveService.auth()
  }
  start.setOnClickListener {
    googleDriveService.pickFiles(null)
  }
  logout.setOnClickListener {
    googleDriveService.logout()
    state = ButtonState.LOGGED_OUT
    setButtons()
  }

  //5
  setButtons()
}

Here’s what the above does:

  1. Creates the service with your title and the document mime types.
  2. Sets MainActivity as the listener.
  3. Changes the state to logged-in if there is any logged-in account present.
  4. Sets the button click listeners. There are three buttons: Login, Pick a File and Logout.
  5. Updates views based on the current state.

Handling the OnActivityResult Method

Add the onActivityResult() method and have it pass the result to the service:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 googleDriveService.onActivityResult(requestCode, resultCode, data)
}

Now, add implementations for the listener methods:

override fun loggedIn() {
  state = ButtonState.LOGGED_IN
  setButtons()
}

override fun fileDownloaded(file: File) {
  val intent = Intent(Intent.ACTION_VIEW)
  val apkURI = FileProvider.getUriForFile(
      this,
      applicationContext.packageName + ".provider",
      file)
  val uri = Uri.fromFile(file)
  val extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
  val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
  intent.setDataAndType(apkURI, mimeType)
  intent.flags = FLAG_GRANT_READ_URI_PERMISSION
  if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
  } else {
    Snackbar.make(main_layout, R.string.not_open_file, Snackbar.LENGTH_LONG).show()
  }
}

override fun cancelled() {
  Snackbar.make(main_layout, R.string.status_user_cancelled, Snackbar.LENGTH_LONG).show()
  }

override fun handleError(exception: Exception) {
  val errorMessage = getString(R.string.status_error, exception.message)
  Snackbar.make(main_layout, errorMessage, Snackbar.LENGTH_LONG).show()
}

The code inside loggedIn(), cancelled(), and handleError() are pretty straightforward. They update the UI and/or display messages with Snackbar.

In fileDownloaded(), a file is received; subsequently, you want the system to open the file. This is where the FileProvider information you put in the AndroidManifest.xml file comes in.

In Android 8.0 Oreo and above, you can no longer open file:// url’s, so you need to provide your own FileProvider for that. You don’t need any other code than this. MimeTypeMap is a system class that has a few helper methods you can use to get the file extension and mime type from the url. You create an intent and make sure that the system can handle it before starting the activity — the app will crash otherwise.

Time to give it a try! Build and run the app.

First, try logging in:

You will first be presented with an account chooser. After you’ve chosen an account, you’ll need to give the app permissions to access your Google Drive.

Next, hit the “Start Google Drive” button, and you will see your files like this:

Once you select a file and press Select, the download process will start. After the download is complete, you should then see the file you picked automatically open in a system viewer.

Where to Go From Here?

In this tutorial, you have learned how to integrate your app with Google Drive and how to download a file. Congratulations on successfully downloading files from your Google Drive!

You can download the final project by using the download button at the top or bottom of this tutorial.

You can do much more with Google Drive. Try, for example, adding more capabilities to your app, such as creating a file or deleting a file. Check out the documentation about other Google Drive SDK features for Android.

If you have any comments or questions about this tutorial or Google Drive SDK, feel free to join the forum discussion below!