AndroidアプリからGoogle Driveにファイルをバックアップする

6 min read読了の目安(約6200字

この記事では、デベロッパーが作成したAndroidアプリからGoogle Driveにファイルをバックアップする機能の実装を解説していきます。

Google Cloud PlatformでGoogle Drive APIを有効化

Google Cloud Platformにログインして、Google Drive APIを有効化します。まだ、プロジェクトを作成していない場合は、作成してください。

左側のメニューから「APIとサービス>ダッシュボード」を選択します。

少しスクロールするとGoogle Drive APIのタブが出てきます。

OAuth同意画面の作成

左側のメニューから「OAuth同意画面」をクリックすると、設定画面が表示されるので色々と入力していきます。

スコープの画面で、「スコープを追加または削除」をクリックします。

../auth/driveに✓を入れます。

なお、../auth/driveのような機密性の高いスコープを設定した状態で本番環境にする場合は、Googleからの審査が必要です。その際、アプリケーションのホームページやプライバシーポリシーのページをの用意が必須です。一方、非機密のスコープのみ使用する場合は審査が不要です。アプリ独自の設定データや特定のファイルのみ参照・編集する場合は.../auth/drive.appdata../auth/drive.fileのほうが審査の必要がないためお勧めです。

認証情報の作成

左側のメニューから認証情報をクリックし、上部の「認証情報を作成>OAuthクライアントID」を選択します。

ここでも各種情報を入力します。

SHA-1 証明書フィンガープリントは、以下のコマンドで確認することが出来ます。
keytool -list -keystore YOUR_KEYSTORE_PATH -v

Androidアプリへの実装

まず、moduleレベルのbuild.gradleに関連ライブラリを導入します。

dependencies {
	...
	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-rev173-1.25.0') {
		exclude group: 'org.apache.httpcomponents'
	}
	...
}

ログイン

ログイン用にGoogleSinginClient変数、Google Driveを操作するためにDrive変数を定義します。

private var googleSigninClient: GoogleSigninClient? = null
private var drive: Drive? = null

Googleへのログイン処理は以下の通りです。

private val driveContent =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == Activity.RESULT_OK && it.data != null) {
                //ログイン成功
		connectDrive(it.data!!)
            } else {
                //ログイン失敗orキャンセル
            }
        }

private fun loginToGoogle() {
	val googleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(Scope(DriveScopes.DRIVE_FILE))
            .requestEmail()
            .build()
	googleSignInClient = GoogleSignIn.getClient(activity, googleSignInOptions)
        val intent = googleSignInClient.signInIntent
        googleSignInClient?.apply {
            driveContent.launch(intent)
        }
}

ログイン処理が終わると、registerForActivityResultに返って来るので処理します。ログイン成功後、Google Driveにアクセスするための準備を行います。

private fun connectDrive(intent: Intent) {
	GoogleSignIn.getSignedInAccountFromIntent(intent)
		.addOnSuccessListener {
			val credential = GoogleAccountCredential.usingOAuth2(
			    context, Collections.singleton(DriveScopes.DRIVE_FILE)
			)
			credential.selectedAccount = it.account
			drive = Drive.Builder(
			    AndroidHttp.newCompatibleTransport(),
			    GsonFactory(),
			    credential
			).setApplicationName(getString(R.string.app_name)).build()
		}
		.addOnFailureListener {
			Log.w("Sign in failed", it.toString())
		}
}

Google Driveへの処理

ファイルIDの生成

新しくGoogle Driveにファイルを作成する場合、ファイルIDを生成する必要があります。

fun createDriveIdTask(
	drive: Drive,
        parents: List<String>,
        mimeType: String,
        fileName: String
    ): Task<String> {
        val taskCompletionSource = TaskCompletionSource<String>()
        val metadata = File()
            .setParents(parents)
            .setMimeType(mimeType)
            .setName(fileName)
        val googleFile =
            drive.files().create(metadata).execute() ?: throw IOException("Result is null")
	taskCompletionSource.setResult(googleFile.id)
        return taskCompletionSource.task
}

TaskCompletionSourceTaskベースのAPIを作成する機能を提供してくれるクラスです。非同期処理から返されたTaskに結果や例外を設定して使用します。

ファイルの保存・更新

ファイルIDを指定して、保存および更新を行います。

fun saveFile(
        drive: Drive,
        fileId: String,
        fileName: String,
        contentType: String,
        data: ByteArray
    ): Task<Unit> {
        val taskCompletionSource = TaskCompletionSource<Unit>()
        val metadata = File().setName(fileName)
        val contentStream = ByteArrayContent(contentType, data)
        val coroutineScope = CoroutineScope(Job() + Dispatchers.IO)
        coroutineScope.launch {
            drive.files().update(fileId, metadata, contentStream).execute()
            taskCompletionSource.setResult(null)
        }
        return taskCompletionSource.task 
}

これらを実際に使ってGoogle Driveにファイルをバックアップする例が以下の関数です。

fun backupToDrive(drive: Drive, fileName: String, mimeType: String, fileContent: ByteArray) {
        val coroutineScope = CoroutineScope(Job() + Dispatchers.IO)
        coroutineScope.launch {
            createDriveIdTask(
                drive,
                listOf("root"),
                mimeType,
                fileName
            ).addOnSuccessListener {
		//ファイルIDの生成成功
                val fileId = it
                saveFile(
                    drive,
                    fileId,
                    fileName,
                    mimeType,
                    fileContent
                ).addOnSuccessListener {
                    //バックアップ成功
		    Log.d("Success", "Backup succeeded")
                }.addOnFailureListener { exception ->
                    //バックアップ失敗
                    Log.w("Failure", exception.toString())
                }
            }.addOnFailureListener { exception ->
                //ファイルIDの生成失敗
                Log.w("Failure", exception.toString())
            }
        }
}