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:
parent
e6dcbd7b32
commit
f9e3a324e4
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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!!
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user