question

newuser-3a06577d-da25-4bcb-ae75-a8bc626cf2d3 avatar image
newuser-3a06577d-da25-4bcb-ae75-a8bc626cf2d3 asked ·

LWA no login response on non-amazon device (Android)

Hey guys, I recently implemented LWA on a demo app I'm working on. At first I had only tried in on an Amazon Fire device, and there everything worked as advertised. Recently I tried to login on several different Samsung devices and I noticed I didn't get any response upon returning from the device's browser. Here's my current code:

internal class AmazonAccountManager(override val context: Context) : AccountManager {
    //region public fields
    override val user: MutableLiveData<User?> = MutableLiveData()
    //endregion public fields

    //region private fields
    private var lastTokenRefresh: Long? = null
    private var accessToken: String?  by Delegates.observable<String?>(null) { _, _, newValue ->
        if (!newValue.isNullOrBlank()) {
            lastTokenRefresh = System.currentTimeMillis()
        }
    }

    private val requestContext = RequestContext.create(context)
    private val onUserCreatedListener = object : Listener<User, AuthError> {
        /* fetch completed successfully. */
        override fun onSuccess(amazonUser: User) {
            user.postValue(amazonUser)
            changeLoginState(amazonUser)
        }

        /* There was an error during the attempt to get the profile. */
        override fun onError(authError: AuthError) {
            changeLoginState()
            Log.e(TAG, "onUserCreatedListener onError: ", authError)
        }
    }
    private val authorizationListener = object : AuthorizeListener() {
        /* Authorization was completed successfully. */
        override fun onSuccess(result: AuthorizeResult) {
            // At this point we know the authorization completed, so remove the ability to return to the app to sign-in again
            fetchUserProfile()
            accessToken = result.accessToken
        }

        /* There was an error during the attempt to authorize the application */
        override fun onError(authError: AuthError) {
            changeLoginState()
            Log.e(TAG, "authorizationListener onError: ", authError)
        }

        /* Authorization was cancelled before it could be completed. */
        override fun onCancel(authCancellation: AuthCancellation) {
            changeLoginState()
        }
    }
    private val logoutListener = object : Listener<Void?, AuthError> {
        override fun onSuccess(response: Void?) {
            changeLoginState()
        }

        override fun onError(authError: AuthError) {
            Log.e(TAG, "logoutListener onError: ", authError)
        }
    }
    //endregion private fields

    init {
        requestContext.registerListener(authorizationListener)
        fetchUserProfile()
    }

    //region public API
    override fun login(): Boolean {
        AuthorizationManager.authorize(
            AuthorizeRequest.Builder(requestContext)
                .addScopes(*SCOPES)
                .build()
        )
        return true
    }

    override fun logout(): Boolean {
        AuthorizationManager.signOut(context, logoutListener)
        return true
    }

    override suspend fun getAccessToken(): String? {
        refreshAccessToken()
        return accessToken
    }
    //endregion public API

    //region lifecycle
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private fun onStart() {
        GlobalScope.launch { getAccessToken(context, SCOPES) }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private fun onResume() {
        requestContext.onResume()
    }
    //endregion lifecycle

    //region private
    private fun fetchUserProfile() {
        User.fetch(context, onUserCreatedListener)
    }

    private fun changeLoginState(amazonUser: User? = null) {
        user.postValue(amazonUser)
    }

    /**
     * Since the Amazon LWA SDK doesn't currently have a refreshAccessToken option, we create our own, knowing that
     * the provided access tokens have a life span of 60 Minutes
     */
    private suspend fun refreshAccessToken() {
        lastTokenRefresh?.let {
            val currentTime = System.currentTimeMillis()
            val shouldRefresh = (currentTime - it) > REFRESH_AFTER

            if (shouldRefresh) {
                getAccessToken(context, SCOPES)
            }
        }
    }

    private suspend fun getAccessToken(context: Context, scopes: Array<Scope>): Unit =
        suspendCancellableCoroutine { cont ->
            AuthorizationManager.getToken(context, scopes, object : Listener<AuthorizeResult, AuthError> {
                override fun onSuccess(authResult: AuthorizeResult) {
                    accessToken = authResult.accessToken
                    if (authResult.accessToken != null) {
                        /* The user is signed in */
                        fetchUserProfile()
                    } else {
                        Log.e(TAG, "getAccessToken onSuccess: accessToken = $accessToken")
                        /* The user is not signed in */
                    }
                    cont.resume(Unit)
                }

                override fun onError(authError: AuthError) {
                    accessToken = null
                    cont.resumeWithException(authError)
                    Log.e(TAG, "getAccessToken onError: ", authError)
                    /* The user is not signed in */
                }
            })
        }
    //endregion private

    companion object {
        private val TAG by lazy { AmazonAccountManager::class.java.simpleName }

        private val REFRESH_AFTER = TimeUnit.MINUTES.toMillis(55)
        private val SCOPES = arrayOf(ProfileScope.profile(), ProfileScope.postalCode())
    }
}
login with amazonandroid
10 |2000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

0 Answers