From 149c6be1bfd4bd84381757940fece1be7b9801aa Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 31 Dec 2018 18:57:28 -0500 Subject: Enhancement/coroutines (#1273) * Convert rest of fbcookie to suspended methods * Replace active checks with yield * Apply spotless * Switch cookie domain to exact url * Optimize imports and enable travis tests again * Update proguard rules * Remove unnecessary yield * Remove unused flyweight * Remove unused disposable and method * Use contexthelper instead of dispatcher main * Convert login activity to coroutines * Use kau helper methods for coroutines * Enhancement/offline site (#1288) * Begin conversion of offline site logic * Fix offline tests and add validation tests * Ignore cookie in jsoup if it is blank * Force load and zip to be in io * Use different zip files to fix tests * Log all test output * Do not log stdout * Allow test skip for fb offline --- .../pitchedapps/frost/activities/LoginActivity.kt | 169 +++++++++++---------- 1 file changed, 90 insertions(+), 79 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt index 8b5fe38d..9540636a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt @@ -18,7 +18,6 @@ package com.pitchedapps.frost.activities import android.graphics.drawable.Drawable import android.os.Bundle -import android.os.Handler import android.widget.ImageView import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.Toolbar @@ -26,6 +25,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import ca.allanwang.kau.utils.bindView import ca.allanwang.kau.utils.fadeIn import ca.allanwang.kau.utils.fadeOut +import ca.allanwang.kau.utils.withMainContext import com.bumptech.glide.RequestManager import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException @@ -34,7 +34,7 @@ import com.bumptech.glide.request.target.Target import com.pitchedapps.frost.R import com.pitchedapps.frost.dbflow.CookieModel import com.pitchedapps.frost.dbflow.fetchUsername -import com.pitchedapps.frost.dbflow.loadFbCookiesAsync +import com.pitchedapps.frost.dbflow.loadFbCookiesSuspend import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.profilePictureUrl import com.pitchedapps.frost.glide.FrostGlide @@ -46,11 +46,16 @@ import com.pitchedapps.frost.utils.frostEvent import com.pitchedapps.frost.utils.launchNewTask import com.pitchedapps.frost.utils.logFrostEvent import com.pitchedapps.frost.utils.setFrostColors +import com.pitchedapps.frost.utils.uniqueOnly import com.pitchedapps.frost.web.LoginWebView -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.BiFunction -import io.reactivex.subjects.SingleSubject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import kotlin.coroutines.resume /** * Created by Allan Wang on 2017-06-01. @@ -63,18 +68,8 @@ class LoginActivity : BaseActivity() { private val textview: AppCompatTextView by bindView(R.id.textview) private val profile: ImageView by bindView(R.id.profile) - private val profileSubject = SingleSubject.create() - private val usernameSubject = SingleSubject.create() private lateinit var profileLoader: RequestManager - - // Helper to set and enable swipeRefresh - private var refresh: Boolean - get() = swipeRefresh.isRefreshing - set(value) { - if (value) swipeRefresh.isEnabled = true - swipeRefresh.isRefreshing = value - if (!value) swipeRefresh.isEnabled = false - } + private val refreshChannel = Channel(10) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -84,80 +79,96 @@ class LoginActivity : BaseActivity() { setFrostColors { toolbar(toolbar) } - web.loadLogin({ refresh = it != 100 }) { cookie -> + profileLoader = GlideApp.with(profile) + launch { + for (refreshing in refreshChannel.uniqueOnly(this)) { + if (refreshing) swipeRefresh.isEnabled = true + swipeRefresh.isRefreshing = refreshing + if (!refreshing) swipeRefresh.isEnabled = false + } + } + launch { + val cookie = web.loadLogin { refresh(it != 100) } L.d { "Login found" } FbCookie.save(cookie.id) web.fadeOut(onFinish = { profile.fadeIn() - loadInfo(cookie) + launch { loadInfo(cookie) } }) } - profileLoader = GlideApp.with(profile) } - private fun loadInfo(cookie: CookieModel) { - refresh = true - Single.zip>( - profileSubject, - usernameSubject, - BiFunction(::Pair) - ) - .observeOn(AndroidSchedulers.mainThread()).subscribe { (foundImage, name) -> - refresh = false - if (!foundImage) { - L.e { "Could not get profile photo; Invalid userId?" } - L._i { cookie } - } - textview.text = String.format(getString(R.string.welcome), name) - textview.fadeIn() - frostEvent("Login", "success" to true) - /* - * The user may have logged into an account that is already in the database - * We will let the db handle duplicates and load it now after the new account has been saved - */ - loadFbCookiesAsync { - val cookies = ArrayList(it) - Handler().postDelayed({ - if (Showcase.intro) - launchNewTask(cookies, true) - else - launchNewTask(cookies, true) - }, 1000) - } - }.disposeOnDestroy() - loadProfile(cookie.id) - loadUsername(cookie) + private fun refresh(refreshing: Boolean) { + refreshChannel.offer(refreshing) } - private fun loadProfile(id: Long) { - profileLoader.load(profilePictureUrl(id)) - .transform(FrostGlide.roundCorner).listener(object : RequestListener { - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - profileSubject.onSuccess(true) - return false - } - - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - e.logFrostEvent("Profile loading exception") - profileSubject.onSuccess(false) - return false - } - }).into(profile) + private suspend fun loadInfo(cookie: CookieModel): Unit = withMainContext { + refresh(true) + + val imageDeferred = async { loadProfile(cookie.id) } + val nameDeferred = async { loadUsername(cookie) } + + val foundImage = imageDeferred.await() + val name = nameDeferred.await() + + refresh(false) + + if (!foundImage) { + L.e { "Could not get profile photo; Invalid userId?" } + L._i { cookie } + } + + textview.text = String.format(getString(R.string.welcome), name) + textview.fadeIn() + frostEvent("Login", "success" to true) + + /* + * The user may have logged into an account that is already in the database + * We will let the db handle duplicates and load it now after the new account has been saved + */ + val cookies = ArrayList(loadFbCookiesSuspend()) + delay(1000) + if (Showcase.intro) + launchNewTask(cookies, true) + else + launchNewTask(cookies, true) + } + + private suspend fun loadProfile(id: Long): Boolean = withMainContext { + suspendCancellableCoroutine { cont -> + profileLoader.load(profilePictureUrl(id)) + .transform(FrostGlide.roundCorner).listener(object : RequestListener { + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + cont.resume(true) + return false + } + + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + e.logFrostEvent("Profile loading exception") + cont.resume(false) + return false + } + }).into(profile) + } } - private fun loadUsername(cookie: CookieModel) { - cookie.fetchUsername(usernameSubject::onSuccess).disposeOnDestroy() + private suspend fun loadUsername(cookie: CookieModel): String = withContext(Dispatchers.IO) { + suspendCancellableCoroutine { cont -> + cookie.fetchUsername { + cont.resume(it) + } + } } override fun backConsumer(): Boolean { -- cgit v1.2.3