Как авторизовать API Google Sheets в приложении Android? - PullRequest
0 голосов
/ 04 мая 2020

Я долго пытался начать работу с 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?

...