Skip to content

Commit

Permalink
Refactor PromotedStoryListView.kt BindableAdapterTest.kt classes to p…
Browse files Browse the repository at this point in the history
…rovide Adapter through injection for isuue oppia#2658.
  • Loading branch information
KevinGitonga committed Jul 19, 2022
1 parent 20c1ff3 commit dfed2cb
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,30 @@ class PromotedStoryListView @JvmOverloads constructor(
lateinit var promotedDataList: List<PromotedStoryViewModel>

override fun onAttachedToWindow() {
super.onAttachedToWindow()
val viewComponentFactory =
FragmentManager.findFragment<Fragment>(this) as ViewComponentFactory
val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
viewComponent.inject(this)
try {
super.onAttachedToWindow()
val viewComponentFactory =
FragmentManager.findFragment<Fragment>(this) as ViewComponentFactory
val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
viewComponent.inject(this)

// The StartSnapHelper is used to snap between items rather than smooth scrolling, so that
// the item is completely visible in [HomeFragment] as soon as learner lifts the finger
// after scrolling.
val snapHelper = StartSnapHelper()
onFlingListener = null
snapHelper.attachToRecyclerView(this)
// The StartSnapHelper is used to snap between items rather than smooth scrolling, so that
// the item is completely visible in [HomeFragment] as soon as learner lifts the finger
// after scrolling.
val snapHelper = StartSnapHelper()
onFlingListener = null
snapHelper.attachToRecyclerView(this)

checkIfComponentsInitialized()
} catch (e: IllegalStateException) {
if (::oppiaLogger.isInitialized) {
oppiaLogger.e(
"PromotedStoryListView",
"Throws exception on attach to window",
e
)
}
}
}

private fun checkIfComponentsInitialized() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import android.view.View
import android.view.ViewGroup
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import java.lang.ref.WeakReference
import javax.inject.Inject
import kotlin.reflect.KClass

Expand Down Expand Up @@ -93,6 +95,34 @@ class BindableAdapter<T : Any> internal constructor(
internal abstract fun bind(data: T)
}

/**
* The base builder for [BindableAdapter]. This class should not be used directly--use either
* [SingleTypeBuilder] or [MultiTypeBuilder] instead.
*/
abstract class BaseBuilder(fragment: Fragment) {
/**
* A [WeakReference] to a [LifecycleOwner] for databinding inflation.
* Note that this needs to be a weak reference so that long-held references to the adapter do
* not potentially leak lifecycle owners (such as fragments and activities).
*/
private var lifecycleOwnerRef: WeakReference<LifecycleOwner> = WeakReference(fragment)

/**
* Returns the [LifecycleOwner] bound to this adapter, or null if there isn't one. This method
* will throw if there was a lifecycle owner bound but is now expired.
*/
protected fun getLifecycleOwner(): LifecycleOwner? {
// Crash if the lifecycle owner has been cleaned up since it's not valid to use the adapter
// with an old lifecycle owner, and silently ignoring this may result in part of the layout
// not responding to events.
return lifecycleOwnerRef?.let { ref ->
checkNotNull(ref.get()) {
"Attempted to inflate data binding with expired lifecycle owner"
}
}
}
}

/**
* Constructs a new [BindableAdapter] that for a single view type.
*
Expand All @@ -101,7 +131,7 @@ class BindableAdapter<T : Any> internal constructor(
class SingleTypeBuilder<T : Any>(
private val dataClassType: KClass<T>,
private val fragment: Fragment
) {
) : BaseBuilder(fragment) {
private lateinit var viewHolderFactory: ViewHolderFactory<T>

/**
Expand Down Expand Up @@ -173,10 +203,10 @@ class BindableAdapter<T : Any> internal constructor(
/* attachToRoot= */ false
)

binding.lifecycleOwner = fragment.viewLifecycleOwner
object : BindableViewHolder<T>(binding.root) {
override fun bind(data: T) {
setViewModel(binding, data)
binding.lifecycleOwner = getLifecycleOwner()
}
}
}
Expand Down Expand Up @@ -216,7 +246,7 @@ class BindableAdapter<T : Any> internal constructor(
private val dataClassType: KClass<T>,
private val computeViewType: ComputeViewType<T, E>,
private val fragment: Fragment
) {
) : BaseBuilder(fragment) {
private var viewHolderFactoryMap: MutableMap<E, ViewHolderFactory<T>> = HashMap()

/**
Expand Down Expand Up @@ -301,10 +331,10 @@ class BindableAdapter<T : Any> internal constructor(
/* attachToRoot= */ false
)

binding.lifecycleOwner = fragment.viewLifecycleOwner
object : BindableViewHolder<T>(binding.root) {
override fun bind(data: T) {
setViewModel(binding, transformViewModel(data))
binding.lifecycleOwner = getLifecycleOwner()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import javax.inject.Inject
/** The test-only fragment presenter corresponding to [BindableAdapterTestFragment]. */
class BindableAdapterTestFragmentPresenter @Inject constructor(
private val fragment: Fragment,
private val singleTypeBuilder: BindableAdapter.SingleTypeBuilder.Factory,
private val multiTypeBuilder: BindableAdapter.MultiTypeBuilder.Factory,
private val testBindableAdapterFactory: BindableAdapterFactory,
@VisibleForTesting val viewModel: BindableAdapterTestViewModel
) {
Expand All @@ -22,7 +24,7 @@ class BindableAdapterTestFragmentPresenter @Inject constructor(
/* attachToRoot= */ false
)
binding.testRecyclerView.apply {
adapter = testBindableAdapterFactory.create(fragment)
adapter = testBindableAdapterFactory.create(singleTypeBuilder, multiTypeBuilder)
}
binding.let {
it.viewModel = viewModel
Expand All @@ -33,6 +35,9 @@ class BindableAdapterTestFragmentPresenter @Inject constructor(

/** Factory for creating new [BindableAdapter]s for the current fragment. */
interface BindableAdapterFactory {
fun create(fragment: Fragment): BindableAdapter<BindableAdapterTestDataModel>
fun create(
singleTypeBuilder: BindableAdapter.SingleTypeBuilder.Factory,
multiTypeBuilder: BindableAdapter.MultiTypeBuilder.Factory
): BindableAdapter<BindableAdapterTestDataModel>
}
}
Loading

0 comments on commit dfed2cb

Please sign in to comment.