iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
Mobile Development

Flutter - 從 Packages & Plugins 掌握原生系列 第 25

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
    }
}

上一篇
Day24 Plugin 從零開始到上架 - FlutterPlugin與 MethodCallHandler
下一篇
Day26 Plugin 從零開始到上架 - Android總結
系列文
Flutter - 從 Packages & Plugins 掌握原生30

尚未有邦友留言

立即登入留言