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
}
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.