Я долго пытался начать работу с Google Sheets API из приложения Android. Я много гуглил, но пока не могу заставить его работать. Я надеюсь, что кто-то может дать мне подсказку, где я делаю ошибку, и, возможно, может привести рабочий пример.
SheetsApiActivity.kt:
package com.example.mypackage
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.Scope
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.sheets.v4.Sheets
import com.google.api.services.sheets.v4.SheetsScopes
import kotlinx.android.synthetic.main.activity_sheets_api.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class SheetsApiActivity : AppCompatActivity() {
private val TAG = "SheetsApi"
private val clientId = "MyClientId"
private val sheetsOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Scope(SheetsScopes.SPREADSHEETS))
.requestIdToken(clientId)
.requestEmail()
.build()
private val APPLICATION_NAME = "Test Sheet Api"
private val HTTP_TRANSPORT = com.google.api.client.http.javanet.NetHttpTransport()
private val JSON_FACTORY = JacksonFactory.getDefaultInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sheets_api)
setSupportActionBar(toolbar)
login()
}
/**
* Handles the callback from the OAuth sign in flow, executing the post sign in function
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (resultCode) {
RESULT_OK -> {
testSheetsApi()
}
else -> Log.e(TAG, "login error: $resultCode")
}
}
private fun login() {
val googleSignInClient = GoogleSignIn.getClient(this, sheetsOptions)
startActivityForResult(googleSignInClient.signInIntent, 1)
}
private fun testSheetsApi() {
val sheetId = "MySpreadSheetId"
val sheetName = "Test"
val range = "Test!A:1"
val credentials = GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.build()
val user = GoogleSignIn.getLastSignedInAccount(this)
if (user != null) {
credentials.accessToken = user.idToken
}
val service: Sheets = Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY, credentials)
.setApplicationName(APPLICATION_NAME)
.build()
CoroutineScope(Dispatchers.IO).launch {
val result = service.spreadsheets().values().get(sheetId, range).execute()
}
}
}
Моя ошибка:
2020-05-03 23: 55: 03.756 14862-14956 / com.example.mypackage E / AndroidRuntime: ИСКЛЮЧИТЕЛЬНОЕ ИСКЛЮЧЕНИЕ: DefaultDispatcher-worker-1 Процесс: com.example.mypackage, PID: 14862 com.google.api .client.googleapis. json .GoogleJsonResponseException: 401 Несанкционированный {"код": 401, "ошибки": [{"domain": "global", "location": "Authorization", "locationType": "header", "message": "Недействительные учетные данные", "причина": "authError"}], "message": "Запрос имеет недопустимые учетные данные для аутентификации. Ожидаемый токен доступа OAuth 2, логин cook ie или другие действительные учетные данные для проверки подлинности. См. https://developers.google.com/identity/sign-in/web/devconsole-project. "," Status ":" UNAUTHENTICATED "} на com.google.api.client.googleapis.services. json .AbstractGoogleJsonClientRequest.newExceptionOnError (AbstractGoogleJsonClientRequest. java: 113) на com. google.api.client.googleapis.services . * json client.http.HttpRequest.execute (HttpRequest. java: 1108) на странице com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed (AbstractGoogleClientRequest. java: 542) на странице com.google.i. googleapis.services. testSheetsApi $ 1.invokeSuspend (SheetsApiActivity.kt: 81) в kotlin .coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt: 33) в kotlinx.coroutines.DispatchedTask.run (DispatchedTask. atk. 56). coroutines.scheduling.CoroutineScheduler.runSafe ly (CoroutineScheduler.kt: 571) по адресу kotlinx.coroutines.scheduling.CoroutineScheduler $ Worker.executeTask (CoroutineScheduler.kt: 738) по адресу kotlinx.coroutines.scheduling.CoroutineScheduler $ Worker.runWorks для рабочего класса (6 человек) из CoroutSine: .scheduling.CoroutineScheduler $ Worker.run (CoroutineScheduler.kt: 665)
Мой build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.mypackage"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
// To inline the bytecode built with JVM target 1.8 into
// bytecode that is being built with JVM target 1.6. (e.g. navArgs)
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
}
repositories {
jcenter()
mavenCentral()
google()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.android.gms:play-services-fitness:18.0.0'
implementation 'com.google.android.gms:play-services-auth:18.0.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4"
implementation 'com.google.api-client:google-api-client:1.30.9'
implementation 'com.google.apis:google-api-services-sheets:v4-rev581-1.25.0'
}
MyClientId
взят из Web client (Auto-created for Google Sign-in)
в проекте приставка. Я также создал ключ api для api листов там.
Обновление:
Вместо этого я попытался использовать API Sheets с библиотекой модернизации. Но похоже у меня такая же проблема. Всякий раз, когда я хочу получить доступ к защитному листу, я получаю HTTP 401 Unauthorized
Исключение.
SiginOptions:
private val sheetsOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Scope(SheetsScopes.SPREADSHEETS))
.requestIdToken(clientId)
.requestEmail()
.build()
как получить и использовать токен:
val token = GoogleSignIn.getLastSignedInAccount(this)?.idToken
val authorization = "Bearer ${token}"
val range = SheetsApi.retrofitService.getRange(sheet, range, authorization)
Если Я пытаюсь скопировать токен из https://developers.google.com/oauthplayground, он работает нормально.
val authorization = "Bearer token-copyed-from-google-oauth-playground"
Я пробовал несколько clientIds:
- Веб-клиент (автоматически создан для входа в Google) | HTTP 401
- Android Клиент | ошибка входа
- Новый созданный веб-клиент | HTTP 401
Как настроить правильные учетные данные для моего проекта, которые могут получить доступ к API Sheets? Как получить действительные токены в моем приложении Android для авторизации вызовов API?