From 20c1ff3aa8c4c34d25048e2dbce847845b36538b Mon Sep 17 00:00:00 2001 From: kevingitonga Date: Mon, 18 Jul 2022 20:22:55 +0300 Subject: [PATCH] More updates as part of refactoring for issue #2658. --- .../AdministratorControlsFragmentPresenter.kt | 7 +- .../ProfileAndDeviceIdFragmentPresenter.kt | 7 +- .../MarkChaptersCompletedFragmentPresenter.kt | 5 +- .../promotedlist/ComingSoonTopicsListView.kt | 3 - .../promotedlist/PromotedStoryListView.kt | 31 ++++++-- .../options/AppLanguageFragmentPresenter.kt | 1 - .../options/AudioLanguageFragmentPresenter.kt | 1 - .../ReadingTextSizeFragmentPresenter.kt | 2 +- .../state/DragDropSortInteractionView.kt | 3 - .../player/state/SelectionInteractionView.kt | 6 +- .../player/state/StateFragmentPresenter.kt | 11 +-- .../state/StatePlayerRecyclerViewAssembler.kt | 33 ++++----- .../ProfileChooserFragmentPresenter.kt | 5 +- .../ProfileProgressFragmentPresenter.kt | 10 +-- .../app/recyclerview/BindableAdapter.kt | 70 ++++--------------- .../lessons/TopicLessonsFragmentPresenter.kt | 6 +- .../QuestionPlayerFragmentPresenter.kt | 5 +- 17 files changed, 77 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt index da96366f5cf..6fc33e4c2b0 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt @@ -31,7 +31,8 @@ import javax.inject.Inject @FragmentScope class AdministratorControlsFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, - private val fragment: Fragment + private val fragment: Fragment, + private val multiTypeBuilder: BindableAdapter.MultiTypeBuilder.Factory ) { private lateinit var binding: AdministratorControlsFragmentBinding private lateinit var linearLayoutManager: LinearLayoutManager @@ -77,8 +78,8 @@ class AdministratorControlsFragmentPresenter @Inject constructor( /** Returns the recycler view adapter for the controls panel in administrator controls fragment. */ private fun createRecyclerViewAdapter(isMultipane: Boolean): BindableAdapter { - return BindableAdapter.MultiTypeBuilder - .Factory(fragment).create { viewModel -> + return multiTypeBuilder + .create { viewModel -> viewModel.isMultipane.set(isMultipane) when (viewModel) { is AdministratorControlsGeneralViewModel -> { diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt index 542e712a40f..ca1b5cb1006 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt @@ -17,7 +17,7 @@ import javax.inject.Inject class ProfileAndDeviceIdFragmentPresenter @Inject constructor( private val fragment: Fragment, private val profileListViewModelFactory: ProfileListViewModel.Factory, - private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory + private val adapterFactory: BindableAdapter.MultiTypeBuilder.Factory ) { private lateinit var binding: ProfileAndDeviceIdFragmentBinding @@ -40,15 +40,14 @@ class ProfileAndDeviceIdFragmentPresenter @Inject constructor( } private fun createRecyclerViewAdapter(): BindableAdapter { - return multiTypeBuilderFactory.create { viewModel -> + return adapterFactory.create { viewModel -> when (viewModel) { is DeviceIdItemViewModel -> ProfileListItemViewType.DEVICE_ID is ProfileLearnerIdItemViewModel -> ProfileListItemViewType.LEARNER_ID is SyncStatusItemViewModel -> ProfileListItemViewType.SYNC_STATUS else -> error("Encountered unexpected view model: $viewModel") } - }.setLifecycleOwner(fragment) + } .registerViewDataBinder( viewType = ProfileListItemViewType.DEVICE_ID, inflateDataBinding = ProfileListDeviceIdItemBinding::inflate, diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt index fee0646f45f..63c448d7a88 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt @@ -24,7 +24,7 @@ class MarkChaptersCompletedFragmentPresenter @Inject constructor( private val fragment: Fragment, private val viewModelProvider: ViewModelProvider, private val modifyLessonProgressController: ModifyLessonProgressController, - private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory + private val adapterFactory: BindableAdapter.MultiTypeBuilder.Factory ) : ChapterSelector { private lateinit var binding: MarkChaptersCompletedFragmentBinding private lateinit var linearLayoutManager: LinearLayoutManager @@ -95,8 +95,7 @@ class MarkChaptersCompletedFragmentPresenter @Inject constructor( } private fun createRecyclerViewAdapter(): BindableAdapter { - return multiTypeBuilderFactory.create { viewModel -> + return adapterFactory.create { viewModel -> when (viewModel) { is StorySummaryViewModel -> ViewType.VIEW_TYPE_STORY is ChapterSummaryViewModel -> ViewType.VIEW_TYPE_CHAPTER diff --git a/app/src/main/java/org/oppia/android/app/home/promotedlist/ComingSoonTopicsListView.kt b/app/src/main/java/org/oppia/android/app/home/promotedlist/ComingSoonTopicsListView.kt index e386b94ebf5..04239be8b94 100644 --- a/app/src/main/java/org/oppia/android/app/home/promotedlist/ComingSoonTopicsListView.kt +++ b/app/src/main/java/org/oppia/android/app/home/promotedlist/ComingSoonTopicsListView.kt @@ -32,9 +32,6 @@ class ComingSoonTopicsListView @JvmOverloads constructor( @Inject lateinit var oppiaLogger: OppiaLogger - @Inject - lateinit var fragment: Fragment - @Inject lateinit var singleTypeAdapterFactory: BindableAdapter.SingleTypeBuilder.Factory diff --git a/app/src/main/java/org/oppia/android/app/home/promotedlist/PromotedStoryListView.kt b/app/src/main/java/org/oppia/android/app/home/promotedlist/PromotedStoryListView.kt index e02141e63c5..05469ded4bb 100644 --- a/app/src/main/java/org/oppia/android/app/home/promotedlist/PromotedStoryListView.kt +++ b/app/src/main/java/org/oppia/android/app/home/promotedlist/PromotedStoryListView.kt @@ -38,10 +38,12 @@ class PromotedStoryListView @JvmOverloads constructor( @Inject lateinit var singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + lateinit var promotedDataList: List + override fun onAttachedToWindow() { super.onAttachedToWindow() - - val viewComponentFactory = FragmentManager.findFragment(this) as ViewComponentFactory + val viewComponentFactory = + FragmentManager.findFragment(this) as ViewComponentFactory val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl viewComponent.inject(this) @@ -53,12 +55,31 @@ class PromotedStoryListView @JvmOverloads constructor( snapHelper.attachToRecyclerView(this) } + private fun checkIfComponentsInitialized() { + if (::bindingInterface.isInitialized && + ::bindingInterface.isInitialized && + ::oppiaLogger.isInitialized && + ::fragment.isInitialized && + ::singleTypeBuilderFactory.isInitialized && + ::promotedDataList.isInitialized + ) { + bindDataToAdapter() + } + } + /** * Sets the list of promoted stories that this view shows to the learner. * * @param newDataList the new list of stories to present */ fun setPromotedStoryList(newDataList: List?) { + if (newDataList != null) { + promotedDataList = newDataList + checkIfComponentsInitialized() + } + } + + private fun bindDataToAdapter() { // To reliably bind data only after the adapter is created, we manually set the data so we can first // check for the adapter; when using an existing [RecyclerViewBindingAdapter] there is no reliable // way to check that the adapter is created. @@ -67,10 +88,10 @@ class PromotedStoryListView @JvmOverloads constructor( if (adapter == null) { adapter = createAdapter() } - if (newDataList == null) { - oppiaLogger.w(PROMOTED_STORY_LIST_VIEW_TAG, "Failed to resolve new story list data") + if (::promotedDataList.isInitialized) { + (adapter as BindableAdapter<*>).setDataUnchecked(promotedDataList) } else { - (adapter as BindableAdapter<*>).setDataUnchecked(newDataList) + oppiaLogger.w(PROMOTED_STORY_LIST_VIEW_TAG, "Failed to resolve new story list data") } } diff --git a/app/src/main/java/org/oppia/android/app/options/AppLanguageFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/options/AppLanguageFragmentPresenter.kt index ebadf3921a0..1433ae55098 100644 --- a/app/src/main/java/org/oppia/android/app/options/AppLanguageFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/AppLanguageFragmentPresenter.kt @@ -43,7 +43,6 @@ class AppLanguageFragmentPresenter @Inject constructor( private fun createRecyclerViewAdapter(): BindableAdapter { return singleTypeBuilderFactory.create() - .setLifecycleOwner(fragment) .registerViewDataBinderWithSameModelType( inflateDataBinding = LanguageItemsBinding::inflate, setViewModel = LanguageItemsBinding::setViewModel diff --git a/app/src/main/java/org/oppia/android/app/options/AudioLanguageFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/options/AudioLanguageFragmentPresenter.kt index 89e19749e11..de64c710f08 100644 --- a/app/src/main/java/org/oppia/android/app/options/AudioLanguageFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/AudioLanguageFragmentPresenter.kt @@ -43,7 +43,6 @@ class AudioLanguageFragmentPresenter @Inject constructor( private fun createRecyclerViewAdapter(): BindableAdapter { return singleTypeBuilderFactory.create() - .setLifecycleOwner(fragment) .registerViewDataBinderWithSameModelType( inflateDataBinding = LanguageItemsBinding::inflate, setViewModel = LanguageItemsBinding::setViewModel diff --git a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragmentPresenter.kt index 9ea9259672a..6a1d4b145bf 100644 --- a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragmentPresenter.kt @@ -53,7 +53,7 @@ class ReadingTextSizeFragmentPresenter @Inject constructor( .registerViewDataBinderWithSameModelType( inflateDataBinding = TextSizeItemsBinding::inflate, setViewModel = TextSizeItemsBinding::setViewModel - ).setLifecycleOwner(fragment) + ) .build() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt index 7632ada52ae..289bcc69301 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt @@ -53,9 +53,6 @@ class DragDropSortInteractionView @JvmOverloads constructor( @Inject lateinit var viewBindingShim: ViewBindingShim - @Inject - lateinit var fragment: Fragment - @Inject lateinit var singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory diff --git a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt index d1785f22bdc..b7dcf363372 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt @@ -45,9 +45,6 @@ class SelectionInteractionView @JvmOverloads constructor( @Inject lateinit var bindingInterface: ViewBindingShim - @Inject - lateinit var fragment: Fragment - @Inject lateinit var singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory @@ -112,8 +109,7 @@ class SelectionInteractionView @JvmOverloads constructor( ) .build() SelectionItemInputType.RADIO_BUTTONS -> - BindableAdapter.SingleTypeBuilder - .Factory(fragment).create() + singleTypeBuilderFactory.create() .registerViewBinder( inflateView = { parent -> bindingInterface.provideMultipleChoiceInteractionItemsInflatedView( diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt index d7145ec98ef..f0d22cecad7 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt @@ -31,7 +31,6 @@ import org.oppia.android.app.player.state.ConfettiConfig.MEDIUM_CONFETTI_BURST import org.oppia.android.app.player.state.ConfettiConfig.MINI_CONFETTI_BURST import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListener import org.oppia.android.app.player.stopplaying.StopStatePlayingSessionWithSavedProgressListener -import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.topic.conceptcard.ConceptCardFragment.Companion.CONCEPT_CARD_DIALOG_FRAGMENT_TAG import org.oppia.android.app.utility.SplitScreenManager import org.oppia.android.app.viewmodel.ViewModelProvider @@ -69,8 +68,7 @@ class StateFragmentPresenter @Inject constructor( @DefaultResourceBucketName private val resourceBucketName: String, private val assemblerBuilderFactory: StatePlayerRecyclerViewAssembler.Builder.Factory, private val splitScreenManager: SplitScreenManager, - private val oppiaClock: OppiaClock, - private val multiTypeAdapterBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory + private val oppiaClock: OppiaClock ) { private val routeToHintsAndSolutionListener = activity as RouteToHintsAndSolutionListener @@ -118,8 +116,7 @@ class StateFragmentPresenter @Inject constructor( assemblerBuilderFactory.create(resourceBucketName, entityType, profileId), binding.congratulationsTextView, binding.congratulationsTextConfettiView, - binding.fullScreenConfettiView, - multiTypeAdapterBuilderFactory + binding.fullScreenConfettiView ) val stateRecyclerViewAdapter = recyclerViewAssembler.adapter @@ -220,12 +217,10 @@ class StateFragmentPresenter @Inject constructor( builder: StatePlayerRecyclerViewAssembler.Builder, congratulationsTextView: TextView, congratulationsTextConfettiView: KonfettiView, - fullScreenConfettiView: KonfettiView, - multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory + fullScreenConfettiView: KonfettiView ): StatePlayerRecyclerViewAssembler { val isTablet = context.resources.getBoolean(R.bool.isTablet) return builder - .addAdapterBuilderFactory(multiTypeBuilderFactory) .hasConversationView(hasConversationView) .addContentSupport() .addFeedbackSupport() diff --git a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt index c93c6689758..b30a6843920 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt @@ -887,11 +887,13 @@ class StatePlayerRecyclerViewAssembler private constructor( private val interactionViewModelFactoryMap: Map, private val backgroundCoroutineDispatcher: CoroutineDispatcher, private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + private val adapterBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, + private val singleAdapterFactory: BindableAdapter.SingleTypeBuilder.Factory ) { - private lateinit var adapterBuilder: - BindableAdapter.MultiTypeBuilder + private var adapterBuilder: BindableAdapter.MultiTypeBuilder = adapterBuilderFactory.create { it.viewType } /** * Tracks features individually enabled for the assembler. No features are enabled by default. @@ -1116,8 +1118,7 @@ class StatePlayerRecyclerViewAssembler private constructor( gcsEntityId: String, supportsConceptCards: Boolean ): BindableAdapter { - return BindableAdapter.SingleTypeBuilder - .Factory(fragment).create() + return singleAdapterFactory.create() .registerViewBinder( inflateView = { parent -> SubmittedAnswerListItemBinding.inflate( @@ -1138,8 +1139,7 @@ class StatePlayerRecyclerViewAssembler private constructor( gcsEntityId: String, supportsConceptCards: Boolean ): BindableAdapter { - return BindableAdapter.SingleTypeBuilder - .Factory(fragment).create() + return singleAdapterFactory.create() .registerViewBinder( inflateView = { parent -> SubmittedHtmlAnswerItemBinding.inflate( @@ -1183,17 +1183,6 @@ class StatePlayerRecyclerViewAssembler private constructor( } } - /** - * Add AdapterBuilderFactory passed through injection from [StateFragmentPresenter] - */ - fun addAdapterBuilderFactory - (multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory): Builder { - adapterBuilder = multiTypeBuilderFactory.create { - it.viewType - } - return this - } - /** * Adds support for automatically collapsing past wrong answers. This feature is not enabled * without [addPastAnswersSupport] also being enabled. @@ -1400,7 +1389,9 @@ class StatePlayerRecyclerViewAssembler private constructor( String, @JvmSuppressWildcards InteractionItemFactory>, @BackgroundDispatcher private val backgroundCoroutineDispatcher: CoroutineDispatcher, private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + private val multiAdapterBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, + private val singleAdapterFactory: BindableAdapter.SingleTypeBuilder.Factory ) { /** * Returns a new [Builder] for the specified GCS resource bucket information for loading @@ -1418,7 +1409,9 @@ class StatePlayerRecyclerViewAssembler private constructor( interactionViewModelFactoryMap, backgroundCoroutineDispatcher, resourceHandler, - translationController + translationController, + multiAdapterBuilderFactory, + singleAdapterFactory ) } } diff --git a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt index 5e71216c7a1..b3a86046b56 100644 --- a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt @@ -66,7 +66,7 @@ class ProfileChooserFragmentPresenter @Inject constructor( private val viewModelProvider: ViewModelProvider, private val profileManagementController: ProfileManagementController, private val oppiaLogger: OppiaLogger, - private val multiTypeAdapterBuilder: BindableAdapter.MultiTypeBuilder.Factory + private val adapterBuilder: BindableAdapter.MultiTypeBuilder.Factory ) { private lateinit var binding: ProfileChooserFragmentBinding val hasProfileEverBeenAddedValue = ObservableField(true) @@ -149,8 +149,7 @@ class ProfileChooserFragmentPresenter @Inject constructor( } private fun createRecyclerViewAdapter(): BindableAdapter { - return multiTypeAdapterBuilder.create( + return adapterBuilder.create( ProfileChooserUiModel::getModelTypeCase ) .registerViewDataBinderWithSameModelType( diff --git a/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentPresenter.kt index c449ee04532..e39fdbed8a7 100644 --- a/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentPresenter.kt @@ -20,15 +20,11 @@ private const val TAG_PROFILE_PICTURE_EDIT_DIALOG = "PROFILE_PICTURE_EDIT_DIALOG @FragmentScope class ProfileProgressFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, - private val fragment: Fragment + private val fragment: Fragment, + private val viewModel: ProfileProgressViewModel, + private val multiTypeAdapterBuilder: BindableAdapter.MultiTypeBuilder.Factory ) { - @Inject - lateinit var viewModel: ProfileProgressViewModel - - @Inject - lateinit var multiTypeAdapterBuilder: BindableAdapter.MultiTypeBuilder.Factory - fun handleCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/org/oppia/android/app/recyclerview/BindableAdapter.kt b/app/src/main/java/org/oppia/android/app/recyclerview/BindableAdapter.kt index 5fa53d2d6f8..cb013f8d859 100644 --- a/app/src/main/java/org/oppia/android/app/recyclerview/BindableAdapter.kt +++ b/app/src/main/java/org/oppia/android/app/recyclerview/BindableAdapter.kt @@ -5,9 +5,7 @@ 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 @@ -95,55 +93,6 @@ class BindableAdapter 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 { - /** - * A [WeakReference] to a [LifecycleOwner] for databinding inflation. See [setLifecycleOwner]. - * 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? = null - - /** - * Sets the [LifecycleOwner] corresponding to this adapter. This will automatically be used as - * the lifecycle owner for all databinding classes created during view inflation. Note that the - * adapter holds a weak reference to the owner to ensure long-lived references to the adapter - * class itself does not result in leaks, however it's up to the caller's responsibility to make - * sure that the adapter itself is not actually used after the lifecycle owner has expired - * (otherwise the app may crash). - * - * @return this - */ - fun setLifecycleOwner(lifecycleOwner: LifecycleOwner): BuilderType { - check(lifecycleOwnerRef == null) { - "A lifecycle owner has already been bound to this adapter." - } - lifecycleOwnerRef = WeakReference(lifecycleOwner) - - // This cast is not, strictly speaking, safe, but child classes are expected to inherit from - // the builder & pass their own type in. - @Suppress("UNCHECKED_CAST") return this as BuilderType - } - - /** - * 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. * @@ -152,7 +101,7 @@ class BindableAdapter internal constructor( class SingleTypeBuilder( private val dataClassType: KClass, private val fragment: Fragment - ) : BaseBuilder>() { + ) { private lateinit var viewHolderFactory: ViewHolderFactory /** @@ -223,7 +172,8 @@ class BindableAdapter internal constructor( viewGroup, /* attachToRoot= */ false ) - binding.lifecycleOwner = getLifecycleOwner() + + binding.lifecycleOwner = fragment.viewLifecycleOwner object : BindableViewHolder(binding.root) { override fun bind(data: T) { setViewModel(binding, data) @@ -243,9 +193,13 @@ class BindableAdapter internal constructor( ) } + /** Fragment injectable factory to create new [SingleTypeBuilder] */ class Factory @Inject constructor( val fragment: Fragment ) { + /** + * Returns a new [SingleTypeBuilder] for the specified Data class type. + */ inline fun create(): SingleTypeBuilder { return SingleTypeBuilder(T::class, fragment) } @@ -262,7 +216,7 @@ class BindableAdapter internal constructor( private val dataClassType: KClass, private val computeViewType: ComputeViewType, private val fragment: Fragment - ) : BaseBuilder>() { + ) { private var viewHolderFactoryMap: MutableMap> = HashMap() /** @@ -346,7 +300,8 @@ class BindableAdapter internal constructor( viewGroup, /* attachToRoot= */ false ) - binding.lifecycleOwner = getLifecycleOwner() + + binding.lifecycleOwner = fragment.viewLifecycleOwner object : BindableViewHolder(binding.root) { override fun bind(data: T) { setViewModel(binding, transformViewModel(data)) @@ -374,9 +329,14 @@ class BindableAdapter internal constructor( ) } + /** Fragment injectable factory to create new [MultiTypeBuilder] */ class Factory @Inject constructor( val fragment: Fragment ) { + + /** + * Returns a new [MultiTypeBuilder] for the specified data class type. + */ inline fun > create( noinline computeViewType: ComputeViewType ): MultiTypeBuilder { diff --git a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt index ca5ec11b19f..e148a5aba53 100644 --- a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt @@ -35,7 +35,8 @@ class TopicLessonsFragmentPresenter @Inject constructor( private val oppiaLogger: OppiaLogger, private val explorationDataController: ExplorationDataController, private val explorationCheckpointController: ExplorationCheckpointController, - private val multiTypeAdapterFactory: BindableAdapter.MultiTypeBuilder.Factory + private val multiTypeAdapterFactory: BindableAdapter.MultiTypeBuilder.Factory, + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory ) { private val routeToResumeLessonListener = activity as RouteToResumeLessonListener @@ -207,8 +208,7 @@ class TopicLessonsFragmentPresenter @Inject constructor( } private fun createChapterRecyclerViewAdapter(): BindableAdapter { - return BindableAdapter.SingleTypeBuilder - .Factory(fragment).create() + return singleTypeBuilderFactory.create() .registerViewDataBinderWithSameModelType( inflateDataBinding = LessonsChapterViewBinding::inflate, setViewModel = LessonsChapterViewBinding::setViewModel diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt index f16eec1ca16..596ee8287d8 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt @@ -25,7 +25,6 @@ import org.oppia.android.app.player.state.StatePlayerRecyclerViewAssembler import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListener import org.oppia.android.app.player.stopplaying.RestartPlayingSessionListener import org.oppia.android.app.player.stopplaying.StopStatePlayingSessionListener -import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.topic.conceptcard.ConceptCardFragment.Companion.CONCEPT_CARD_DIALOG_FRAGMENT_TAG import org.oppia.android.app.utility.SplitScreenManager import org.oppia.android.app.viewmodel.ViewModelProvider @@ -48,8 +47,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private val oppiaLogger: OppiaLogger, @QuestionResourceBucketName private val resourceBucketName: String, private val assemblerBuilderFactory: StatePlayerRecyclerViewAssembler.Builder.Factory, - private val splitScreenManager: SplitScreenManager, - private val multiTypeAdapterFactory: BindableAdapter.MultiTypeBuilder.Factory + private val splitScreenManager: SplitScreenManager ) { // TODO(#503): Add tests for the question player. @@ -332,7 +330,6 @@ class QuestionPlayerFragmentPresenter @Inject constructor( // controller & possibly the ephemeral question data model. // TODO(#502): Add support for surfacing skills that need to be reviewed by the learner. return builder - .addAdapterBuilderFactory(multiTypeAdapterFactory) .hasConversationView(hasConversationView) .addContentSupport() .addFeedbackSupport()