🐥

Android kotlinでGmail APIに接続して受信箱のメッセージを取得する方法

6 min read

1 やりたいこと

AndroidのアプリでGmailの受信箱にあるメッセージを取りたい。

2 過去の記事と実装方針(読み飛ばしてもOKです)

過去の記事ではOAuth2.0を使って認証するときにclient idとclient secretを使った方法が多かった【参考記事(Qiita, @hsn))】。Androidのアプリはclient idのみで動かせるそうなので、それを使いたい(実際はclient idを入力するところすら無い)。
そこで、AndroidアプリでGoogleスプレッドシートを使う記事【参考記事(寝室コンピューティング, karintomania)】を参考に認証のコードを書いて、Gmail APIに応用。この記事では今では非推奨のstartActivityForResultが使われているので、この部分を【参考記事(Composeとその他のライブラリ, Android)】を参考にして推奨形式に書き直す。

3 前準備(Google Cloud Platformですること)

Google Developer Consoleで検索してアクセスしてもGCPに飛ぶと思う。
そこで、「APIとサービス」を選択し「認証情報」を選択する。そのページの「認証情報を作成」から「OAuthクライアントID」を選択する。指示に従って空欄を埋めていく。ここで、アプリケーションの種類は必ずAndroidを選択すること。
また、OAuthの同意画面も設定する。ScopeはGmailで目的に合わせたScopeを設定する。

4 動いたコード

以下に動いたコードを示します。
実装のアーキテクチャは『Jetpack ComposeによるAndroid MVVMアーキテクチャ入門 (OnDeck Books(NextPublishing)) 』(奥澤 俊樹 著)を参考にしました。
まずは認証をするViewについて。

AuthGmailView.kt
@Composable
fun AuthGmailView(mainViewModel: MainViewModel,context: Context, activity: Activity) {
   val uiState: MainViewModel.UiState by mainViewModel.uiState

   lateinit var mGoogleSignInClient: GoogleSignInClient
   lateinit var credential: GoogleAccountCredential
   val scopeGmail: String = "目的に合わせたscopeを入れてください。"

   val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestScopes(
       Scope(scopeGmail)
   ).requestEmail().build()
   mGoogleSignInClient = GoogleSignIn.getClient(activity, gso)
   credential = GoogleAccountCredential.usingOAuth2(activity, Collections.singleton(scopeGmail))

   val signInIntent: Intent = mGoogleSignInClient.signInIntent
   val test = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult(), onResult = {

       if (it.resultCode == Activity.RESULT_OK) {
           val task = GoogleSignIn.getSignedInAccountFromIntent(it.data)
           try {
               val account = task.getResult(ApiException::class.java)
               Log.i("Sign in", "Name : " + account?.displayName.toString())  //Debug用
               Log.i("Sign in", "Email : " + account?.email)  //Debug用
               mainViewModel.initialLoad(context = context)  //Gmailの中身を探すModelを呼んでいます。
           } catch (e: ApiException) {
               Log.w("Sign in fail", "signInResult: failed code = " + e.statusCode)
               mainViewModel.resultAuth()  //失敗したと表示
           }
       } else {
           mainViewModel.resultAuth()  //失敗したと表示
       }
   })
   SideEffect {
       test.launch(signInIntent)   //サインインの画面を出す。
   }
}

そして、メッセージを呼んでくるところ。ここではスニペットを表示している。

   val scopeGmail: String = "目的に合わせたscopeを入れてください。"

   override suspend fun getInboxEmails(context: Context): MessagesGmail {
       val account = GoogleSignIn.getLastSignedInAccount(context)
       val credential: GoogleAccountCredential =
           GoogleAccountCredential.usingOAuth2(context, Collections.singleton(scopeGmail))
       credential?.setSelectedAccount(account?.account) 

   withContext(Dispatchers.Default) {
           val mailService = Gmail.Builder(
               AndroidHttp.newCompatibleTransport(),
               JacksonFactory.getDefaultInstance(), credential
           ).setApplicationName("Test")
               .build()
           val messages = mailService.users().messages().list("me").execute()
           Log.i("size of messages", messages.messages.size.toString())  //Debug用に何個Messageあるかを表示
           var listMessage: MutableList<Message> = mutableListOf()
           var a = 0;
           for (message in messages.messages) {
               val mdata = mailService.users().messages().get("me", message.id).execute()
               listMessage.add(a++, mdata)
           }
           Log.d("Mutable list", listMessage[0].snippet)
           val messagesGmail = MessagesGmail(messsages = listMessage)
           return@withContext messagesGmail
       }

ここで

for (message in messages.messages) {
   val mdata = mailService.users().messages().get("me", message.id).execute()
   listMessage.add(a++, mdata)
}

これをしないと、Messageが読めないので注意。
あとは、Gmail APIのリファレンス(messages)をもとに、listMessageから欲しい情報を取得すれば良い。

おまけ

ログアウトしたいときは、以下のような関数を適当に作って呼ぶ。

fun logout() {
       val scopeGmail: String = "目的に合わせたscopeを入れてください。"

       lateinit var mGoogleSignInClient: GoogleSignInClient
       val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestScopes(
           Scope(scopeGmail)
       ).requestEmail().build()
       mGoogleSignInClient = GoogleSignIn.getClient(this, gso)
       mGoogleSignInClient.signOut()
   }

さいごに

Androidアプリ開発初心者なので、良くないコードの書き方とか、不具合あればコメントいただけると幸いです。質問なども答えられる範囲でお返ししますので、ぜひコメントください。
もしも、コメントに書けない事や質問などありましたら、akira.kashihara[at]hotmail.comまでメールをお願いします。

参考記事

ここは必ず見ておいたほうがいい記事

以下の記事は、調べたときにヒントになった記事

Discussion

ログインするとコメントできます