From 1dd7be9174f1740aa1cae29f6d62d6f83f5917ba Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 22 Nov 2021 22:24:17 -0800 Subject: Migrate refresh channel to flow --- .../com/pitchedapps/frost/contracts/FrostContentContract.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/contracts') 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 b8d0d86f..1d429138 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt @@ -18,9 +18,11 @@ package com.pitchedapps.frost.contracts import android.view.View import com.pitchedapps.frost.facebook.FbItem +import com.pitchedapps.frost.web.FrostEmitter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.BroadcastChannel +import kotlinx.coroutines.flow.SharedFlow /** * Created by Allan Wang on 20/12/17. @@ -56,7 +58,9 @@ interface FrostContentParent : DynamicUiContract { /** * Observable to get data on whether view is refreshing or not */ - val refreshChannel: BroadcastChannel + val refreshFlow: SharedFlow + + val refreshEmit: FrostEmitter /** * Observable to get data on refresh progress, with range [0, 100] @@ -124,17 +128,15 @@ interface FrostContentCore : DynamicUiContract { * Reference to parent * Bound through calling [FrostContentParent.bind] */ - var parent: FrostContentParent + val parent: FrostContentParent /** * Initializes view through given [container] * * The content may be free to extract other data from * the container if necessary - * - * [parent] must be bounded before calling this! */ - fun bind(container: FrostContentContainer): View + fun bind(parent: FrostContentParent, container: FrostContentContainer): View /** * Call to reload wrapped data -- cgit v1.2.3 From 779ec08188f4bda736b3e0f2940570f1f7eb49e1 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 22 Nov 2021 22:53:00 -0800 Subject: Migrate progress channel to flow --- .../pitchedapps/frost/contracts/FrostContentContract.kt | 4 +++- .../com/pitchedapps/frost/views/FrostContentView.kt | 15 ++++++++++----- .../com/pitchedapps/frost/views/FrostRecyclerView.kt | 4 ++-- .../com/pitchedapps/frost/web/FrostChromeClients.kt | 6 +++--- 4 files changed, 18 insertions(+), 11 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/contracts') 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 1d429138..8ebf7af7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt @@ -65,7 +65,9 @@ interface FrostContentParent : DynamicUiContract { /** * Observable to get data on refresh progress, with range [0, 100] */ - val progressChannel: BroadcastChannel + val progressFlow: SharedFlow + + val progressEmit: FrostEmitter /** * Observable to get new title data (unique values only) 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 1891a786..3ec80f36 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt @@ -22,7 +22,6 @@ import android.util.AttributeSet import android.view.View import android.widget.FrameLayout import android.widget.ProgressBar -import ca.allanwang.kau.utils.ContextHelper import ca.allanwang.kau.utils.bindView import ca.allanwang.kau.utils.circularReveal import ca.allanwang.kau.utils.fadeIn @@ -39,7 +38,6 @@ import com.pitchedapps.frost.contracts.FrostContentParent import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.WEB_LOAD_DELAY import com.pitchedapps.frost.injectors.ThemeProvider -import com.pitchedapps.frost.kotlin.subscribeDuringJob import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.web.FrostEmitter @@ -51,6 +49,7 @@ import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.ConflatedBroadcastChannel import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.collect @@ -146,7 +145,13 @@ abstract class FrostContentViewBase( override val refreshEmit: FrostEmitter = FrostEmitter { refreshMutableFlow.tryEmit(it) } - override val progressChannel: BroadcastChannel = ConflatedBroadcastChannel() + private val progressMutableFlow = MutableStateFlow(0) + + override val progressFlow: SharedFlow = progressMutableFlow.asSharedFlow() + + override val progressEmit: FrostEmitter = + FrostEmitter { progressMutableFlow.tryEmit(it) } + override val titleChannel: BroadcastChannel = ConflatedBroadcastChannel() override lateinit var scope: CoroutineScope @@ -200,13 +205,13 @@ abstract class FrostContentViewBase( refresh.isRefreshing = r }.launchIn(scope) - progressChannel.subscribeDuringJob(scope, ContextHelper.coroutineContext) { p -> + progressFlow.onEach { p -> progress.invisibleIf(p == 100) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) progress.setProgress(p, true) else progress.progress = p - } + }.launchIn(scope) } override fun reloadTheme() { 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 a2b71572..9e21ede8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt @@ -80,8 +80,8 @@ class FrostRecyclerView @JvmOverloads constructor( if (prefs.animate) fadeOut(onFinish = onReloadClear) scope.launch { parent.refreshEmit(true) - recyclerContract.reload { parent.progressChannel.offer(it) } - parent.progressChannel.offer(100) + recyclerContract.reload { parent.progressEmit(it) } + parent.progressEmit(100) parent.refreshEmit(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 372a7bad..9f2437b0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt @@ -53,7 +53,7 @@ class FrostChromeClient( // private val refresh: SendChannel = web.parent.refreshChannel private val refreshEmit = web.parent.refreshEmit - private val progress: SendChannel = web.parent.progressChannel + private val progressEmit = web.parent.progressEmit private val title: SendChannel = web.parent.titleChannel private val context = web.context!! @@ -74,7 +74,7 @@ class FrostChromeClient( override fun onProgressChanged(view: WebView, newProgress: Int) { super.onProgressChanged(view, newProgress) - progress.offer(newProgress) + progressEmit(newProgress) } override fun onShowFileChooser( @@ -89,7 +89,7 @@ class FrostChromeClient( private fun JsResult.frostCancel() { cancel() refreshEmit(false) - progress.offer(100) + progressEmit(100) } override fun onJsAlert( -- cgit v1.2.3 From bbfac885b89a79af2c85f5f0df7635770b49a07a Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 22 Nov 2021 22:57:55 -0800 Subject: Convert title channel to flow --- .../kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt | 8 +++----- .../com/pitchedapps/frost/contracts/FrostContentContract.kt | 6 +++--- .../main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt | 8 ++++++-- .../main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt | 5 ++--- 4 files changed, 14 insertions(+), 13 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/contracts') 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 171583ed..ae8d442f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt @@ -27,7 +27,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout import ca.allanwang.kau.swipe.SwipeBackContract import ca.allanwang.kau.swipe.kauSwipeOnCreate import ca.allanwang.kau.swipe.kauSwipeOnDestroy -import ca.allanwang.kau.utils.ContextHelper import ca.allanwang.kau.utils.bindView import ca.allanwang.kau.utils.copyToClipboard import ca.allanwang.kau.utils.darken @@ -56,7 +55,6 @@ import com.pitchedapps.frost.facebook.USER_AGENT import com.pitchedapps.frost.facebook.USER_AGENT_DESKTOP_CONST import com.pitchedapps.frost.facebook.USER_AGENT_MOBILE_CONST import com.pitchedapps.frost.facebook.formattedFbUrl -import com.pitchedapps.frost.kotlin.subscribeDuringJob import com.pitchedapps.frost.utils.ARG_URL import com.pitchedapps.frost.utils.ARG_USER_ID import com.pitchedapps.frost.utils.BiometricUtils @@ -69,6 +67,8 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import okhttp3.HttpUrl.Companion.toHttpUrlOrNull @@ -215,9 +215,7 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT content.bind(this) - content.titleChannel.subscribeDuringJob(this, ContextHelper.coroutineContext) { - toolbar.title = it - } + content.titleFlow.onEach { toolbar.title = it }.launchIn(this) with(web) { userAgentString = userAgent 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 8ebf7af7..d32cb873 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt @@ -21,7 +21,6 @@ import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.web.FrostEmitter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.flow.SharedFlow /** @@ -72,8 +71,9 @@ interface FrostContentParent : DynamicUiContract { /** * Observable to get new title data (unique values only) */ - // todo note that this should be like a behavior subject vs publish subject - val titleChannel: BroadcastChannel + val titleFlow: SharedFlow + + val titleEmit: FrostEmitter var baseUrl: String 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 3ec80f36..b76f6e39 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt @@ -80,7 +80,6 @@ class FrostContentRecycler @JvmOverloads constructor( override val layoutRes: Int = R.layout.view_content_base_recycler } -@UseExperimental(ExperimentalCoroutinesApi::class) abstract class FrostContentView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -152,7 +151,12 @@ abstract class FrostContentViewBase( override val progressEmit: FrostEmitter = FrostEmitter { progressMutableFlow.tryEmit(it) } - override val titleChannel: BroadcastChannel = ConflatedBroadcastChannel() + private val titleMutableFlow = MutableStateFlow("") + + override val titleFlow: SharedFlow = titleMutableFlow.asSharedFlow() + + override val titleEmit: FrostEmitter = + FrostEmitter { titleMutableFlow.tryEmit(it) } override lateinit var scope: CoroutineScope 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 9f2437b0..90345aa2 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt @@ -34,7 +34,6 @@ import com.pitchedapps.frost.contracts.WebFileChooser import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.views.FrostWebView -import kotlinx.coroutines.channels.SendChannel /** * Created by Allan Wang on 2017-05-31. @@ -54,7 +53,7 @@ class FrostChromeClient( // private val refresh: SendChannel = web.parent.refreshChannel private val refreshEmit = web.parent.refreshEmit private val progressEmit = web.parent.progressEmit - private val title: SendChannel = web.parent.titleChannel + private val titleEmit = web.parent.titleEmit private val context = web.context!! override fun getDefaultVideoPoster(): Bitmap? = @@ -69,7 +68,7 @@ class FrostChromeClient( override fun onReceivedTitle(view: WebView, title: String) { super.onReceivedTitle(view, title) if (title.startsWith("http")) return - this.title.offer(title) + titleEmit(title) } override fun onProgressChanged(view: WebView, newProgress: Int) { -- cgit v1.2.3 From dcd0db9282d92beacd35b3418d924ff3c607dead Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 22 Nov 2021 23:21:34 -0800 Subject: Convert header channel to flow --- .../pitchedapps/frost/activities/MainActivity.kt | 39 ++++++++++++++++------ .../frost/contracts/ActivityContract.kt | 7 +++- .../pitchedapps/frost/views/FrostContentView.kt | 2 -- .../kotlin/com/pitchedapps/frost/web/FrostJSI.kt | 5 ++- 4 files changed, 36 insertions(+), 17 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/contracts') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index 755064cd..4b18088c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -18,23 +18,33 @@ package com.pitchedapps.frost.activities import android.os.Bundle import androidx.viewpager.widget.ViewPager -import ca.allanwang.kau.utils.withMainContext import com.google.android.material.tabs.TabLayout import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.parsers.BadgeParser -import com.pitchedapps.frost.kotlin.subscribeDuringJob import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.views.BadgedIcon +import com.pitchedapps.frost.web.FrostEmitter import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.BroadcastChannel -import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach @UseExperimental(ExperimentalCoroutinesApi::class) class MainActivity : BaseMainActivity() { override val fragmentChannel = BroadcastChannel(10) - override val headerBadgeChannel = BroadcastChannel(Channel.CONFLATED) + + private val headerMutableFlow = MutableStateFlow("") + override val headerFlow: SharedFlow = headerMutableFlow.asSharedFlow() + override val headerEmit: FrostEmitter = FrostEmitter { headerMutableFlow.tryEmit(it) } override fun onNestedCreate(savedInstanceState: Bundle?) { with(contentBinding) { @@ -90,12 +100,18 @@ class MainActivity : BaseMainActivity() { (tab.customView as BadgedIcon).badgeText = null } }) - headerBadgeChannel.subscribeDuringJob(this@MainActivity, Dispatchers.IO) { html -> - val data = - BadgeParser.parseFromData(cookie = fbCookie.webCookie, text = html)?.data - ?: return@subscribeDuringJob - L.v { "Badges $data" } - withMainContext { + headerFlow + .filter { it.isNotBlank() } + .mapNotNull { html -> + BadgeParser.parseFromData( + cookie = fbCookie.webCookie, + text = html + )?.data + } + .distinctUntilChanged() + .flowOn(Dispatchers.IO) + .onEach { data -> + L.v { "Badges $data" } tabsForEachView { _, view -> when (view.iicon) { FbItem.FEED.icon -> view.badgeText = data.feed @@ -105,6 +121,7 @@ class MainActivity : BaseMainActivity() { } } } - } + .flowOn(Dispatchers.Main) + .launchIn(this@MainActivity) } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt index 756b1f3d..83110417 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt @@ -18,13 +18,18 @@ package com.pitchedapps.frost.contracts import com.mikepenz.iconics.typeface.IIcon import com.pitchedapps.frost.fragments.BaseFragment +import com.pitchedapps.frost.web.FrostEmitter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.BroadcastChannel +import kotlinx.coroutines.flow.SharedFlow @UseExperimental(ExperimentalCoroutinesApi::class) interface MainActivityContract : MainFabContract { val fragmentChannel: BroadcastChannel - val headerBadgeChannel: BroadcastChannel + + val headerFlow: SharedFlow + val headerEmit: FrostEmitter + fun setTitle(res: Int) fun setTitle(text: CharSequence) 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 b76f6e39..75d1ffe4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt @@ -44,9 +44,7 @@ import com.pitchedapps.frost.web.FrostEmitter import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.channels.ConflatedBroadcastChannel import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow 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 f43f3b81..4d92e8c2 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -32,7 +32,6 @@ import com.pitchedapps.frost.utils.isIndependent import com.pitchedapps.frost.utils.launchImageActivity import com.pitchedapps.frost.utils.showWebContextMenu import com.pitchedapps.frost.views.FrostWebView -import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.launch import javax.inject.Inject @@ -50,7 +49,7 @@ class FrostJSI @Inject internal constructor( private val mainActivity: MainActivity? = activity as? MainActivity private val webActivity: WebOverlayActivityBase? = activity as? WebOverlayActivityBase - private val header: SendChannel? = mainActivity?.headerBadgeChannel + private val headerEmit: FrostEmitter? = mainActivity?.headerEmit private val cookies: List = activity.cookies() /** @@ -159,7 +158,7 @@ class FrostJSI @Inject internal constructor( @JavascriptInterface fun handleHeader(html: String?) { html ?: return - header?.offer(html) + headerEmit?.invoke(html) } @JavascriptInterface -- cgit v1.2.3 From 30d6fd9d33e17110726a299749b058416ed77ecf Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 23 Nov 2021 11:46:10 -0800 Subject: Convert fragment channel to flow --- .../frost/activities/BaseMainActivity.kt | 19 ++++++++-------- .../pitchedapps/frost/activities/MainActivity.kt | 12 +++++++---- .../frost/contracts/ActivityContract.kt | 3 ++- .../pitchedapps/frost/fragments/BaseFragment.kt | 25 ++++++++-------------- .../frost/fragments/FragmentContract.kt | 10 +-------- 5 files changed, 30 insertions(+), 39 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/contracts') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt index 84352cb4..8585f68b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt @@ -498,7 +498,10 @@ abstract class BaseMainActivity : ) positiveButton(R.string.kau_yes) { this@BaseMainActivity.launch { - fbCookie.logout(this@BaseMainActivity, deleteCookie = true) + fbCookie.logout( + this@BaseMainActivity, + deleteCookie = true + ) } } negativeButton(R.string.kau_no) @@ -637,7 +640,7 @@ abstract class BaseMainActivity : private fun refreshAll() { L.d { "Refresh all" } - fragmentChannel.offer(REQUEST_REFRESH) + fragmentEmit(REQUEST_REFRESH) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -737,19 +740,19 @@ abstract class BaseMainActivity : * These results can be stacked */ if (hasRequest(REQUEST_REFRESH)) { - fragmentChannel.offer(REQUEST_REFRESH) + fragmentEmit(REQUEST_REFRESH) } if (hasRequest(REQUEST_NAV)) { frostNavigationBar(prefs, themeProvider) } if (hasRequest(REQUEST_TEXT_ZOOM)) { - fragmentChannel.offer(REQUEST_TEXT_ZOOM) + fragmentEmit(REQUEST_TEXT_ZOOM) } if (hasRequest(REQUEST_SEARCH)) { invalidateOptionsMenu() } if (hasRequest(REQUEST_FAB)) { - fragmentChannel.offer(lastPosition) + fragmentEmit(lastPosition) } if (hasRequest(REQUEST_NOTIFICATION)) { scheduleNotificationsFromPrefs(prefs) @@ -792,7 +795,6 @@ abstract class BaseMainActivity : override fun onDestroy() { controlWebview?.destroy() super.onDestroy() - fragmentChannel.close() } override fun collapseAppBar() { @@ -864,10 +866,9 @@ abstract class BaseMainActivity : lastPosition = 0 viewpager.setCurrentItem(0, false) viewpager.offscreenPageLimit = pages.size + // todo check if post is necessary viewpager.post { - if (!fragmentChannel.isClosedForSend) { - fragmentChannel.offer(0) - } + fragmentEmit(0) } // trigger hook so title is set } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index 4b18088c..97067b21 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -27,6 +27,8 @@ import com.pitchedapps.frost.web.FrostEmitter import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.BroadcastChannel +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -39,8 +41,10 @@ import kotlinx.coroutines.flow.onEach @UseExperimental(ExperimentalCoroutinesApi::class) class MainActivity : BaseMainActivity() { - - override val fragmentChannel = BroadcastChannel(10) + + private val fragmentMutableFlow = MutableSharedFlow(extraBufferCapacity = 10, onBufferOverflow = BufferOverflow.DROP_OLDEST) + override val fragmentFlow: SharedFlow = fragmentMutableFlow.asSharedFlow() + override val fragmentEmit: FrostEmitter = FrostEmitter { fragmentMutableFlow.tryEmit(it) } private val headerMutableFlow = MutableStateFlow("") override val headerFlow: SharedFlow = headerMutableFlow.asSharedFlow() @@ -61,9 +65,9 @@ class MainActivity : BaseMainActivity() { return } if (lastPosition != -1) { - fragmentChannel.offer(-(lastPosition + 1)) + fragmentEmit(-(lastPosition + 1)) } - fragmentChannel.offer(position) + fragmentEmit(position) lastPosition = position } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt index 83110417..3c5d7412 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt @@ -25,7 +25,8 @@ import kotlinx.coroutines.flow.SharedFlow @UseExperimental(ExperimentalCoroutinesApi::class) interface MainActivityContract : MainFabContract { - val fragmentChannel: BroadcastChannel + val fragmentFlow: SharedFlow + val fragmentEmit: FrostEmitter val headerFlow: SharedFlow val headerEmit: FrostEmitter diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt index 79495b2a..5c97de65 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt @@ -21,6 +21,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.flowWithLifecycle import ca.allanwang.kau.utils.ContextHelper import ca.allanwang.kau.utils.fadeScaleTransition import ca.allanwang.kau.utils.setIcon @@ -47,8 +48,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -121,7 +123,6 @@ abstract class BaseFragment : } override var firstLoad: Boolean = true - private var activityReceiver: ReceiveChannel? = null private var onCreateRunnable: ((FragmentContract) -> Unit)? = null override var content: FrostContentParent? = null @@ -152,8 +153,7 @@ abstract class BaseFragment : onCreateRunnable?.invoke(this) onCreateRunnable = null firstLoadRequest() - detachMainObservable() - activityReceiver = attachMainObservable(mainContract) + attach(mainContract) } override fun setUserVisibleHint(isVisibleToUser: Boolean) { @@ -177,10 +177,10 @@ abstract class BaseFragment : mainContract.setTitle(title) } - override fun attachMainObservable(contract: MainActivityContract): ReceiveChannel { - val receiver = contract.fragmentChannel.openSubscription() - launch { - for (flag in receiver) { + override fun attach(contract: MainActivityContract) { + contract.fragmentFlow + .flowWithLifecycle(viewLifecycleOwner.lifecycle) + .onEach { flag -> when (flag) { REQUEST_REFRESH -> { core?.apply { @@ -201,9 +201,7 @@ abstract class BaseFragment : reloadTextSize() } } - } - } - return receiver + }.launchIn(this) } override fun updateFab(contract: MainFabContract) { @@ -222,16 +220,11 @@ abstract class BaseFragment : setOnClickListener { click() } } - override fun detachMainObservable() { - activityReceiver?.cancel() - } - override fun onDestroyView() { super.onDestroyView() L.i { "Fragment on destroy $position ${hashCode()}" } content?.destroy() content = null - detachMainObservable() } override fun onDestroy() { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt index 10c612c5..beac7494 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt @@ -22,7 +22,6 @@ import com.pitchedapps.frost.contracts.FrostContentParent import com.pitchedapps.frost.contracts.MainActivityContract import com.pitchedapps.frost.contracts.MainFabContract import com.pitchedapps.frost.views.FrostRecyclerView -import kotlinx.coroutines.channels.ReceiveChannel /** * Created by Allan Wang on 2017-11-07. @@ -77,15 +76,8 @@ interface FragmentContract : FrostContentContainer { /** * Call whenever a fragment is attached so that it may listen * to activity emissions. - * Returns a means of closing the listener, which can be called from [detachMainObservable] */ - fun attachMainObservable(contract: MainActivityContract): ReceiveChannel - - /** - * Call when fragment is detached so that any existing - * observable is disposed - */ - fun detachMainObservable() + fun attach(contract: MainActivityContract) /* * ----------------------------------------- -- cgit v1.2.3 From 0c97abd838ee834ef17d1b2e746fa26ea9663bd5 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 23 Nov 2021 11:50:06 -0800 Subject: Create extension for converting emitters --- .../kotlin/com/pitchedapps/frost/activities/MainActivity.kt | 8 ++++---- .../kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt | 1 - .../kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt | 1 - .../kotlin/com/pitchedapps/frost/views/FrostContentView.kt | 10 ++++------ app/src/main/kotlin/com/pitchedapps/frost/web/FrostWeb.kt | 3 +++ 5 files changed, 11 insertions(+), 12 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/contracts') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index 97067b21..2e44e5f9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -24,9 +24,9 @@ import com.pitchedapps.frost.facebook.parsers.BadgeParser import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.views.BadgedIcon import com.pitchedapps.frost.web.FrostEmitter +import com.pitchedapps.frost.web.asFrostEmitter import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -41,14 +41,14 @@ import kotlinx.coroutines.flow.onEach @UseExperimental(ExperimentalCoroutinesApi::class) class MainActivity : BaseMainActivity() { - + private val fragmentMutableFlow = MutableSharedFlow(extraBufferCapacity = 10, onBufferOverflow = BufferOverflow.DROP_OLDEST) override val fragmentFlow: SharedFlow = fragmentMutableFlow.asSharedFlow() - override val fragmentEmit: FrostEmitter = FrostEmitter { fragmentMutableFlow.tryEmit(it) } + override val fragmentEmit: FrostEmitter = fragmentMutableFlow.asFrostEmitter() private val headerMutableFlow = MutableStateFlow("") override val headerFlow: SharedFlow = headerMutableFlow.asSharedFlow() - override val headerEmit: FrostEmitter = FrostEmitter { headerMutableFlow.tryEmit(it) } + override val headerEmit: FrostEmitter = headerMutableFlow.asFrostEmitter() override fun onNestedCreate(savedInstanceState: Bundle?) { with(contentBinding) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt index 3c5d7412..721282aa 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt @@ -20,7 +20,6 @@ import com.mikepenz.iconics.typeface.IIcon import com.pitchedapps.frost.fragments.BaseFragment import com.pitchedapps.frost.web.FrostEmitter import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.flow.SharedFlow @UseExperimental(ExperimentalCoroutinesApi::class) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt index 5c97de65..e10fd141 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt @@ -47,7 +47,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.isActive 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 75d1ffe4..f9d04ad1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt @@ -41,6 +41,7 @@ import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.web.FrostEmitter +import com.pitchedapps.frost.web.asFrostEmitter import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -139,22 +140,19 @@ abstract class FrostContentViewBase( override val refreshFlow: SharedFlow = refreshMutableFlow.asSharedFlow() - override val refreshEmit: FrostEmitter = - FrostEmitter { refreshMutableFlow.tryEmit(it) } + override val refreshEmit: FrostEmitter = refreshMutableFlow.asFrostEmitter() private val progressMutableFlow = MutableStateFlow(0) override val progressFlow: SharedFlow = progressMutableFlow.asSharedFlow() - override val progressEmit: FrostEmitter = - FrostEmitter { progressMutableFlow.tryEmit(it) } + override val progressEmit: FrostEmitter = progressMutableFlow.asFrostEmitter() private val titleMutableFlow = MutableStateFlow("") override val titleFlow: SharedFlow = titleMutableFlow.asSharedFlow() - override val titleEmit: FrostEmitter = - FrostEmitter { titleMutableFlow.tryEmit(it) } + override val titleEmit: FrostEmitter = titleMutableFlow.asFrostEmitter() override lateinit var scope: CoroutineScope diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWeb.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWeb.kt index 30845a79..ba05a2c4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWeb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWeb.kt @@ -25,6 +25,7 @@ import dagger.hilt.DefineComponent import dagger.hilt.EntryPoint import dagger.hilt.InstallIn import dagger.hilt.android.components.ViewComponent +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import javax.inject.Qualifier import javax.inject.Scope @@ -62,6 +63,8 @@ interface FrostWebEntryPoint { fun interface FrostEmitter : (T) -> Unit +fun MutableSharedFlow.asFrostEmitter(): FrostEmitter = FrostEmitter { tryEmit(it) } + @Module @InstallIn(FrostWebComponent::class) object FrostWebFlowModule { -- cgit v1.2.3 From 3599803b05188b8f9f4758b43fe120578fa8cf06 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 23 Nov 2021 12:10:23 -0800 Subject: Remove all experimental annotations --- app/build.gradle | 1 - .../main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt | 3 --- .../main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt | 2 -- .../kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt | 2 -- app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt | 2 -- app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt | 2 -- app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt | 2 -- 7 files changed, 14 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/contracts') diff --git a/app/build.gradle b/app/build.gradle index 7e064152..f13c71e1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -135,7 +135,6 @@ android { } def compilerArgs = [ - "-Xuse-experimental=kotlin.Experimental", // "-XXLanguage:+InlineClasses", "-Xopt-in=kotlin.RequiresOptIn", ] 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 ae8d442f..8dbf9d5c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt @@ -65,7 +65,6 @@ import com.pitchedapps.frost.views.FrostVideoViewer import com.pitchedapps.frost.views.FrostWebView import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -87,7 +86,6 @@ import javax.inject.Inject * Used by notifications. Unlike the other overlays, this runs as a singleInstance * Going back will bring you back to the previous app */ -@UseExperimental(ExperimentalCoroutinesApi::class) class FrostWebActivity : WebOverlayActivityBase() { override fun onCreate(savedInstanceState: Bundle?) { @@ -151,7 +149,6 @@ class WebOverlayDesktopActivity : WebOverlayActivityBase(USER_AGENT_DESKTOP_CONS */ class WebOverlayActivity : WebOverlayActivityBase() -@UseExperimental(ExperimentalCoroutinesApi::class) @AndroidEntryPoint abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT) : BaseActivity(), diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt index 721282aa..2b7f7b2c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt @@ -19,10 +19,8 @@ package com.pitchedapps.frost.contracts import com.mikepenz.iconics.typeface.IIcon import com.pitchedapps.frost.fragments.BaseFragment import com.pitchedapps.frost.web.FrostEmitter -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharedFlow -@UseExperimental(ExperimentalCoroutinesApi::class) interface MainActivityContract : MainFabContract { val fragmentFlow: SharedFlow val fragmentEmit: FrostEmitter 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 d32cb873..7f91f901 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt @@ -20,7 +20,6 @@ import android.view.View import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.web.FrostEmitter import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharedFlow /** @@ -47,7 +46,6 @@ interface FrostContentContainer : CoroutineScope { * Contract for components shared among * all content providers */ -@UseExperimental(ExperimentalCoroutinesApi::class) interface FrostContentParent : DynamicUiContract { val scope: CoroutineScope diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt index e10fd141..a3303638 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/BaseFragment.kt @@ -44,7 +44,6 @@ import com.pitchedapps.frost.utils.REQUEST_TEXT_ZOOM import com.pitchedapps.frost.utils.frostEvent import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.launchIn @@ -59,7 +58,6 @@ import kotlin.coroutines.CoroutineContext * All fragments pertaining to the main view * Must be attached to activities implementing [MainActivityContract] */ -@UseExperimental(ExperimentalCoroutinesApi::class) @AndroidEntryPoint abstract class BaseFragment : Fragment(), 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 d2083816..c77ae590 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt @@ -44,7 +44,6 @@ import com.pitchedapps.frost.web.FrostEmitter import com.pitchedapps.frost.web.asFrostEmitter import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.flow.MutableSharedFlow @@ -96,7 +95,6 @@ abstract class FrostContentView @JvmOverloads constructor( /** * Subsection of [FrostContentView] that is [AndroidEntryPoint] friendly (no generics) */ -@UseExperimental(ExperimentalCoroutinesApi::class) @AndroidEntryPoint abstract class FrostContentViewBase( context: Context, 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 9e21ede8..04ee7f3c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt @@ -29,7 +29,6 @@ import com.pitchedapps.frost.contracts.FrostContentParent import com.pitchedapps.frost.fragments.RecyclerContentContract import com.pitchedapps.frost.prefs.Prefs import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import javax.inject.Inject @@ -37,7 +36,6 @@ import javax.inject.Inject * Created by Allan Wang on 2017-05-29. * */ -@UseExperimental(ExperimentalCoroutinesApi::class) @AndroidEntryPoint class FrostRecyclerView @JvmOverloads constructor( context: Context, -- cgit v1.2.3