Android Google Drive API Storing App Data (Like WhatsApp Google Drive Backup & Restore)

Create, Query and Read File In App Folder

Refer to Setup Android Google Drive Api for initial setup.

The following code backup a String to a file named backup-latest.db in Google Drive App Folder.

The App Folder is a special folder that is only accessible by your application. Its content is hidden from the user and from other apps. Despite being hidden from the user, the App Folder is stored on the user's Drive and therefore uses the user's Drive storage quota. The App Folder can be used to store configuration files, temporary files, or any other types of files that belong to the user but should not be tampered with.

  • call initBackup to sign in to Google Drive and perform backup to App Folder.
  • call initRestore to sign in to Google Drive and perform restore from App Folder.
  • backupToGoogleDrive create a file in App Folder
  • restoreFromGoogleDrive perform a search/query for backup file in App Folder followed by read operation.
class BackupActivity : AppCompatActivity() {    companion object {        private const val GOOGLE_SIGNIN_BACKUP_REQUEST_CODE = 1        private const val GOOGLE_SIGNIN_RESTORE_REQUEST_CODE = 2    }    private lateinit var googleSignInClient: GoogleSignInClient    private var driveClient: DriveClient? = null    private var driveResourceClient: DriveResourceClient? = null    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {        super.onActivityResult(requestCode, resultCode, data)        if (requestCode == GOOGLE_SIGNIN_BACKUP_REQUEST_CODE || requestCode == GOOGLE_SIGNIN_RESTORE_REQUEST_CODE) {            if (resultCode == Activity.RESULT_OK) {                val googleSignInAccount = GoogleSignIn.getLastSignedInAccount(this)                driveClient = googleSignInAccount?.let { Drive.getDriveClient(this, it) }                driveResourceClient = googleSignInAccount?.let { Drive.getDriveResourceClient(this, it) }                if (requestCode == GOOGLE_SIGNIN_BACKUP_REQUEST_CODE) {                    backupToGoogleDrive()                }                else if (requestCode == GOOGLE_SIGNIN_RESTORE_REQUEST_CODE) {                    restoreFromGoogleDrive()                }            }            else {            }          }          }    private fun initBackup() {        googleSignInClient = buildGoogleSignInClient()        startActivityForResult(googleSignInClient.signInIntent, GOOGLE_SIGNIN_BACKUP_REQUEST_CODE)    }    private fun initRestore() {        googleSignInClient = buildGoogleSignInClient()        startActivityForResult(googleSignInClient.signInIntent, GOOGLE_SIGNIN_RESTORE_REQUEST_CODE)    }    private fun signOut() {        googleSignInClient.signOut()    }    private fun buildGoogleSignInClient(): GoogleSignInClient {        val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)              // .requestScopes(Drive.SCOPE_FILE)              .requestScopes(Drive.SCOPE_APPFOLDER)              .build()        return GoogleSignIn.getClient(this, signInOptions)    }    private fun backupToGoogleDrive(backupData: String) {        driveResourceClient?.let { driveResourceClient ->            val appFolderTask = driveResourceClient.appFolder            val createContentsTask = driveResourceClient.createContents()            val task = Tasks.whenAll(appFolderTask, createContentsTask)                    .continueWithTask({ task ->                        val parent = appFolderTask.result                        val contents = createContentsTask.result                        val outputStream = contents.outputStream                        val input = backupData                        val inputStream = ByteArrayInputStream(input.toByteArray(Charsets.UTF_8))                        inputStream.use { input ->                            outputStream.use { output ->                                input.copyTo(output)                            }                        }                        val changeSet = MetadataChangeSet.Builder()                                .setTitle("backup-latest.db")                                .setMimeType("application/octet-stream")                                .setStarred(true)                                .build()                        driveResourceClient.createFile(parent, changeSet, contents)                    })            task.addOnSuccessListener { driveFile ->                        // show success message                    }                    .addOnFailureListener { e ->                        Log.e(TAG, "GoogleDrive create file failed", e)                        // show failure message                    }        }    }    private fun restoreFromGoogleDrive() {        driveResourceClient?.let { driveResourceClient ->            val appFolderTask = driveResourceClient.appFolder            appFolderTask.continueWithTask { task ->                val parent = task.result                val query = Query.Builder()                        .addFilter(Filters.eq(SearchableField.TITLE, "backup-latest.db"))                        .build()                driveResourceClient.queryChildren(parent, query)            }.addOnSuccessListener { meta ->                if (meta.count > 0) {                    val fileMeta = meta.get(0)                }                else {                    // viewModel.toastMessage.value = R.string.message_no_backup_found                }            }.continueWithTask { task ->                val meta = task.result                if (meta.count == 0)   {                    return@continueWithTask null                }                val fileMeta = meta.get(0)                val file = fileMeta.driveId.asDriveFile()                driveResourceClient.openFile(file, DriveFile.MODE_READ_ONLY)            }.addOnSuccessListener { driveContents ->                val tempInputStream = driveContents.inputStream                val inputStream = tempInputStream.copyToByteArrayStream()                val outputStream = ByteArrayOutputStream()                inputStream.use { input ->                    outputStream.use { output ->                        input.copyTo(output)                    }                }                val restoreData = String(outputStream.toByteArray(), Charsets.UTF_8)                // use this to free resources?                driveResourceClient.discardContents(driveContents)            }        }    }    }

References:

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.