Compare commits

...

1 commit

Author SHA1 Message Date
Osten
cb5d517da0
fixed observe, aka #2567 2026-04-29 22:28:32 +02:00
4 changed files with 62 additions and 13 deletions

View file

@ -1,16 +1,68 @@
package com.lagradost.cloudstream3.mvvm package com.lagradost.cloudstream3.mvvm
import android.view.View
import androidx.activity.ComponentActivity
import androidx.core.view.doOnAttach
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.viewbinding.ViewBinding
import com.lagradost.cloudstream3.ui.BaseFragment
/** NOTE: Only one observer at a time per value */ /** NOTE: Only one observer at a time per value */
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) { fun <T> ComponentActivity.observe(liveData: LiveData<T>, action: (T) -> Unit) {
liveData.removeObservers(this) observeNullable(liveData) { t -> t?.run(action) }
liveData.observe(this) { it?.let { t -> action(t) } }
} }
/** NOTE: Only one observer at a time per value */ /** NOTE: Only one observer at a time per value */
fun <T> LifecycleOwner.observeNullable(liveData: LiveData<T>, action: (t: T) -> Unit) { fun <T> ComponentActivity.observeNullable(liveData: LiveData<T>, action: (T?) -> Unit) {
liveData.removeObservers(this) liveData.removeObservers(this)
liveData.observe(this) { action(it) } liveData.observe(this, action)
}
/** NOTE: Only one observer at a time per value */
fun <T, V : ViewBinding> BaseFragment<V>.observe(liveData: LiveData<T>, action: (T) -> Unit) {
observeNullable(liveData) { t -> t?.run(action) }
}
/**
* Attaches an observable to the root binding, instead of the fragment. This is more efficient as
* it will not call observe if the view is in the background.
*
* NOTE: Only one observer at a time per value
* */
fun <T, V : ViewBinding> BaseFragment<V>.observeNullable(
liveData: LiveData<T>, action: (T?) -> Unit
) {
val root = this.binding?.root
if (root == null) {
liveData.removeObservers(this)
liveData.observe(this, action)
} else {
root.doOnAttach { view ->
// On attach should make findViewTreeLifecycleOwner non-null, but use "this" just in case
val owner: LifecycleOwner = view.findViewTreeLifecycleOwner() ?: this@observeNullable
liveData.removeObservers(owner)
liveData.observe(owner, action)
}
}
}
/** NOTE: Only one observer at a time per value */
fun <T> View.observe(liveData: LiveData<T>, action: (T) -> Unit) {
observeNullable(liveData) { t -> t?.run(action) }
}
/** NOTE: Only one observer at a time per value */
fun <T> View.observeNullable(liveData: LiveData<T>, action: (T?) -> Unit) {
doOnAttach { view ->
// On attach should make findViewTreeLifecycleOwner non-null
val owner: LifecycleOwner? = view.findViewTreeLifecycleOwner()
if(owner == null) {
debugException { "Expected non-null findViewTreeLifecycleOwner" }
return@doOnAttach
}
liveData.removeObservers(owner)
liveData.observe(owner, action)
}
} }

View file

@ -651,7 +651,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(
} }
homeMasterAdapter = HomeParentItemAdapterPreview( homeMasterAdapter = HomeParentItemAdapterPreview(
fragment = this@HomeFragment,
homeViewModel, accountViewModel homeViewModel, accountViewModel
) )
homeMasterRecycler.setRecycledViewPool(ParentItemAdapter.sharedPool) homeMasterRecycler.setRecycledViewPool(ParentItemAdapter.sharedPool)

View file

@ -63,7 +63,6 @@ import androidx.core.graphics.toColorInt
import com.lagradost.cloudstream3.ui.setRecycledViewPool import com.lagradost.cloudstream3.ui.setRecycledViewPool
class HomeParentItemAdapterPreview( class HomeParentItemAdapterPreview(
val fragment: LifecycleOwner,
private val viewModel: HomeViewModel, private val viewModel: HomeViewModel,
private val accountViewModel: AccountViewModel private val accountViewModel: AccountViewModel
) : ParentItemAdapter( ) : ParentItemAdapter(
@ -105,7 +104,7 @@ class HomeParentItemAdapterPreview(
) )
} }
return HeaderViewHolder(binding, viewModel, accountViewModel, fragment) return HeaderViewHolder(binding, viewModel, accountViewModel)
} }
override fun onBindHeader(holder: ViewHolderState<Bundle>) { override fun onBindHeader(holder: ViewHolderState<Bundle>) {
@ -132,7 +131,6 @@ class HomeParentItemAdapterPreview(
val binding: ViewBinding, val binding: ViewBinding,
val viewModel: HomeViewModel, val viewModel: HomeViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
fragment: LifecycleOwner,
) : ) :
ViewHolderState<Bundle>(binding) { ViewHolderState<Bundle>(binding) {
@ -544,7 +542,7 @@ class HomeParentItemAdapterPreview(
headProfilePicCard?.isGone = isLayout(TV or EMULATOR) headProfilePicCard?.isGone = isLayout(TV or EMULATOR)
alternateHeadProfilePicCard?.isGone = isLayout(TV or EMULATOR) alternateHeadProfilePicCard?.isGone = isLayout(TV or EMULATOR)
fragment.observe(viewModel.currentAccount) { currentAccount -> (headProfilePic ?: alternateHeadProfilePic)?.observe(viewModel.currentAccount) { currentAccount ->
headProfilePic?.loadImage(currentAccount?.image) headProfilePic?.loadImage(currentAccount?.image)
alternateHeadProfilePic?.loadImage(currentAccount?.image) alternateHeadProfilePic?.loadImage(currentAccount?.image)
} }
@ -775,7 +773,7 @@ class HomeParentItemAdapterPreview(
fun onViewAttachedToWindow() { fun onViewAttachedToWindow() {
previewViewpager.registerOnPageChangeCallback(previewCallback) previewViewpager.registerOnPageChangeCallback(previewCallback)
binding.root.findViewTreeLifecycleOwner()?.apply { previewViewpager.apply {
observe(viewModel.preview) { observe(viewModel.preview) {
updatePreview(it) updatePreview(it)
} }
@ -800,7 +798,7 @@ class HomeParentItemAdapterPreview(
} }
toggleListHolder?.isGone = visible.isEmpty() toggleListHolder?.isGone = visible.isEmpty()
} }
} ?: debugException { "Expected findViewTreeLifecycleOwner" } }
} }
} }
} }

View file

@ -40,7 +40,7 @@ class TestFragment : BaseFragment<FragmentTestingBinding>(
providerTest.setProgress(passed, failed, total) providerTest.setProgress(passed, failed, total)
} }
observeNullable(testViewModel.providerResults) { observe(testViewModel.providerResults) {
safe { safe {
val newItems = it.sortedBy { api -> api.first.name } val newItems = it.sortedBy { api -> api.first.name }
(providerTestRecyclerView.adapter as? TestResultAdapter)?.submitList( (providerTestRecyclerView.adapter as? TestResultAdapter)?.submitList(