From d5b205e8bab3bac7f1bc4fe1a6b4fb31ade3d038 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 9 Jul 2024 09:25:21 +0530 Subject: [PATCH] seletion state retain support in question player --- .../android/app/player/state/StateFragment.kt | 8 ++++---- .../questionplayer/QuestionPlayerFragment.kt | 19 ++++++++++++++++++- .../QuestionPlayerFragmentPresenter.kt | 14 ++++++++++++-- .../questionplayer/QuestionPlayerViewModel.kt | 8 ++++++++ .../item_selection_interaction_items.xml | 3 +-- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt index 44c3d333252..8b848ab3c44 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt @@ -43,8 +43,8 @@ class StateFragment : /** Arguments key for StateFragment. */ const val STATE_FRAGMENT_ARGUMENTS_KEY = "StateFragment.arguments" - /** Arguments key for StateFragment. */ - const val STATE_FRAGMENT_USER_ANSWER_STATE_KEY = "StateFragment.user_answer_state" + /** Arguments key for StateFragment saved state. */ + const val STATE_FRAGMENT_STATE_KEY = "StateFragment.state" /** * Creates a new instance of a StateFragment. @@ -92,7 +92,7 @@ class StateFragment : arguments?.getProto(STATE_FRAGMENT_ARGUMENTS_KEY, StateFragmentArguments.getDefaultInstance()) val userAnswerState = savedInstanceState?.getProto( - STATE_FRAGMENT_USER_ANSWER_STATE_KEY, + STATE_FRAGMENT_STATE_KEY, UserAnswerState.getDefaultInstance() ) ?: UserAnswerState.getDefaultInstance() @@ -161,7 +161,7 @@ class StateFragment : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putProto( - STATE_FRAGMENT_USER_ANSWER_STATE_KEY, + STATE_FRAGMENT_STATE_KEY, stateFragmentPresenter.getUserAnswerState() ) } diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt index f92e0884e3d..180b15029b8 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt @@ -22,6 +22,8 @@ import org.oppia.android.app.player.state.listener.SubmitNavigationButtonListene import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState +import org.oppia.android.app.player.state.StateFragment /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -52,8 +54,12 @@ class QuestionPlayerFragment : val args = checkNotNull(arguments) { "Expected arguments to be passed to QuestionPlayerFragment" } + val userAnswerState = savedInstanceState?.getProto( + QUESTION_PLAYER_FRAGMENT_STATE_KEY, + UserAnswerState.getDefaultInstance() + ) ?: UserAnswerState.getDefaultInstance() val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) - return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId) + return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId,userAnswerState) } override fun onAnswerReadyForSubmission(answer: UserAnswer) { @@ -98,6 +104,9 @@ class QuestionPlayerFragment : companion object { private const val PROFILE_ID_ARGUMENT_KEY = "QuestionPlayerFragment.profile_id" + /** Arguments key for QuestionPlayerFragment saved state. */ + const val QUESTION_PLAYER_FRAGMENT_STATE_KEY = "QuestionPlayerFragment.state" + /** * Creates a new fragment to play a question session. * @@ -112,4 +121,12 @@ class QuestionPlayerFragment : } } } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putProto( + QUESTION_PLAYER_FRAGMENT_STATE_KEY, + questionPlayerFragmentPresenter.getUserAnswerState() + ) + } } 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 78a978ba51c..749ec8881e3 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 @@ -36,6 +36,7 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.gcsresource.QuestionResourceBucketName import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** The presenter for [QuestionPlayerFragment]. */ @FragmentScope @@ -69,7 +70,8 @@ class QuestionPlayerFragmentPresenter @Inject constructor( fun handleCreateView( inflater: LayoutInflater, container: ViewGroup?, - profileId: ProfileId + profileId: ProfileId, + userAnswerState: UserAnswerState ): View? { binding = QuestionPlayerFragmentBinding.inflate( inflater, @@ -79,7 +81,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( this.profileId = profileId recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, "skill", profileId), + assemblerBuilderFactory.create(resourceBucketName, "skill", profileId,userAnswerState), binding.congratulationsTextView, binding.congratulationsTextConfettiView ) @@ -154,6 +156,11 @@ class QuestionPlayerFragmentPresenter @Inject constructor( recyclerViewAssembler.adapter.notifyDataSetChanged() } + /** Returns the [UserAnswerState] representing the user's current pending answer. */ + fun getUserAnswerState(): UserAnswerState { + return questionViewModel.getUserAnswerState(recyclerViewAssembler::getPendingAnswerHandler) + } + /** * Updates whether the submit button should be active based on whether the pending answer is in an * error state. @@ -257,6 +264,9 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private fun subscribeToAnswerOutcome( answerOutcomeResultLiveData: LiveData> ) { + if (questionViewModel.getCanSubmitAnswer().get() == true) { + recyclerViewAssembler.resetUserAnswerState() + } val answerOutcomeLiveData = Transformations.map(answerOutcomeResultLiveData, ::processAnsweredQuestionOutcome) answerOutcomeLiveData.observe( diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt index ee470eb7b66..b51189d97a9 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt @@ -12,6 +12,7 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.viewmodel.ObservableArrayList import org.oppia.android.app.viewmodel.ObservableViewModel import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** [ObservableViewModel] for the question player. */ class QuestionPlayerViewModel @Inject constructor( @@ -92,6 +93,13 @@ class QuestionPlayerViewModel @Inject constructor( } } + fun getUserAnswerState( + retrieveAnswerHandler: (List) -> InteractionAnswerHandler? + ): UserAnswerState { + return retrieveAnswerHandler(getAnswerItemList())?.getUserAnswerState() + ?: UserAnswerState.getDefaultInstance() + } + private fun getPendingAnswerWithoutError( answerHandler: InteractionAnswerHandler? ): UserAnswer? { diff --git a/app/src/main/res/layout/item_selection_interaction_items.xml b/app/src/main/res/layout/item_selection_interaction_items.xml index ac4434d55d1..45aa17d6d2b 100755 --- a/app/src/main/res/layout/item_selection_interaction_items.xml +++ b/app/src/main/res/layout/item_selection_interaction_items.xml @@ -27,14 +27,13 @@ android:id="@+id/item_selection_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" + app:buttonTint="@{viewModel.isEnabled ? @color/component_color_shared_item_selection_interaction_enabled_color : @color/component_color_shared_item_selection_interaction_disabled_color}" android:clickable="false" android:enabled="@{viewModel.isEnabled}" android:focusable="false" android:labelFor="@id/item_selection_contents_text_view" - app:buttonTint="@{viewModel.isEnabled ? @color/component_color_shared_item_selection_interaction_enabled_color : @color/component_color_shared_item_selection_interaction_disabled_color}" android:checked="@{viewModel.answerSelected}" /> -