Setup Android Google Drive REST API (Java/Kotlin)

April 6, 2019

NOTE: Google Drive REST API is the replacement for the deprecated Google Drive Android API.

Dependencies

Setup depedencies in app module’s build.gradle.

dependencies {
    implementation 'com.google.android.gms:play-services-auth:16.0.1'
    implementation('com.google.api-client:google-api-client-android:1.23.0') {
        exclude group: 'org.apache.httpcomponents'
        exclude module: 'guava-jdk5'
    }
    // https://mvnrepository.com/artifact/com.google.apis/google-api-services-drive
    implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0') {
        exclude group: 'org.apache.httpcomponents'
        exclude module: 'guava-jdk5'
    }
}

Google Cloud Platform Project and Enable API

You need to create a new or use existing Google Cloud Platform project.

Enable Google Drive API in API Library.

Make sure Credential - OAuth 2.0 client IDs contain an entry with

Sometimes this entry is auto created by Google Service. If the entry does not exist, you need to create an entry.

NOTE: Refer to Connecting and Authorizing the Google Drive Android API.

Code

The following sample will

  • Sign-in to request Google Drive permission
  • List all google sheets file
  • Utilize kotlin coroutines to launch job in background thread
  • User Timber to log output
@ExperimentalCoroutinesApi
class GoogleDriveListActivity : AppCompatActivity(), CoroutineScope by MainScope() {

    companion object {
        private const val REQUEST_SIGN_IN = 1
    }

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

        requestSignIn()
    }

    override fun onDestroy() {
        super.onDestroy()
        cancel()
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        Timber.d("onActivityResult=$requestCode")
        when(requestCode) {
            REQUEST_SIGN_IN -> {
                if (resultCode == RESULT_OK && data != null) {
                    handleSignInResult(data)
                }
                else {
                    Timber.d("Signin request failed")
                }
            }
        }

        super.onActivityResult(requestCode, resultCode, data)
    }

    private fun buildGoogleSignInClient(): GoogleSignInClient {
        val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            // .requestScopes(Drive.SCOPE_FILE)
            // .requestScopes(Scope(DriveScopes.DRIVE_FILE))
             .requestScopes(Scope(DriveScopes.DRIVE))
            .build()
        return GoogleSignIn.getClient(this, signInOptions)
    }

    private fun handleSignInResult(result: Intent) {
        GoogleSignIn.getSignedInAccountFromIntent(result)
            .addOnSuccessListener { googleAccount ->
                Timber.d("Signin successful")

                // Use the authenticated account to sign in to the Drive service.
                val credential = GoogleAccountCredential.usingOAuth2(
                    this, listOf(DriveScopes.DRIVE_FILE)
                )
                credential.selectedAccount = googleAccount.account
                val googleDriveService = Drive.Builder(
                    AndroidHttp.newCompatibleTransport(),
                    JacksonFactory.getDefaultInstance(),
                    credential)
                    .setApplicationName(getString(R.string.app_name))
                    .build()

                // https://developers.google.com/drive/api/v3/search-files
                // https://developers.google.com/drive/api/v3/search-parameters
                // https://developers.google.com/drive/api/v3/mime-types

                launch(Dispatchers.Default) {
                    var pageToken: String? = null
                    do {
                        val result = googleDriveService.files().list().apply {
                            q = "mimeType='application/vnd.google-apps.spreadsheet'"
                            spaces = "drive"
                            fields = "nextPageToken, files(id, name)"
                            this.pageToken = pageToken
                        }.execute()
                        for (file in result.files) {
                            Timber.d("name=${file.name}, id=${file.id}")
                        }
                    } while (pageToken != null)
                }
            }
            .addOnFailureListener { e ->
                Timber.e(e, "Signin error")
            }
    }

    private fun requestSignIn() {
        val client = buildGoogleSignInClient()
        startActivityForResult(client.signInIntent, REQUEST_SIGN_IN)
    }
}

NOTE: If you bump into error Unresolved references: DriveScopes, make sure you are not using the deprecated com.google.android.gms:play-services-drive. Use com.google.apis:google-api-services-drive instead.

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.