com.google.api.client.googleapis. json .GoogleJsonResponseException: 401 неавторизовано - PullRequest
0 голосов
/ 27 февраля 2020

У меня есть следующий код:

package com.example.bluelightlite.modules

import android.content.Context
import android.os.AsyncTask
import com.example.bluelightlite.builders.ServiceBuilder
import com.google.api.client.auth.oauth2.Credential
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.util.DateTime
import com.google.api.services.calendar.Calendar
import com.google.api.services.calendar.model.Event
import com.google.api.services.calendar.model.Events

class GoogleCalendarsServiceModule constructor(context: Context): AsyncTask<Void, Void, List<Event>>() {

    private val googleCredential: Credential
    private val httpTransport: NetHttpTransport = NetHttpTransport()
    private val googleCredentialsUtilityModule = GoogleCredentialsUtilityModule(context)

    init {
        this.googleCredential = this.googleCredentialsUtilityModule.execute(this.httpTransport).get()
    }

    private var googleCalendarService: Calendar =
        ServiceBuilder().buildGoogleCalendarService(this.googleCredential, this.httpTransport)

    /**
     * gets all calendar events
     * @return Events list
     */
    private fun getEvents(): List<Event> {
        val now = DateTime(System.currentTimeMillis())
        val events: Events = this.googleCalendarService
            .events()
            .list("Marc Freeman")
            .setMaxResults(10)
            .setOrderBy("startTime")
            .execute()

        return events.items;
    }

    override fun doInBackground(vararg params: Void?): List<Event> {
        return this.getEvents()
    }
}

Приложение просит пользователя принять разрешения на доступ к календарю, у меня есть токен авторизации, однако, когда я звоню:

GoogleCalendarsServiceModule.execute() // because it needs to run on another thread

Я получаю эту ошибку:

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    Process: com.example.bluelightlite, PID: 11410
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$4.done(AsyncTask.java:399)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
     Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
    {
      "code" : 401,
      "errors" : [ {
        "domain" : "global",
        "location" : "Authorization",
        "locationType" : "header",
        "message" : "Invalid Credentials",
        "reason" : "authError"
      } ],
      "message" : "Invalid Credentials"
    }
        at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
        at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
        at com.example.bluelightlite.modules.GoogleCalendarsServiceModule.getEvents(GoogleCalendarsServiceModule.kt:38)
        at com.example.bluelightlite.modules.GoogleCalendarsServiceModule.doInBackground(GoogleCalendarsServiceModule.kt:44)
        at com.example.bluelightlite.modules.GoogleCalendarsServiceModule.doInBackground(GoogleCalendarsServiceModule.kt:14)
        at android.os.AsyncTask$3.call(AsyncTask.java:378)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
            ... 4 more

Как передать токен доступа в мой запрос? Кажется, я не могу найти API, который позволяет мне делать это, когда я вызываю сервис GoogleCalendarService, что, между прочим, вызывает резкую критику Android.

1 Ответ

0 голосов
/ 04 марта 2020

Так что я понятия не имею, что произошло, но я очистил свой проект, и код сработал ... Он выглядит следующим образом:

class GoogleCalendarsServiceModule constructor(context: Context) {

    private val googleCredential: Credential
    private val httpTransport: NetHttpTransport = NetHttpTransport()
    private val googleCredentialsUtilityModule = GoogleCredentialsUtilityModule(context)
    private var googleCalendarService: Calendar

    init {
        this.googleCredential = this.googleCredentialsUtilityModule.execute(this.httpTransport).get()
        this.googleCalendarService = ServiceBuilder().buildGoogleCalendarService(this.googleCredential, this.httpTransport)
    }

    /**
     * gets all calendar events
     * @see - https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-i-o.html
     * @return Events list
     */
    suspend fun getCalendarEvents(): List<Event> = withContext(Dispatchers.IO) {
        val now = DateTime(System.currentTimeMillis())
        val events: Calendar.Events? = googleCalendarService.events()

        val allEvents = events!!.list("primary")
        .setMaxResults(10)
            .setTimeMin(now)
            .setOrderBy("startTime")
            .setSingleEvents(true)
            .execute()

        allEvents.items
    }

    suspend fun getCalendarEventsForRoom(roomName: String): List<Event> = withContext(Dispatchers.IO) {
        val now = DateTime(System.currentTimeMillis())
        val events: Calendar.Events? = googleCalendarService.events()

        val allEventsForRoom = events!!.list("primary")
            .setMaxResults(10)
            .setTimeMin(now)
            .setOrderBy("startTime")
            .setSingleEvents(true)
            .execute()

        allEventsForRoom.items.distinctBy { x -> x.location == roomName }
    }
}

Модуль учетных данных, который устанавливает и поддерживает учетные данные Google 0Auth. Включая настройку папки токенов, если она отсутствует.

class GoogleCredentialsUtilityModule constructor(private val context: Context): AsyncTask<NetHttpTransport, Void, Credential>() {

    /**
     * Starts getting credentials on a different thread by implementing
     * AsyncTask<>()
     * @param params: a list of parameters
     * @return none
     */
    override fun doInBackground(vararg params: NetHttpTransport): Credential {
        return this.getCredentials(params[0])
    }

    /**
     * Creates an authorized Credential object.
     * @param HTTP_TRANSPORT The network HTTP Transport.
     * @return An authorized Credential object.
     * @throws java.io.IOException If the credentials.json file cannot be found.
     */
    private fun getCredentials(HTTP_TRANSPORT: NetHttpTransport): Credential {

        val inputStream: InputStream = getCredentialsAsInputStream()
        val clientSecrets: GoogleClientSecrets = GoogleClientSecrets.load(JSON_FACTORY, InputStreamReader(inputStream))

        createTokenFolderIfMissing()

        val authorisationFlow: GoogleAuthorizationCodeFlow = getAuthorisationFlow(HTTP_TRANSPORT, clientSecrets)
        val localServerReceiver: LocalServerReceiver = LocalServerReceiver.Builder().setPort(43783).build()

        val ab: AuthorizationCodeInstalledApp =
            object : AuthorizationCodeInstalledApp(authorisationFlow, localServerReceiver) {
                @Throws(IOException::class)
                override fun onAuthorization(authorizationUrl: AuthorizationCodeRequestUrl) {
                    val url = authorizationUrl.build()
                    val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
                    context.startActivity(browserIntent)
                }
            }

        return ab.authorize("user")
    }

    /**
     * Gets the /credentials.json file
     * @return InputStream
     */
    private fun getCredentialsAsInputStream(): InputStream {
        return this.javaClass.getResourceAsStream(CREDENTIALS_FILE_PATH)
            ?: throw FileNotFoundException("Resource Not found: $CREDENTIALS_FILE_PATH")
    }

    /**
     * Creates the Tokens Folder for Google Authentication
     * Uses the current context for the folder path from
     * Context.getExternalFilesDir()
     */
    private fun createTokenFolderIfMissing() {
        val tokenFolder = getTokenFolder()
        if (!tokenFolder.exists()) {
            tokenFolder.mkdir()
        }
    }

    /**
     * gets External storage directory from the
     * current context
     * @return File
     */
    private fun getTokenFolder(): File {
        return File(this.context.getExternalFilesDir("")?.absolutePath + TOKENS_DIRECTORY_PATH)
    }

    /**
     * Gets authorisation flow so that the application can authenticate into Google Calendars
     * @param HTTP_TRANSPORT allows the app to use HTTP connections
     * @param clientSecrets secrets for authentication into Google
     * @return GoogleAuthorizationCodeFlow
     */
    private fun getAuthorisationFlow(HTTP_TRANSPORT: NetHttpTransport, clientSecrets: GoogleClientSecrets): GoogleAuthorizationCodeFlow {
        return GoogleAuthorizationCodeFlow.Builder(
            HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
            .setDataStoreFactory(FileDataStoreFactory(getTokenFolder()))
            .setAccessType("offline")
            .build()
    }
}

И, наконец, вызов службы с использованием сопрограммы:

class MainActivity : AppCompatActivity(), CoroutineScope, IMeetingRoomDelegate {
    private var job: Job = Job()
    override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + job

    @Inject
    lateinit var permissionsUtilityModule: PermissionsUtilityModule

    private lateinit var googleCalendarsServiceModule: GoogleCalendarsServiceModule
    private lateinit var meetingRooms: MeetingRooms

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DaggerUtilityModuleComponent.create().inject(this)

        this.permissionsUtilityModule.checkUserPermissions(this)
        this.googleCalendarsServiceModule = GoogleCalendarsServiceModule(this);

        launch {
            val events: List<Event> = googleCalendarsServiceModule.getCalendarEvents()
            meetingRooms = setMeetingRooms(events)
            setMeetingRoomFragments(meetingRooms)
        }

        setContentView(R.layout.activity_main)
    }

    override fun onDestroy() {
        super.onDestroy()
        this.job.cancel()
    }

    override fun onMeetingRoomFragmentClicked(fragment: MeetingRoomFragment, meetingRoom: MeetingRoom) {
        val intent = Intent(this, ViewMeetingRoomDataActivity::class.java)
        intent.putExtra("meetingRoomData", meetingRoom)

        startActivity(intent)
    }

    private fun setMeetingRooms(events: List<Event>): MeetingRooms {
        val mappedEvents: List<MeetingEvent> = mapMeetingEvents(events)
        val meetingEvents = MeetingEvents(mappedEvents)

        return MeetingRoomFactory().getRooms(meetingEvents)
    }

    private fun setMeetingRoomFragments(mappedMeetingRooms: MeetingRooms) {
        for(meetingRoom: MeetingRoom in mappedMeetingRooms.getMeetingRooms()) {
            val bundle = Bundle()
            val meetingRoomFragment = MeetingRoomFragment()

            bundle.putParcelable("meetingRoomData", meetingRoom)
            meetingRoomFragment.arguments = bundle

            supportFragmentManager.beginTransaction()
                .add(R.id.root_container, meetingRoomFragment)
                .commitAllowingStateLoss()
        }
    }
}

Мысль, которую я опубликую, если кому-то понадобится помощь с весь 0Auth rigmarole.

...