androidアプリからGoogle API(Spreadsheet)にアクセスする

目次

  1. 背景
    1. Google APIにandroidアプリで使ってみる
  2. Google APIの設定
    1. SHA-1ハッシュの取得
    2. APIの設定
    3. Spreadsheet APIの追加
  3. build.gradleで依存性の設定
  4. Manifestoファイル
  5. サインインボタンを作る
  6. Googleにサインイン
  7. 読み込み
  8. 書き込み
  9. サインアウト
  10. まとめ
  11. ところで…
  12. 関連記事

背景

こんにちは。 かりんとうマニア(@karintozuki)です。

今日はだいぶニッチですが、Googleが提供しているSpreadsheetのAPIに
androidアプリからアクセスする方法を紹介します。

Google APIにandroidアプリで使ってみる

GoogleはMapやGmailなど様々なサービスを提供していますが、
それぞれにAPIがあったりします。

今回は例としてGoogle DocumentのSpreadsheetにアクセスしますが、
もちろん他のサービスのAPIに対しても使えます。

これらのAPIをうまく使うことで
面白いアプリが作れるかもしれません。

Google APIの設定

まずはSpreadsheetにアクセスするためのGoogle APIを用意します。

SHA-1ハッシュの取得

APIを利用するに当たって、SHA-1ハッシュというものが必要になります。

Android Studioからterminalを開きます。
以下のコマンドでハッシュを表示します。

1
2
keytool -list -v \
-alias androiddebugkey -keystore ~/.android/debug.keystore

パスワードを聞かれますが、デフォルトのパスワードはandroidです。
表示されたSHA1を控えておいてください。

またGradleのタスクからも呼び出せます。
個人的にはこっちのが簡単です。
./gradlew signingReport

その場合は複数表示される中からVariant: debugとなっているものを使ってください。

APIの設定

Google API ConsoleからAPI用のプロジェクトを作成します。

https://developers.google.com/identity/sign-in/android/start-integrating
ここのページの真ん中くらいから青いボタンを探してください。

プロジェクトを新規作成します。

プロジェクト名を入力します。

次にプロダクト名を聞かれます。

ここで指定した名前が
アプリ側でユーザが権限を承認するときに表示されます。

タイプはAndroidを指定します。

パッケージ名とSHA1を入力します。

次にGoogleSpreadsheetにアクセスするために
もう少し設定があるので頑張りましょう。

Spreadsheet APIの追加

以下のサイトにアクセスします。
https://console.developers.google.com/apis

先ほど作ったプロジェクトのダッシュボードを開きます。
左側のメニューからライブラリを開きます。

Spreadsheetと検索するとお目当てのAPIが出てきます。

有効にするボタンを押して有効化しましょう。

これでAPI側の準備は完了です。

ここからはソースをいじっていきます。

build.gradleで依存性の設定

appレベルのbuild.gradleに以下の依存性を追加します。

API以外にもCoroutineを追加しておきます。

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
dependencies {

//spreadsheet
implementation 'com.google.apis:google-api-services-sheets:v4-rev516-1.23.0'
implementation 'com.google.api-client:google-api-client-android:1.23.0'
implementation 'com.google.android.gms:play-services-auth:19.0.0'

// coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'


}

Manifestoファイル

インターネット利用の権限を追加してください。

AndroidManifest.xml
1
2
3
4
5
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bedroomcomputing.googlespreadsheettest">
<!--中略-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

サインインボタンを作る

サインイン用のボタンを作ります。

他にも先を見据えて読み取り・書き込み・サインアウトボタンを作っておきます。

activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<com.google.android.gms.common.SignInButton
android:id="@+id/sign_in_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Read"
app:layout_constraintBottom_toTopOf="@+id/button_write"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sign_in_button" />

<Button
android:id="@+id/button_write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Write"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sign_in_button" />

<Button
android:id="@+id/button_signout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SignOut"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_write" />
</androidx.constraintlayout.widget.ConstraintLayout>

Googleにサインイン

MainActivityにサインイン処理を書いていきます。
コメントを多めに入れたので、細かい処理は説明しません笑

MainActivity.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class MainActivity : AppCompatActivity() {

lateinit var mGoogleSignInClient: GoogleSignInClient
lateinit var credential: GoogleAccountCredential

// サインイン用intentを識別するためのID。0であることに意味はない
val RC_SIGN_IN = 0

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// サインインのオプションを設定。Emailの取得とspreadsheetのアクセスを要求する
val gso= GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Scope("https://www.googleapis.com/auth/spreadsheets"))
.requestEmail()
.build()

mGoogleSignInClient = GoogleSignIn.getClient(this, gso)
credential = GoogleAccountCredential.usingOAuth2(this, Collections.singleton("https://www.googleapis.com/auth/spreadsheets"))

// もし前回起動時にサインインしていたら、サインイン不要
val account = GoogleSignIn.getLastSignedInAccount(this)
account?.let{
Log.i("Main", "${account.displayName}")
credential?.setSelectedAccount(account.account)
}

// サインイン
findViewById<SignInButton>(R.id.sign_in_button).setOnClickListener{
signIn()
}

}

private fun signIn() {
// サインイン用のインテントを呼び出す。onActivityResultに戻ってくる
val signInIntent: Intent = mGoogleSignInClient.getSignInIntent()
startActivityForResult(signInIntent, RC_SIGN_IN)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

// サインイン完了時の処理
if (requestCode == RC_SIGN_IN) {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
handleSignInResult(task)
}
}

private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
try {
val account = completedTask.getResult(ApiException::class.java)

Log.i("Main", "${account?.displayName}")
credential?.setSelectedAccount(account?.account)

} catch (e: ApiException) {
Log.w("Main", "signInResult:failed code=" + e.statusCode)

}
}

}

サインインボタンを押すとログイン用のダイアログが開きます。

読み込み

それでは読み込み処理を作っていきます。

onCreateメソッド内でクリックリスナーを設定します。
ネットワーク通信を行うので、coroutineを使用します。

MainActivity.kt
1
2
3
4
5
6
7
8
// 読み込み。ネットワーク通信なので、coroutine内で行う。
findViewById<Button>(R.id.button_read).setOnClickListener{
MainScope().launch{
withContext(Dispatchers.Default){
read()
}
}
}

そしてreadメソッドを追加します。

MainActivity.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fun read(){
val sheetsService = Sheets.Builder(AndroidHttp.newCompatibleTransport(), JacksonFactory.getDefaultInstance(), credential)
// アプリケーション名を指定するのだが、適当でいいっぽい
.setApplicationName("Test Project2")
.build();

// 値取得
val response = sheetsService
.spreadsheets().values()
// Spreadsheet idはURLのhttps://docs.google.com/spreadsheets/d/xxxx/...のxxx部分
.get("1XoRcqhbAkhYB8zh_k4_VTh3_V6TrJLeTz9NfW5mTY_8", "Sheet1!A1")
.execute()

val values = response.getValues()

// Stringにキャストする
val a1 = values[0][0] as String

Log.i("MainActivity", a1)
}

読み込めたでしょうか?

書き込み

書き込みも同様です。

クリックリスナーを指定します。

MainActivity.kt
1
2
3
4
5
6
7
8
// 書き込み。ネットワーク通信なので、coroutine内で行う。
findViewById<Button>(R.id.button_write).setOnClickListener{
MainScope().launch{
withContext(Dispatchers.Default){
write()
}
}
}

関数を追加します。

MainActivity.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 fun write(){
val sheetsService = Sheets.Builder(AndroidHttp.newCompatibleTransport(), JacksonFactory.getDefaultInstance(), credential)
// アプリケーション名を指定するのだが、適当でいいっぽい
.setApplicationName("Test Project2")
.build();

// 二次元配列で書き込む値を保持
val values: List<List<Any>> = Arrays.asList(
Arrays.asList("A","B") ,
Arrays.asList("C","D") // Additional rows ...
)
val body = ValueRange().setValues(values)

// 書き込み。
val result: AppendValuesResponse =
sheetsService.spreadsheets().values()
// appendは一番下の行に追加していってくれる
.append("1XoRcqhbAkhYB8zh_k4_VTh3_V6TrJLeTz9NfW5mTY_8", "Sheet1!A:B", body)
// RAWを指定すると値がそのまま表示される。USER_ENTEREDだと数字や日付の書式が手入力時と同じになるらしい
.setValueInputOption("RAW")
.execute()

}

うまく動くとこんな感じでボタンを押すとSpreadsheetに
データが書き込まれます。

サインアウト

ついでにサインアウトの機能をつけておきます。

クリックリスナー追加

MainActivity.kt
1
2
3
4
// サインアウト
findViewById<Button>(R.id.button_signout).setOnClickListener{
signOut()
}

関数を追加します。

MainActivity.kt
1
2
3
4
5
private fun signOut() {
mGoogleSignInClient.signOut()
.addOnCompleteListener(this) {
}
}

これは簡単ですね。

まとめ

今日はGoogle Spreadsheetへのアクセスを例に
Google APIにアクセスする方法を紹介しました。

GoogleAPIを利用したアプリを作成するときは参考にしてみてください。
ちなみに今回作成したプロジェクトはGitHubからダウンロードできます。
https://github.com/karintomania/GoogleSpreadsheetTest

それじゃ今日はこの辺で。

ところで…

仕事で扱っている技術がレガシーだったり、同じことの繰り返しだったりで
最近、成長してないと感じてませんか?

転職することで、もっと成長できるかもしれません。

いますぐ転職しない人でも、とりあえずエージェントに登録しておいて
案件や年収を眺めるだけでも市場の需要を知ることができ、勉強になります。

ここでエンジニアに人気の転職サイトを紹介します。

レバテックキャリア
エンジニアとして働いていて実務経験があるなら、
求人数の充実具合からレバテックキャリアがおすすめです。
IT転職ではデファクト・スタンダードですね。
▼レバテック キャリア 登録はこちら▼


Tech Clips
Tech Clipsは年収500万以上&自社サービスを持った会社に特化した求人サイトです。
首都圏限定になってはしまいますが、
収入を増やしたい、自社サービスを持った企業への転職をしたい人におすすめです。

▼Tech Clips 登録はこちら▼


関連記事

こちらの記事もおすすめです。

アプリ未経験エンジニアが独学で個人開発Androidアプリを公開するまでにやったこと