Day25 Plugin 从零开始到上架 - Android instagram APIs

GraphInstagramService:

interface GraphInstagramService {

    @GET("access_token")
    suspend fun getLongAccessTokenInfo(
        @Query("grant_type") grantType: String,
        @Query("client_secret") clientSecret: String,
        @Query("access_token") accessToken: String
    ): LongAccessTokenInfo

    @GET("me")
    suspend fun getUserInfo(
        @Query("fields") fields: String,
        @Query("access_token") accessToken: String
    ): UserInfoResponse

    @GET("me/media")
    suspend fun getMedias(
        @Query("fields") fields: String,
        @Query("access_token") accessToken: String
    ): MediasResponse

    @GET("{albumId}/children")
    suspend fun getAlbumDetail(
        @Path("albumId") albumId: String,
        @Query("fields") fields: String,
        @Query("access_token") accessToken: String
    ): AlbumDetailResponse

    @GET("{mediaId}")
    suspend fun getMediaItem(
        @Path("mediaId") mediaId: String,
        @Query("fields") fields: String,
        @Query("access_token") accessToken: String
    ): Response<Map<String, Any>>
}

DataRepository:


class DataRepository(
    context: Context,
    private val apiInstagramService: ApiInstagramService,
    private val graphInstagramService: GraphInstagramService
) {
    private val preference = SharedPreferencesManagerImpl(context)

    private val TAG = javaClass.name
    private val _accessTokenResult = MutableLiveData<Boolean?>(null)
    val accessTokenResult: LiveData<Boolean?> = _accessTokenResult

    suspend fun getUserInfo(): UserInfoResponse {
        if (preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "").isNullOrEmpty()) {
            throw Exception("TOKEN_EMPTY")
        }
        if (!isTokenValid()) {
            throw Exception("TOKEN_EXPIRED")
        }
        return withContext(Dispatchers.IO) {
            return@withContext graphInstagramService.getUserInfo(
                fields = "id,username,account_type",
                accessToken = preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "")!!
            )
        }
    }

    suspend fun getMedias(): List<Map<String,Any>> {
        if (preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "").isNullOrEmpty()) {
            throw Exception("TOKEN_EMPTY")
        }
        if (!isTokenValid()) {
            throw Exception("TOKEN_EXPIRED")
        }
        return withContext(Dispatchers.IO) {
            return@withContext graphInstagramService.getMedias(
                fields = "id,caption,media_type,timestamp,permalink,media_url,thumbnail_url",
                accessToken = preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "")!!
            ).data
        }
    }

    suspend fun getAlbumDetail(albumId: String): List<Map<String, Any>> {
        if (preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "").isNullOrEmpty()) {
            throw Exception("TOKEN_EMPTY")
        }
        if (!isTokenValid()) {
            throw Exception("TOKEN_EXPIRED")
        }
        return withContext(Dispatchers.IO) {
            return@withContext graphInstagramService.getAlbumDetail(
                albumId = albumId,
                fields = "id,media_type,media_url,timestamp,thumbnail_url",
                accessToken = preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "")!!
            ).data
        }
    }

    suspend fun getMediaItem(mediaId: String): Map<String, Any> {
        if (preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "").isNullOrEmpty()) {
            throw Exception("TOKEN_EMPTY")
        }
        if (!isTokenValid()) {
            throw Exception("TOKEN_EXPIRED")
        }
        return withContext(Dispatchers.IO) {
            return@withContext graphInstagramService.getMediaItem(
                mediaId = mediaId,
                fields = "id,caption,media_type,timestamp,permalink,media_url,thumbnail_url",
                accessToken = preference.getString(Constants.PREF_KEY_ACCESS_TOKEN, "")!!
            ).body() ?: throw Exception("UNKNOWN_EXCEPTION")
        }
    }

    suspend fun logout(): UserInfoResponse {
        return withContext(Dispatchers.IO) {
            preference.clear()
            return@withContext UserInfoResponse("","","")
        }
    }

    suspend fun getAccessToken(
        clientId: String,
        clientSecret: String,
        code: String,
        redirectUri: String
    ) {
        withContext(Dispatchers.IO) {
            getShortAccessToken(
                clientId,
                clientSecret,
                code,
                redirectUri
            )
        }
    }

    private suspend fun getShortAccessToken(
        clientId: String,
        clientSecret: String,
        code: String,
        redirectUri: String
    ) {
        try {
            val shortAccessTokenInfo = apiInstagramService.getShortAccessTokenInfo(
                clientId = clientId,
                clientSecret = clientSecret,
                code = code,
                grantType = "authorization_code",
                redirectUri = redirectUri
            )

            Log.d(TAG, "shortAccessTokenInfo = $shortAccessTokenInfo")

            preference.set(Constants.PREF_KEY_INSTAGRAM_USER_ID, shortAccessTokenInfo.userId)

            val currentTimeMillis: Long = System.currentTimeMillis()

            getLongAccessToken(shortAccessTokenInfo.accessToken, clientSecret, currentTimeMillis)
        } catch (exception: UnknownHostException) { // Request Api when no internet
            exception.printStackTrace()
            Log.e(TAG, "shortAccessTokenInfo exception = $exception")
            _accessTokenResult.postValue(false)
        } catch (exception: Exception) {
            exception.printStackTrace()
            Log.e(TAG, "shortAccessTokenInfo exception = $exception")
            _accessTokenResult.postValue(false)
        }
    }

    private suspend fun getLongAccessToken(
        shortAccessToken: String,
        clientSecret: String,
        currentTimeMillis: Long
    ) {
        try {
            val longAccessTokenInfo = graphInstagramService.getLongAccessTokenInfo(
                grantType = "ig_exchange_token",
                clientSecret = clientSecret,
                accessToken = shortAccessToken
            )

            Log.d(TAG, "longAccessTokenInfo = $longAccessTokenInfo")

            val expiredTimeMillis = currentTimeMillis + longAccessTokenInfo.expiresIn
            Log.d(
                TAG,
                "currentTimeMillis = $currentTimeMillis, expiredTimeMillis = $expiredTimeMillis"
            )

            preference.set(Constants.PREF_KEY_EXPIRED_MILLISECONDS, expiredTimeMillis)
            preference.set(Constants.PREF_KEY_ACCESS_TOKEN, longAccessTokenInfo.accessToken)

            _accessTokenResult.postValue(true)

        } catch (exception: UnknownHostException) { // Request Api when no internet
            exception.printStackTrace()
            Log.e(TAG, "getLongAccessToken exception = $exception")
            _accessTokenResult.postValue(false)
        } catch (exception: Exception) {
            exception.printStackTrace()
            Log.e(TAG, "getLongAccessToken exception = $exception")
            _accessTokenResult.postValue(false)
        }
    }

    fun clear() {
        _accessTokenResult.value = null
    }

    private fun isTokenValid(): Boolean {
        val expiredMilliseconds = preference.getLong(Constants.PREF_KEY_EXPIRED_MILLISECONDS, 0)
        val currentTimeMillis: Long = System.currentTimeMillis()

        return currentTimeMillis < expiredMilliseconds
    }
}

<<:  day27 coroutine和任务的爱情长跑,application和workManager

>>:  Day25 Data Storage in iOS 01 - UserDefaults

#7 Python进阶教学4

IO 今天要介绍常见的IO操作,流程:开档 >> 处理 >> 关档,下面来讲...

Day39 ( 游戏设计 ) 青蛙过河

青蛙过河 教学原文参考:青蛙过河 这篇文章会介绍,如何在 Scratch 3 里使用多个角色,搭配重...

[Day07- React Native]建立 React Native 专案

React Native 官网 现在有许多可以建立双平台的工具,像是 Golang 的 Flutte...

Day 0x2 UVa11150 Cola

Virtual Judge ZeroJudge 题意 3瓶可乐换一瓶,可和朋友借一瓶 (需还),问...

数值系统与补数

本文目标 了解计算机如何储存资料 了解计算机如何处理负数以及减法 练习进制间的转换 进制系统 进位制...