1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-10 04:52:38 +01:00

Convert remaining view observables

This commit is contained in:
Allan Wang 2018-12-27 14:34:29 -05:00
parent e6dcbd7b32
commit f9e3a324e4
No known key found for this signature in database
GPG Key ID: C93E3F9C679D7A56
9 changed files with 66 additions and 141 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
}
}

View File

@ -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!!

View File

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

View File

@ -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,

View File

@ -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()
}
}
}