Search code examples
listkotlindirectorynullgoogle-drive-api

Google Drive API returns null intermittently for files().list() on the App created folder using Kotlin


I am using Google Drive API but retrieving the Files/Folder list has being elusive. I have many problems but I am addressing one of them in this post.

I have created a folder using the API in my App in Kotlin(Android API 34) and it is created successfully, I know because I connect to the Google Drive Web site and it is listed, also I can upload and download files in the same folder. Then later I need to verify the Folder exists in the Drive. In order to do that I try to retrieve the Folders list but returns null in a intermittent way. Many times it returns null but some times returns the expected values.

Here is the Gradle library I am using:

implementation 'com.google.android.gms:play-services-auth:19.0.0'
implementation 'com.google.http-client:google-http-client-gson:1.26.0'
implementation('com.google.api-client:google-api-client-android:1.26.0') {
        exclude group: 'org.apache.httpcomponents'
    }
implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0') {
        exclude group: 'org.apache.httpcomponents'
    }

Here are the Manifest permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

As per request this the code to create the mDriveService:

val googleDriveService =
                Drive.Builder(
                    AndroidHttp.newCompatibleTransport(),
                    GsonFactory(),
                    credential
                )
                    .setApplicationName("House Inventory") 
                    .build()

            // The DriveServiceHelper encapsulates all REST API and SAF functionality.
            // Its instantiation is required before handling any onClick actions.
            mDriveServiceHelper = GoogleDriveServiceHelper(googleDriveService)

Class GoogleDriveServiceHelper top code lines where mDriveService is created:

class GoogleDriveServiceHelper(driveService: Drive) {
private val mDriveService: Drive = driveService

private val FOLDER_MIME_TYPE = "application/vnd.google-apps.folder"
private val FOLDER_NAME = "Inventory_Backup"
private val FILE_MIME_TYPE = "application/zip"
var FILE_NAME = ""

The following code is used to create the folder:

private val FOLDER_MIME_TYPE = "application/vnd.google-apps.folder"
private val FOLDER_NAME = "Inventory_Backup"

fun createFolder(): Task<String> {
    val tcs = TaskCompletionSource<String>()
    val service = Executors.newFixedThreadPool(1)
    val metadata = File()
        .setParents(listOf("root"))
        .setMimeType(FOLDER_MIME_TYPE)
        .setName(FOLDER_NAME)

    service.execute {
        val googleFolder: File?
        try {
            googleFolder = mDriveService.files().create(metadata).execute()
        } catch (e: IOException) {
            e.printStackTrace()
            throw IOException("Null result when requesting Folder creation.")
        }
        val finalResult = googleFolder.id
        Handler(Looper.getMainLooper()).postDelayed({
            tcs.setResult(finalResult)
        }, 1000)
    }

    return tcs.task
    }

The following code is used to verify the Folder exist:

val isFolderPresent: Task<String>
    get() = getFolderPresent()

    fun getFolderPresent(): Task<String> {
    val tcs = TaskCompletionSource<String>()
    val service = Executors.newFixedThreadPool(1)

    service.execute {
        var result: FileList? = null
        try {
            result = mDriveService.files().list()
                .setQ("mimeType='$FOLDER_MIME_TYPE' and trashed=false")
                .execute()
            ////Here the result is null
        } catch (e: IOException) {
            e.printStackTrace()
        }

        var finalResult = ""

        if (result != null)
            for (file in result.files) {
                if (file.name == FOLDER_NAME) finalResult = file.id
            }

        Handler(Looper.getMainLooper()).postDelayed({
            tcs.setResult(finalResult)
        }, 1000)
    }
    return tcs.task
}

Solution

  • As per Andrew's question I made some code modifications.

    To make sure the Tasks are finished I rearrange the code sequence (for example):

    mDriveServiceHelper!!.isFolderPresent
                    .addOnSuccessListener { id: String ->
                        folderId = id
                        if (folderId.isNotEmpty()) {
                            //awake getFolder
                            mDriveServiceHelper!!.getFolderAccess(folderId)
                                .addOnSuccessListener { result: Boolean ->
                                    if (result) {
                                        mDriveServiceHelper!!.getFolderFileList
    
                                        //enable other button as sign-in complete
                                        signInButton!!.isEnabled = false
                                        createFolderButton!!.isEnabled = true
                                        folderFilesButton!!.isEnabled = true
                                        uploadFileButton!!.isEnabled = true
                                        signOutButton!!.isEnabled = true
                                    } else {
                                        signInButton!!.isEnabled = false
                                        signOutButton!!.isEnabled = true
                                        createFolderButton!!.isEnabled = true
                                    }
                                }
                                .addOnFailureListener { exception: Exception? ->
                                    Log.e(
                                        GoogleDriveServiceHelper.TAG,
                                        "Couldn't get permissions.",
                                        exception
                                    )
                                    signInButton!!.isEnabled = false
                                    signOutButton!!.isEnabled = true
                                    createFolderButton!!.isEnabled = true
                                }
    
                        } else {
                            signInButton!!.isEnabled = false
                            signOutButton!!.isEnabled = true
                            createFolderButton!!.isEnabled = true
                        }
                    }
                    .addOnFailureListener { exception: Exception? ->
                        Log.e(
                            GoogleDriveServiceHelper.TAG,
                            "Couldn't create file.",
                            exception
                        )
                        signInButton!!.isEnabled = false
                        signOutButton!!.isEnabled = true
                        createFolderButton!!.isEnabled = true
                    }
    

    I moved all the code inside the [addOnSuccessListener] segments. That way anything that is dependent of the Tasks completions will wait for it before doing anything.

    So far the Tasks are returning the data correctly.