AndroidアプリからのGoogle Drive API使用方法まとめ - (4) アプリ開発編

# 目的
- Androidアプリからの Google Drive API Ver.3 (旧REST API) 使用方法をまとめる。
- ここでは下図の手順(c)、APIアクセスするアプリの開発について説明する。
![Key_Certificate_Debug_c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2408239/f2c7ee5b-b0e4-1cee-2b64-6418a3972e4a.png)

- 全体の記事は[こちら][link0]

# 環境
- OS：Windows10 Professional 21H2
- Android Studio 2021.2.1
- アプリケーション開発言語：Java
- ブラウザ：Google Chrome 103.0.5060.66（64bit）


# Androidアプリ開発

(1) Androidアプリプロジェクトへ依存関係追加

  - プロジェクトがGoogle Drive API のリモートリポジトリに依存していることをビルドスクリプト内で宣言する。

    ビルドスクリプト内での依存関係追加については、[ビルド依存関係を追加する][link1] 参照。

  - app用のbuild.gradleのdependenciesブロックへ以下を追加する。

        dependencies {
          :
            implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0') {
                exclude group: 'org.apache.httpcomponents'
            }
          :
        }

(2) DriveServiceHelper クラスの準備

  -  Google提供のDrive用の各種サービスを提供するクラス DriveServiceHelper をgithub（[DriveServiceHelper.java][link2]）からコピーする。
  -  DriveServiceHelper にはファイルの読み込み・書込み・検索等、Google Drive内のファイルを操作する各種メソッドが用意されている。

(3) Google へのサインイン処理

  -  ここから Driveへのアクセス処理を実装する。最初にGoogle DriveへアクセスするためにGoogleアカウントへサインインする。
  -  サインイン処理を開始するクラスはどこでも良いが、アプリケーションのメインアクティビティを引数に取るメソッド（下記のGoogleSignIn#getClient）があるため、アクティビティ内が一番簡単と思われる。
  
    public class SampleActivity
    {
      private static final int REQUEST_CODE_SIGN_IN = 0;
      private static final string MY_APP_NAME = "MyAppName";
      private DriveServiceHelper mDriveServiceHelper;
      :
      public void signIn()
      {
        GoogleSignInOptions signInOptions =
          new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(Drive.SCOPE_FILE)
            .requestEmail()
            .build();
        client = GoogleSignIn.getClient(this, signInOptions);
        startActivityForResult(client.getSignInIntent(), REQUEST_CODE_SIGN_IN);
      }
      :
    }

  - 参考：[GoogleSignInOptions][link3], [GoogleSignIn][link4],  [Google Sign-In for Android][link5]

  -  サインイン処理の最後でintent取得しリクエストコード付きでActivityを開始しているが、初めてサインインする場合はここでアカウントを選択する画面が開く。
![SignIn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2408239/84509cb9-d15d-d0cc-0a10-d4d2e985f929.png)

  -  gmailアカウント未設定のAndroidであればアカウント入力画面が開き、複数のアカウントが設定されていれば複数の選択肢が表示されるので、使用するアカウントを選択または入力する、
  -  アカウントを選択または入力すると、アクティビティのonActivityResultハンドラが呼び出される。このハンドラの中でリクエストコードが上で指定したもので、かつ処理結果が成功 の場合、サインイン処理を実行する。

     
    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data)
    {
      super.onActivityResult(requestCode, resultCode, data);
      switch (requestCode) {
      case REQUEST_CODE_SIGN_IN:
        if(RESULT_OK == resultCode) {
          onSignedIn();
        }
    }

  -  サインイン処理 onSignedInメソッドは以下の通り。
  
    public void onSignedIn()
    {
      GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);  // (a)
      GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(this, Collections.singleton(DriveScopes.DRIVE_FILE));  // (b)
      credential.setSelectedAccount(account.getAccount());                      // (c)
      com.google.api.services.drive.Drive googleDriveService =
          new com.google.api.services.drive.Drive.Builder(                      // (d)
                AndroidHttp.newCompatibleTransport(),
                new GsonFactory(),
                credential)
                .setApplicationName(MY_APP_NAME)
                .build();
      mDriveServiceHelper = new DriveServiceHelper(googleDriveService);         // (e)
    }

 - 各処理の内容は以下。
  
   (a) Google SignIn APIで最後にサインインしたアカウントを取得。
   - 上記signInメソッドより前にGoogleSignIn.getLastSignedInAccountを呼び出し、返値がnullならsignInメソッドを呼び出す、という順番でも良い。

   (b) OAuth2を使って資格情報取得する。3番目の引数でスコープ指定する。
   - Google APIコンソールの認証情報で設定したスコープと整合性を取る必要があるかもしれない。
  
   (c) 資格情報にアカウントを設定。

   (d) Drive APIサービスオブジェクトを生成。

   (e) DriveServiceHelperオブジェクトを生成。以降はこのオブジェクトを通してファイル操作を行う。

 - 参考：[GoogleAccountCredential][link6], [Drive API v3][link7]

(4) GoogleDriveFileHolder クラスの準備

 - DriveServiceHelperオブジェクトで扱うファイル情報を保持するクラスを作る。
    
        public class GoogleDriveFileHolder
        {
            private String id;
            private String name;
            private DateTime modifiedTime;
            private long size;
            private DateTime createdTime;
            private Boolean starred;
            private String mimeType;

            public DateTime getCreatedTime() {
                return createdTime;
            }
            public void setCreatedTime(DateTime createdTime) {
                this.createdTime = createdTime;
            }
            public Boolean getStarred() {
                return starred;
            }
            public void setStarred(Boolean starred) {
                this.starred = starred;
            }
            public String getId() {
                return id;
            }
            public void setId(String id) {
                this.id = id;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public DateTime getModifiedTime() {
                return modifiedTime;
            }
            public void setModifiedTime(DateTime modifiedTime) {
                this.modifiedTime = modifiedTime;
            }
            public long getSize() {
                return size;
            }
            public void setSize(long size) {
                this.size = size;
            }
            public String getMimeType() {
                return mimeType;
            }
            public void setMimeType(String mimeType) {
                this.mimeType = mimeType;
            }
          }

 - Google Drive上のファイルやフォルダーは、上記GoogleDriveFileHolderクラスのメンバー変数に相当する属性データをDBのテーブルとして持ち、ファイルやフォルダーの実データは別領域で管理するイメージ。
![RemoteFileImage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2408239/9462de10-3574-85a6-f3ee-9740e6372992.png)
 - 属性データは上記クラスで定義しているものが全てではないので、必要に応じて追加する。
 - Google Driveではファイルやフォルダーも名前ではなく、システムで生成されるユニークなID（上図の"id"）で管理される。同じ名前のフォルダでもIDが異なれば複数存在できるため、既存ファイルと同じ名前でファイルを作成しても上書きされることが無い。
 - その他、名前も柔軟に決められるため ".." や "/" という名前でフォルダが作成できたりするので注意。

(5) Google Drive上のファイル操作

 - DriveServiceHelperで用意されたメソッドを呼び出し、ファイル操作を行う。例えばファイルのアップロードは以下の通り。

        mDriveServiceHelper.uploadFile(new java.io.File(getApplicationContext().getFilesDir(), "dummy.txt"), "text/plain", null)
                .addOnSuccessListener(new OnSuccessListener<GoogleDriveFileHolder>() {
                    @Override
                    public void onSuccess(GoogleDriveFileHolder googleDriveFileHolder) {
                        Gson gson = new Gson();
                        Log.d(TAG, "onSuccess: " + gson.toJson(googleDriveFileHolder));
                        // googleDriveFileHolderにアップロードしたファイルの情報が格納されている
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        Log.d(TAG, "onFailure: " + e.getMessage());
                    }
                });

 - DriveServiceHelperで用意されたメソッドはTaskオブジェクトを生成・実行する。このタスクの実行が成功したとき、失敗したときの処理をそれぞれonSuccessハンドラ・onFailureハンドラへ書く。
  

# 参考

- [AndroidアプリからのGoogle Drive API使用方法まとめ - (1) 全体編][link0]
- [ビルド依存関係を追加する][link1]
- [DriveServiceHelper.java][link2]
- [GoogleSignInOptions][link3]
- [GoogleSignIn][link4]
- [Google Sign-In for Android][link5]
- [GoogleAccountCredential][link6]
- [Drive API v3][link7]



# 変更履歴
- 2022/8/28 初版


[link0]:https://qiita.com/SolitaryFiatist/items/fa830b4828375873bcc1
[link1]:https://developer.android.com/studio/build/dependencies?hl=ja
[link2]:https://github.com/googleworkspace/android-samples/blob/master/drive/deprecation/app/src/main/java/com/google/android/gms/drive/sample/driveapimigration/DriveServiceHelper.java
[link3]:https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInOptions
[link4]:https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignIn
[link5]:https://developers.google.com/identity/sign-in/android
[link6]:https://cloud.google.com/java/docs/reference/google-api-client/latest/com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
[link7]:https://developers.google.com/resources/api-libraries/documentation/drive/v3/java/latest/com/google/api/services/drive/Drive.html