aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2018-12-27 14:34:29 -0500
committerAllan Wang <me@allanwang.ca>2018-12-27 14:34:29 -0500
commitf9e3a324e47a81a30aade003cf6f829d03c81414 (patch)
tree655a7fc8894170716986a0cab1c784822d750f6f
parente6dcbd7b32dc49b11184b6beca598819c3f071fd (diff)
downloadfrost-f9e3a324e47a81a30aade003cf6f829d03c81414.tar.gz
frost-f9e3a324e47a81a30aade003cf6f829d03c81414.tar.bz2
frost-f9e3a324e47a81a30aade003cf6f829d03c81414.zip
Convert remaining view observables
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt29
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt7
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostObservables.kt46
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt62
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt5
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt11
-rw-r--r--app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt35
9 files changed, 66 insertions, 141 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
index bf04c524..816524ba 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
@@ -73,6 +73,9 @@ import com.pitchedapps.frost.views.FrostWebView
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import okhttp3.HttpUrl
/**
@@ -99,12 +102,15 @@ class FrostWebActivity : WebOverlayActivityBase(false) {
* We will subscribe to the load cycle once,
* and pop a dialog giving the user the option to copy the shared text
*/
- var disposable: Disposable? = null
- disposable = content.refreshObservable.subscribe {
- disposable?.dispose()
- materialDialogThemed {
- title(R.string.invalid_share_url)
- content(R.string.invalid_share_url_desc)
+ val refreshReceiver = content.refreshChannel.openSubscription()
+ content.scope.launch(Dispatchers.IO) {
+ refreshReceiver.receive()
+ refreshReceiver.cancel()
+ withContext(Dispatchers.Main) {
+ materialDialogThemed {
+ title(R.string.invalid_share_url)
+ content(R.string.invalid_share_url_desc)
+ }
}
}
}
@@ -197,10 +203,13 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
content.bind(this)
- content.titleObservable
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe { toolbar.title = it }
- .disposeOnDestroy()
+ val titleReceiver = content.titleChannel.openSubscription()
+
+ launch {
+ for (t in titleReceiver) {
+ toolbar.title = t
+ }
+ }
with(web) {
if (forceBasicAgent) //todo check; the webview already adds it dynamically
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt
index a2c2b05a..0f8c49d3 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt
@@ -59,22 +59,17 @@ interface FrostContentParent : DynamicUiContract {
/**
* Observable to get data on whether view is refreshing or not
*/
- val refreshObservable: PublishSubject<Boolean>
-
val refreshChannel: BroadcastChannel<Boolean>
/**
* Observable to get data on refresh progress, with range [0, 100]
*/
- val progressObservable: PublishSubject<Int>
-
val progressChannel: BroadcastChannel<Int>
/**
* Observable to get new title data (unique values only)
*/
- val titleObservable: BehaviorSubject<String>
-
+ // todo note that this should be like a behavior subject vs publish subject
val titleChannel: BroadcastChannel<String>
var baseUrl: String
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostObservables.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostObservables.kt
deleted file mode 100644
index b3b93b66..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostObservables.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 Allan Wang
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.pitchedapps.frost.contracts
-
-import io.reactivex.subjects.BehaviorSubject
-import io.reactivex.subjects.PublishSubject
-
-/**
- * Created by Allan Wang on 2017-11-07.
- */
-interface FrostObservables {
- /**
- * Observable to get data on whether view is refreshing or not
- */
- var refreshObservable: PublishSubject<Boolean>
-
- /**
- * Observable to get data on refresh progress, with range [0, 100]
- */
- var progressObservable: PublishSubject<Int>
-
- /**
- * Observable to get new title data (unique values only)
- */
- var titleObservable: BehaviorSubject<String>
-
- fun passObservablesTo(other: FrostObservables) {
- other.refreshObservable = refreshObservable
- other.progressObservable = progressObservable
- other.titleObservable = titleObservable
- }
-}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt
index 72b81b37..38591d38 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt
@@ -39,17 +39,14 @@ import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.WEB_LOAD_DELAY
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
-import io.reactivex.rxkotlin.addTo
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BroadcastChannel
-import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -89,13 +86,9 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
override val core: FrostContentCore
get() = coreView
- override val progressObservable: PublishSubject<Int> = PublishSubject.create()
- override val refreshObservable: PublishSubject<Boolean> = PublishSubject.create()
- override val titleObservable: BehaviorSubject<String> = BehaviorSubject.create()
-
- override val refreshChannel: BroadcastChannel<Boolean> = BroadcastChannel(Channel.UNLIMITED)
- override val progressChannel: BroadcastChannel<Int> = BroadcastChannel(Channel.UNLIMITED)
- override val titleChannel: BroadcastChannel<String> = BroadcastChannel(Channel.UNLIMITED)
+ override val refreshChannel: BroadcastChannel<Boolean> = BroadcastChannel(100)
+ override val progressChannel: BroadcastChannel<Int> = BroadcastChannel(100)
+ override val titleChannel: BroadcastChannel<String> = BroadcastChannel(100)
override lateinit var scope: CoroutineScope
@@ -133,9 +126,13 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
reload(true)
}
}
+ // Begin subscription in the main thread
+ val refreshReceiver = refreshChannel.openSubscription()
+ val progressReceiver = progressChannel.openSubscription()
+
scope.launch(Dispatchers.Default) {
launch {
- for (r in refreshChannel.openSubscription()) {
+ for (r in refreshReceiver) {
withContext(Dispatchers.Main) {
refresh.isRefreshing = r
refresh.isEnabled = true
@@ -143,7 +140,7 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
}
}
launch {
- for (p in progressChannel.openSubscription()) {
+ for (p in progressReceiver) {
withContext(Dispatchers.Main) {
progress.invisibleIf(p == 100)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
@@ -184,6 +181,7 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
private var dispose: Disposable? = null
private var transitionStart: Long = -1
+ private var refreshReceiver: ReceiveChannel<Boolean>? = null
/**
* Hook onto the refresh observable for one cycle
@@ -191,32 +189,32 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
* The cycle only starts on the first load since there may have been another process when this is registered
*/
override fun registerTransition(urlChanged: Boolean, animate: Boolean): Boolean {
- if (!urlChanged && dispose != null) {
+ if (!urlChanged && refreshReceiver != null) {
L.v { "Consuming url load" }
return false // still in progress; do not bother with load
}
L.v { "Registered transition" }
with(coreView) {
- var loading = dispose != null
- dispose?.dispose()
- dispose = refreshObservable
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- if (it) {
- loading = true
- transitionStart = System.currentTimeMillis()
- clearAnimation()
- if (isVisible)
- fadeOut(duration = 200L)
- } else if (loading) {
- loading = false
- if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
- else fadeIn(duration = 200L, offset = WEB_LOAD_DELAY)
- L.v { "Transition loaded in ${System.currentTimeMillis() - transitionStart} ms" }
- dispose?.dispose()
- dispose = null
+ refreshReceiver = refreshChannel.openSubscription().also { receiver ->
+ scope.launch(Dispatchers.Main) {
+ var loading = false
+ for (r in receiver) {
+ if (r) {
+ loading = true
+ transitionStart = System.currentTimeMillis()
+ clearAnimation()
+ if (isVisible)
+ fadeOut(duration = 200L)
+ } else if (loading) {
+ if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
+ else fadeIn(duration = 200L, offset = WEB_LOAD_DELAY)
+ L.v { "Transition loaded in ${System.currentTimeMillis() - transitionStart} ms" }
+ receiver.cancel()
+ refreshReceiver = null
+ }
}
}
+ }
}
return true
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt
index fb20c3ba..2ba78c5e 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt
@@ -73,10 +73,10 @@ class FrostRecyclerView @JvmOverloads constructor(
override fun reloadBase(animate: Boolean) {
if (Prefs.animate) fadeOut(onFinish = onReloadClear)
scope.launch {
- parent.refreshChannel.send(true)
+ parent.refreshChannel.offer(true)
val loaded = recyclerContract.reload { parent.progressChannel.offer(it) }
- parent.progressChannel.send(100)
- parent.refreshChannel.send(false)
+ parent.progressChannel.offer(100)
+ parent.refreshChannel.offer(false)
if (Prefs.animate) circularReveal()
}
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
index 1f8118da..f3538ec3 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
@@ -31,6 +31,8 @@ import com.pitchedapps.frost.utils.frostSnackbar
import com.pitchedapps.frost.views.FrostWebView
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
/**
* Created by Allan Wang on 2017-05-31.
@@ -43,8 +45,8 @@ import io.reactivex.subjects.Subject
*/
class FrostChromeClient(web: FrostWebView) : WebChromeClient() {
- private val progress = web.parent.progressChannel
- private val title = web.parent.titleChannel
+ private val progress: SendChannel<Int> = web.parent.progressChannel
+ private val title: SendChannel<String> = web.parent.titleChannel
private var prevTitle: String? = null
private val activity = (web.context as? ActivityContract)
private val context = web.context!!
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
index 2afb28c9..3945a9a0 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
@@ -30,6 +30,7 @@ import com.pitchedapps.frost.utils.launchImageActivity
import com.pitchedapps.frost.utils.showWebContextMenu
import com.pitchedapps.frost.views.FrostWebView
import io.reactivex.subjects.Subject
+import kotlinx.coroutines.channels.SendChannel
/**
* Created by Allan Wang on 2017-06-01.
@@ -39,7 +40,7 @@ class FrostJSI(val web: FrostWebView) {
private val context = web.context
private val activity = context as? MainActivity
private val header: Subject<String>? = activity?.headerBadgeObservable
- private val refresh: Subject<Boolean> = web.parent.refreshObservable
+ private val refresh: SendChannel<Boolean> = web.parent.refreshChannel
private val cookies = activity?.cookies() ?: arrayListOf()
/**
@@ -120,7 +121,7 @@ class FrostJSI(val web: FrostWebView) {
@JavascriptInterface
fun isReady() {
- refresh.onNext(false)
+ refresh.offer(false)
}
@JavascriptInterface
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
index d75f03bb..5a137c44 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
@@ -41,6 +41,7 @@ import com.pitchedapps.frost.utils.launchImageActivity
import com.pitchedapps.frost.utils.resolveActivityForUri
import com.pitchedapps.frost.views.FrostWebView
import io.reactivex.subjects.Subject
+import kotlinx.coroutines.channels.SendChannel
import org.jetbrains.anko.withAlpha
/**
@@ -64,7 +65,7 @@ open class BaseWebViewClient : WebViewClient() {
*/
open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
- private val refresh: Subject<Boolean> = web.parent.refreshObservable
+ private val refresh: SendChannel<Boolean> = web.parent.refreshChannel
private val isMain = web.parent.baseEnum != null
protected inline fun v(crossinline message: () -> Any?) = L.v { "web client: ${message()}" }
@@ -73,7 +74,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
super.onPageStarted(view, url, favicon)
if (url == null) return
v { "loading $url" }
- refresh.onNext(true)
+ refresh.offer(true)
}
private fun injectBackgroundColor() {
@@ -110,14 +111,14 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
JsAssets.MEDIA
)
else
- refresh.onNext(false)
+ refresh.offer(false)
}
override fun onPageFinished(view: WebView, url: String?) {
url ?: return
v { "finished $url" }
if (!url.isFacebookUrl) {
- refresh.onNext(false)
+ refresh.offer(false)
return
}
onPageFinishedActions(url)
@@ -131,7 +132,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
internal fun injectAndFinish() {
v { "page finished reveal" }
- refresh.onNext(false)
+ refresh.offer(false)
injectBackgroundColor()
web.jsInject(
JsActions.LOGIN_CHECK,
diff --git a/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt
index 2676e37d..20610b2a 100644
--- a/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt
+++ b/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt
@@ -17,15 +17,7 @@
package com.pitchedapps.frost
import com.pitchedapps.frost.facebook.requests.zip
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.asCoroutineDispatcher
-import kotlinx.coroutines.channels.BroadcastChannel
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
import org.junit.Test
-import java.util.concurrent.Executors
import kotlin.test.assertTrue
/**
@@ -53,31 +45,4 @@ class MiscTest {
"zip did not seem to work on different threads"
)
}
-
-@Test
-@UseExperimental(ExperimentalCoroutinesApi::class)
-fun channel() {
- val c = BroadcastChannel<Int>(100)
- runBlocking {
- launch(Dispatchers.IO) {
- println("1 start ${Thread.currentThread()}")
- for (i in c.openSubscription()) {
- println("1 $i")
- }
- println("1 end ${Thread.currentThread()}")
- }
- launch(Dispatchers.IO) {
- println("2 start ${Thread.currentThread()}")
- for (i in c.openSubscription()) {
- println("2 $i")
- }
- println("2 end ${Thread.currentThread()}")
- }
- c.send(1)
- c.send(2)
- c.send(3)
- delay(1000)
- c.close()
- }
-}
}