From 1b72693087bb04959ef08dfd4c054ee445e6e958 Mon Sep 17 00:00:00 2001 From: Vishwajith Shettigar <76042077+Vishwajith-Shettigar@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:49:58 +0530 Subject: [PATCH] Fix #4362, #1491: When reading text size is extra large, resume lesson page and revision tabs content is seen in normal size (#5290) ## Explanation Fixes #4362 Fixes #1491 Support resizable reading text in resume lesson, question player and revision card. | Before| After| |--------|--------| |[before.webm](https://github.com/oppia/oppia-android/assets/76042077/aa58a643-727f-421d-8b3a-a4799718113e)|[device-2024-01-02-150914.webm](https://github.com/oppia/oppia-android/assets/76042077/aa46b657-87cf-4c4f-b512-0c1b67dd351d)| |[tablet_before.webm](https://github.com/oppia/oppia-android/assets/76042077/b9fd6b96-2f28-4a4f-8db5-1106219054f1)|[tablet_after.webm](https://github.com/oppia/oppia-android/assets/76042077/c9a105db-9062-47c4-982b-dd7f35a1f14c)| Question Player [questionPLayer.webm](https://github.com/oppia/oppia-android/assets/76042077/6b321c57-35ed-44e6-8220-3b9d52c089cc) ## Essential Checklist - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). ## For UI-specific PRs only If your PR includes UI-related changes, then: - Add screenshots for portrait/landscape for both a tablet & phone of the before & after UI changes - For the screenshots above, include both English and pseudo-localized (RTL) screenshots (see [RTL guide](https://github.com/oppia/oppia-android/wiki/RTL-Guidelines)) - Add a video showing the full UX flow with a screen reader enabled (see [accessibility guide](https://github.com/oppia/oppia-android/wiki/Accessibility-A11y-Guide)) - For PRs introducing new UI elements or color changes, both light and dark mode screenshots must be included - Add a screenshot demonstrating that you ran affected Espresso tests locally & that they're passing --- .../app/resumelesson/ResumeLessonActivity.kt | 13 ++ .../ResumeLessonActivityPresenter.kt | 117 +++++++++++++++--- .../app/resumelesson/ResumeLessonFragment.kt | 17 ++- .../ResumeLessonFragmentPresenter.kt | 19 ++- .../questionplayer/QuestionPlayerActivity.kt | 8 ++ .../QuestionPlayerActivityPresenter.kt | 86 +++++++++++-- .../questionplayer/QuestionPlayerFragment.kt | 26 ++-- .../QuestionPlayerFragmentPresenter.kt | 17 ++- .../revisioncard/RevisionCardActivity.kt | 10 ++ .../RevisionCardActivityPresenter.kt | 78 ++++++++++-- .../revisioncard/RevisionCardFragment.kt | 12 +- .../RevisionCardFragmentPresenter.kt | 40 +++++- .../resumelesson/ResumeLessonFragmentTest.kt | 60 +++++++++ .../QuestionPlayerActivityTest.kt | 71 +++++++++++ .../revisioncard/RevisionCardFragmentTest.kt | 92 ++++++++++++++ model/src/main/proto/arguments.proto | 15 +++ .../assets/kdoc_validity_exemptions.textproto | 1 - 17 files changed, 631 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivity.kt b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivity.kt index 77bb464cdfc..fc63898ac40 100644 --- a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivity.kt +++ b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivity.kt @@ -9,8 +9,10 @@ import org.oppia.android.app.home.RouteToExplorationListener import org.oppia.android.app.model.ExplorationActivityParams import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.ResumeLessonActivityParams import org.oppia.android.app.model.ScreenName.RESUME_LESSON_ACTIVITY +import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.player.exploration.ExplorationActivity import org.oppia.android.util.extensions.getProtoExtra import org.oppia.android.util.extensions.putProtoExtra @@ -20,6 +22,7 @@ import javax.inject.Inject /** Activity that allows the user to resume a saved exploration. */ class ResumeLessonActivity : InjectableAutoLocalizedAppCompatActivity(), + DefaultFontSizeStateListener, RouteToExplorationListener { @Inject lateinit var resumeLessonActivityPresenter: ResumeLessonActivityPresenter @@ -91,6 +94,7 @@ class ResumeLessonActivity : parentScreen: ExplorationActivityParams.ParentScreen, isCheckpointingEnabled: Boolean ) { + resumeLessonActivityPresenter.setReadingTextSizeNormal() startActivity( ExplorationActivity.createExplorationActivityIntent( this, @@ -105,4 +109,13 @@ class ResumeLessonActivity : ) finish() } + + override fun onDefaultFontSizeLoaded(readingTextSize: ReadingTextSize) { + resumeLessonActivityPresenter.loadResumeLessonFragment(readingTextSize) + } + + override fun onBackPressed() { + resumeLessonActivityPresenter.setReadingTextSizeNormal() + finish() + } } diff --git a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt index 63b89fad7d0..210d49c887c 100644 --- a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt @@ -2,19 +2,40 @@ package org.oppia.android.app.resumelesson import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations import org.oppia.android.R import org.oppia.android.app.model.ExplorationActivityParams import org.oppia.android.app.model.ExplorationCheckpoint +import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize +import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener +import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.databinding.ResumeLessonActivityBinding +import org.oppia.android.domain.oppialogger.OppiaLogger +import org.oppia.android.domain.profile.ProfileManagementController +import org.oppia.android.util.data.AsyncResult +import org.oppia.android.util.data.DataProviders.Companion.toLiveData import javax.inject.Inject private const val RESUME_LESSON_TAG = "ResumeLesson" /** The presenter for [ResumeLessonActivity]. */ class ResumeLessonActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + private val profileManagementController: ProfileManagementController, + private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, + private val oppiaLogger: OppiaLogger ) { + private lateinit var profileId: ProfileId + private lateinit var classroomId: String + private lateinit var topicId: String + private lateinit var storyId: String + private lateinit var explorationId: String + private lateinit var parentScreen: ExplorationActivityParams.ParentScreen + private lateinit var explorationCheckpoint: ExplorationCheckpoint /** Handles onCreate() method of the [ResumeLessonActivity]. */ fun handleOnCreate( @@ -30,30 +51,84 @@ class ResumeLessonActivityPresenter @Inject constructor( activity, R.layout.resume_lesson_activity ) + this.profileId = profileId + this.classroomId = classroomId + this.topicId = topicId + this.storyId = storyId + this.explorationId = explorationId + this.explorationCheckpoint = explorationCheckpoint + this.parentScreen = parentScreen val resumeLessonToolbar = binding.resumeLessonActivityToolbar activity.setSupportActionBar(resumeLessonToolbar) + retrieveReadingTextSize().observe( + activity as ResumeLessonActivity, + { result -> + (activity as DefaultFontSizeStateListener).onDefaultFontSizeLoaded(result) + } + ) + resumeLessonToolbar.setNavigationOnClickListener { + fontScaleConfigurationUtil.adjustFontScale( + context = activity, + ReadingTextSize.MEDIUM_TEXT_SIZE + ) @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. activity.onBackPressed() } + } - if (getResumeLessonFragment() == null) { - val resumeLessonFragment = ResumeLessonFragment.newInstance( - profileId, - classroomId, - topicId, - storyId, - explorationId, - parentScreen, - explorationCheckpoint - ) - activity.supportFragmentManager.beginTransaction().add( - R.id.resume_lesson_fragment_placeholder, - resumeLessonFragment, - RESUME_LESSON_TAG - ).commitNow() - } + /** Loads [ResumeLessonFragment]. */ + fun loadResumeLessonFragment(readingTextSize: ReadingTextSize) { + if (getResumeLessonFragment() != null) + activity.supportFragmentManager.beginTransaction() + .remove(getResumeLessonFragment() as Fragment).commitNow() + + val resumeLessonFragment = ResumeLessonFragment.newInstance( + profileId, + classroomId, + topicId, + storyId, + explorationId, + parentScreen, + explorationCheckpoint, + readingTextSize + ) + activity.supportFragmentManager.beginTransaction().add( + R.id.resume_lesson_fragment_placeholder, + resumeLessonFragment, + RESUME_LESSON_TAG + ).commitNow() + } + + private fun retrieveReadingTextSize(): LiveData { + return Transformations.map( + profileManagementController.getProfile(profileId).toLiveData(), + ::processReadingTextSizeResult + ) + } + + private fun processReadingTextSizeResult( + profileResult: AsyncResult + ): ReadingTextSize { + return when (profileResult) { + is AsyncResult.Failure -> { + oppiaLogger.e( + "ResumeLessonActivity", + "Failed to retrieve profile", + profileResult.error + ) + Profile.getDefaultInstance() + } + is AsyncResult.Pending -> { + oppiaLogger.d( + "ResumeLessonActivity", + "Result is pending" + ) + Profile.getDefaultInstance() + } + is AsyncResult.Success -> profileResult.value + }.readingTextSize } private fun getResumeLessonFragment(): ResumeLessonFragment? { @@ -63,4 +138,12 @@ class ResumeLessonActivityPresenter @Inject constructor( R.id.resume_lesson_fragment_placeholder ) as ResumeLessonFragment? } + + /** Set reading text size normal. */ + fun setReadingTextSizeNormal() { + fontScaleConfigurationUtil.adjustFontScale( + context = activity, + ReadingTextSize.MEDIUM_TEXT_SIZE + ) + } } diff --git a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragment.kt b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragment.kt index d0d48da0b7a..eb99c0cacf9 100644 --- a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragment.kt +++ b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragment.kt @@ -10,6 +10,7 @@ import org.oppia.android.app.fragment.InjectableFragment import org.oppia.android.app.model.ExplorationActivityParams import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.ResumeLessonFragmentArguments import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto @@ -18,7 +19,9 @@ import javax.inject.Inject /** Fragment that allows the user to resume a saved exploration. */ class ResumeLessonFragment : InjectableFragment() { companion object { - private const val ARGUMENTS_KEY = "ResumeExplorationFragment.arguments" + + /** Arguments key for [ResumeLessonFragment]. */ + const val RESUME_LESSON_FRAGMENT_ARGUMENTS_KEY = "ResumeLessonFragment.arguments" /** Creates new instance of [ResumeLessonFragment] for the provided parameters. */ fun newInstance( @@ -28,7 +31,8 @@ class ResumeLessonFragment : InjectableFragment() { storyId: String, explorationId: String, parentScreen: ExplorationActivityParams.ParentScreen, - checkpoint: ExplorationCheckpoint + checkpoint: ExplorationCheckpoint, + readingTextSize: ReadingTextSize ): ResumeLessonFragment { val args = ResumeLessonFragmentArguments.newBuilder().apply { this.profileId = profileId @@ -38,10 +42,11 @@ class ResumeLessonFragment : InjectableFragment() { this.explorationId = explorationId this.parentScreen = parentScreen this.checkpoint = checkpoint + this.readingTextSize = readingTextSize }.build() return ResumeLessonFragment().apply { arguments = Bundle().apply { - putProto(ARGUMENTS_KEY, args) + putProto(RESUME_LESSON_FRAGMENT_ARGUMENTS_KEY, args) } } } @@ -53,6 +58,7 @@ class ResumeLessonFragment : InjectableFragment() { override fun onAttach(context: Context) { super.onAttach(context) (fragmentComponent as FragmentComponentImpl).inject(this) + resumeLessonFragmentPresenter.handleAttach(context) } override fun onCreateView( @@ -62,7 +68,10 @@ class ResumeLessonFragment : InjectableFragment() { ): View? { val args = checkNotNull(arguments) { "Expected arguments to be provided for fragment." - }.getProto(ARGUMENTS_KEY, ResumeLessonFragmentArguments.getDefaultInstance()) + }.getProto( + RESUME_LESSON_FRAGMENT_ARGUMENTS_KEY, + ResumeLessonFragmentArguments.getDefaultInstance() + ) return resumeLessonFragmentPresenter.handleOnCreate( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentPresenter.kt index 55514a7957a..541cfa104ad 100644 --- a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentPresenter.kt @@ -1,5 +1,6 @@ package org.oppia.android.app.resumelesson +import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -12,7 +13,9 @@ import org.oppia.android.app.model.EphemeralChapterSummary import org.oppia.android.app.model.ExplorationActivityParams import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ResumeLessonFragmentArguments import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.databinding.ResumeLessonFragmentBinding import org.oppia.android.domain.exploration.ExplorationDataController import org.oppia.android.domain.oppialogger.OppiaLogger @@ -20,6 +23,7 @@ import org.oppia.android.domain.topic.TopicController import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.HtmlParser import javax.inject.Inject @@ -31,6 +35,7 @@ class ResumeLessonFragmentPresenter @Inject constructor( private val resumeLessonViewModel: ResumeLessonViewModel, private val topicController: TopicController, private val explorationDataController: ExplorationDataController, + private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, private val htmlParserFactory: HtmlParser.Factory, private val translationController: TranslationController, @DefaultResourceBucketName private val resourceBucketName: String, @@ -55,6 +60,11 @@ class ResumeLessonFragmentPresenter @Inject constructor( getChapterSummary() } + /** Handles the [Fragment.onAttach] portion of [ResumeLessonFragment]'s lifecycle. */ + fun handleAttach(context: Context) { + fontScaleConfigurationUtil.adjustFontScale(context, retrieveArguments().readingTextSize) + } + /** Handles onCreateView() method of the [ResumeLessonFragment]. */ fun handleOnCreate( inflater: LayoutInflater, @@ -72,7 +82,6 @@ class ResumeLessonFragmentPresenter @Inject constructor( container, /* attachToRoot= */ false ) - this.profileId = profileId this.topicId = topicId this.classroomId = classroomId @@ -110,10 +119,16 @@ class ResumeLessonFragmentPresenter @Inject constructor( parentScreen ) } - return binding.root } + private fun retrieveArguments(): ResumeLessonFragmentArguments { + return fragment.requireArguments().getProto( + ResumeLessonFragment.RESUME_LESSON_FRAGMENT_ARGUMENTS_KEY, + ResumeLessonFragmentArguments.getDefaultInstance() + ) + } + private fun subscribeToChapterSummary() { chapterSummaryLiveData.observe( fragment, diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt index 1f4a1c57e74..70239733952 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt @@ -11,9 +11,11 @@ import org.oppia.android.app.hintsandsolution.RevealSolutionInterface import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.QuestionPlayerActivityParams +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.ScreenName.QUESTION_PLAYER_ACTIVITY import org.oppia.android.app.model.State import org.oppia.android.app.model.WrittenTranslationContext +import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListener import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener import org.oppia.android.app.player.stopplaying.RestartPlayingSessionListener @@ -41,6 +43,7 @@ class QuestionPlayerActivity : RevealHintListener, RevealSolutionInterface, HintsAndSolutionQuestionManagerListener, + DefaultFontSizeStateListener, ConceptCardListener { @Inject @@ -57,6 +60,7 @@ class QuestionPlayerActivity : override fun onBackPressed() { showStopExplorationDialogFragment() + questionPlayerActivityPresenter.setReadingTextSizeNormal() } override fun restartSession() = questionPlayerActivityPresenter.restartSession() @@ -130,4 +134,8 @@ class QuestionPlayerActivity : override fun stopSession() { questionPlayerActivityPresenter.stopTrainingSession() } + + override fun onDefaultFontSizeLoaded(readingTextSize: ReadingTextSize) { + questionPlayerActivityPresenter.loadFragments(readingTextSize) + } } diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt index c705384c950..5c1f7484766 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt @@ -3,18 +3,26 @@ package org.oppia.android.app.topic.questionplayer import android.view.inputmethod.EditorInfo import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations import org.oppia.android.R import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.hintsandsolution.HintsAndSolutionDialogFragment import org.oppia.android.app.model.HelpIndex +import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.QuestionPlayerActivityParams +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.State import org.oppia.android.app.model.WrittenTranslationContext +import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_DIALOG import org.oppia.android.app.topic.questionplayer.QuestionPlayerActivity.Companion.QUESTION_PLAYER_ACTIVITY_PARAMS_KEY +import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.databinding.QuestionPlayerActivityBinding import org.oppia.android.domain.oppialogger.OppiaLogger +import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.question.QuestionTrainingController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData @@ -29,11 +37,14 @@ private const val TAG_HINTS_AND_SOLUTION_QUESTION_MANAGER = "HINTS_AND_SOLUTION_ class QuestionPlayerActivityPresenter @Inject constructor( private val activity: AppCompatActivity, private val questionTrainingController: QuestionTrainingController, - private val oppiaLogger: OppiaLogger + private val oppiaLogger: OppiaLogger, + private val profileManagementController: ProfileManagementController, + private val fontScaleConfigurationUtil: FontScaleConfigurationUtil ) { private lateinit var profileId: ProfileId private lateinit var state: State private lateinit var writtenTranslationContext: WrittenTranslationContext + private lateinit var readingTextSize: ReadingTextSize fun handleOnCreate(profileId: ProfileId) { this.profileId = profileId @@ -54,14 +65,31 @@ class QuestionPlayerActivityPresenter @Inject constructor( activity.onBackPressed() } + retrieveReadingTextSize().observe( + activity as QuestionPlayerActivity + ) { result -> + (activity as DefaultFontSizeStateListener).onDefaultFontSizeLoaded(result) + } + } + + private fun loadQuestionPlayerFragment(readingTextSize: ReadingTextSize) { + startTrainingSessionWithCallback { + activity.supportFragmentManager.beginTransaction().add( + R.id.question_player_fragment_placeholder, + QuestionPlayerFragment.newInstance(profileId, readingTextSize), + TAG_QUESTION_PLAYER_FRAGMENT + ).commitNow() + } + } + + fun loadFragments(readingTextSize: ReadingTextSize) { + this.readingTextSize = readingTextSize if (getQuestionPlayerFragment() == null) { - startTrainingSessionWithCallback { - activity.supportFragmentManager.beginTransaction().add( - R.id.question_player_fragment_placeholder, - QuestionPlayerFragment.newInstance(profileId), - TAG_QUESTION_PLAYER_FRAGMENT - ).commitNow() - } + loadQuestionPlayerFragment(readingTextSize) + } else { + activity.supportFragmentManager.beginTransaction() + .remove(getQuestionPlayerFragment() as Fragment).commitNow() + loadQuestionPlayerFragment(readingTextSize) } if (getHintsAndSolutionExplorationManagerFragment() == null) { @@ -72,6 +100,36 @@ class QuestionPlayerActivityPresenter @Inject constructor( } } + private fun retrieveReadingTextSize(): LiveData { + return Transformations.map( + profileManagementController.getProfile(profileId).toLiveData(), + ::processReadingTextSizeResult + ) + } + + private fun processReadingTextSizeResult( + profileResult: AsyncResult + ): ReadingTextSize { + return when (profileResult) { + is AsyncResult.Failure -> { + oppiaLogger.e( + "QuestionPlayerActivity", + "Failed to retrieve profile", + profileResult.error + ) + Profile.getDefaultInstance() + } + is AsyncResult.Pending -> { + oppiaLogger.d( + "QuestionPlayerActivity", + "Result is pending" + ) + Profile.getDefaultInstance() + } + is AsyncResult.Success -> profileResult.value + }.readingTextSize + } + private fun getHintsAndSolutionExplorationManagerFragment(): HintsAndSolutionQuestionManagerFragment? { // ktlint-disable max-line-length return activity.supportFragmentManager.findFragmentByTag( TAG_HINTS_AND_SOLUTION_QUESTION_MANAGER @@ -93,7 +151,7 @@ class QuestionPlayerActivityPresenter @Inject constructor( // Re-add the player fragment when the new session is ready. activity.supportFragmentManager.beginTransaction().add( R.id.question_player_fragment_placeholder, - QuestionPlayerFragment.newInstance(profileId), + QuestionPlayerFragment.newInstance(profileId, readingTextSize), TAG_QUESTION_PLAYER_FRAGMENT ).commitNow() } @@ -143,10 +201,12 @@ class QuestionPlayerActivityPresenter @Inject constructor( oppiaLogger.d("QuestionPlayerActivity", "Stopping training session") is AsyncResult.Failure -> { oppiaLogger.e("QuestionPlayerActivity", "Failed to stop training session", it.error) + setReadingTextSizeNormal() activity.finish() // Can't recover from the session failing to stop. } is AsyncResult.Success -> { oppiaLogger.d("QuestionPlayerActivity", "Successfully stopped training session") + setReadingTextSizeNormal() callback() } } @@ -221,4 +281,12 @@ class QuestionPlayerActivityPresenter @Inject constructor( TAG_HINTS_AND_SOLUTION_DIALOG ) as? HintsAndSolutionDialogFragment } + + /** Set reading text size to normal, which is the default. */ + fun setReadingTextSizeNormal() { + fontScaleConfigurationUtil.adjustFontScale( + context = activity, + ReadingTextSize.MEDIUM_TEXT_SIZE + ) + } } 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..23cb0b8afe0 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 @@ -9,6 +9,8 @@ import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.QuestionPlayerFragmentArguments +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -42,6 +44,7 @@ class QuestionPlayerFragment : override fun onAttach(context: Context) { super.onAttach(context) (fragmentComponent as FragmentComponentImpl).inject(this) + questionPlayerFragmentPresenter.handleAttach(context) } override fun onCreateView( @@ -52,7 +55,9 @@ class QuestionPlayerFragment : val args = checkNotNull(arguments) { "Expected arguments to be passed to QuestionPlayerFragment" } - val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) + val arguments = + args.getProto(ARGUMENTS_KEY, QuestionPlayerFragmentArguments.getDefaultInstance()) + val profileId = arguments.profileId return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId) } @@ -96,7 +101,9 @@ class QuestionPlayerFragment : fun dismissConceptCard() = questionPlayerFragmentPresenter.dismissConceptCard() companion object { - private const val PROFILE_ID_ARGUMENT_KEY = "QuestionPlayerFragment.profile_id" + + /** Arguments key for [QuestionPlayerFragment]. */ + const val ARGUMENTS_KEY = "QuestionPlayerFragment.arguments" /** * Creates a new fragment to play a question session. @@ -104,12 +111,17 @@ class QuestionPlayerFragment : * @param profileId the profile in which the question play session will be played * @return a new [QuestionPlayerFragment] to start a question play session */ - fun newInstance(profileId: ProfileId): QuestionPlayerFragment { - return QuestionPlayerFragment().apply { - arguments = Bundle().apply { - putProto(PROFILE_ID_ARGUMENT_KEY, profileId) + fun newInstance(profileId: ProfileId, readingTextSize: ReadingTextSize): + QuestionPlayerFragment { + val args = QuestionPlayerFragmentArguments.newBuilder().apply { + this.profileId = profileId + this.readingTextSize = readingTextSize + }.build() + return QuestionPlayerFragment().apply { + arguments = Bundle().apply { + putProto(ARGUMENTS_KEY, args) + } } } - } } } 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 e78221853c5..faf85e8a885 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 @@ -18,6 +18,7 @@ import org.oppia.android.app.model.EphemeralQuestion import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.QuestionPlayerFragmentArguments import org.oppia.android.app.model.State import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.ConfettiConfig.MINI_CONFETTI_BURST @@ -26,6 +27,7 @@ import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListen import org.oppia.android.app.player.stopplaying.RestartPlayingSessionListener import org.oppia.android.app.player.stopplaying.StopStatePlayingSessionListener import org.oppia.android.app.topic.conceptcard.ConceptCardFragment +import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.app.utility.SplitScreenManager import org.oppia.android.databinding.QuestionPlayerFragmentBinding import org.oppia.android.domain.oppialogger.OppiaLogger @@ -34,6 +36,7 @@ import org.oppia.android.domain.question.QuestionAssessmentProgressController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.QuestionResourceBucketName import javax.inject.Inject @@ -48,7 +51,8 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private val analyticsController: AnalyticsController, @QuestionResourceBucketName private val resourceBucketName: String, private val assemblerBuilderFactory: StatePlayerRecyclerViewAssembler.Builder.Factory, - private val splitScreenManager: SplitScreenManager + private val splitScreenManager: SplitScreenManager, + private val fontScaleConfigurationUtil: FontScaleConfigurationUtil ) { // TODO(#503): Add tests for the question player. @@ -66,6 +70,11 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private lateinit var helpIndex: HelpIndex private lateinit var profileId: ProfileId + /** Handles the [Fragment.onAttach] portion of [QuestionPlayerFragment]'s lifecycle. */ + fun handleAttach(context: Context) { + fontScaleConfigurationUtil.adjustFontScale(context, retrieveArguments().readingTextSize) + } + fun handleCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -117,6 +126,12 @@ class QuestionPlayerFragmentPresenter @Inject constructor( ConceptCardFragment.dismissAll(fragment.childFragmentManager) } + private fun retrieveArguments(): QuestionPlayerFragmentArguments { + return fragment.requireArguments().getProto( + QuestionPlayerFragment.ARGUMENTS_KEY, QuestionPlayerFragmentArguments.getDefaultInstance() + ) + } + fun handleAnswerReadyForSubmission(answer: UserAnswer) { // An interaction has indicated that an answer is ready for submission. handleSubmitAnswer(answer) diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivity.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivity.kt index 4f6e87509e7..7606c7ea5ad 100644 --- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivity.kt +++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivity.kt @@ -6,9 +6,11 @@ import android.os.Bundle import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.RevisionCardActivityParams import org.oppia.android.app.model.ScreenName.REVISION_CARD_ACTIVITY import org.oppia.android.app.player.exploration.BottomSheetOptionsMenuItemClickListener +import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.topic.RouteToRevisionCardListener import org.oppia.android.app.topic.conceptcard.ConceptCardListener import org.oppia.android.util.extensions.getProtoExtra @@ -24,6 +26,7 @@ class RevisionCardActivity : ReturnToTopicClickListener, ConceptCardListener, RouteToRevisionCardListener, + DefaultFontSizeStateListener, BottomSheetOptionsMenuItemClickListener { @Inject @@ -112,7 +115,14 @@ class RevisionCardActivity : revisionCardActivityPresenter.dismissConceptCard() } + // TODO(#5404): Migrate to a back pressed dispatcher. + @Deprecated("Deprecated in Java") override fun onBackPressed() { + revisionCardActivityPresenter.setReadingTextSizeMedium() onReturnToTopicRequested() } + + override fun onDefaultFontSizeLoaded(readingTextSize: ReadingTextSize) { + revisionCardActivityPresenter.loadRevisionCardFragment(readingTextSize) + } } diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt index 1d1036c3ef0..af41963e498 100644 --- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt @@ -4,18 +4,24 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations import org.oppia.android.R import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.help.HelpActivity import org.oppia.android.app.model.EphemeralRevisionCard +import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.options.OptionsActivity import org.oppia.android.app.player.exploration.BottomSheetOptionsMenu +import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener +import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.databinding.RevisionCardActivityBinding import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController +import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.topic.TopicController import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.accessibility.AccessibilityService @@ -30,7 +36,9 @@ class RevisionCardActivityPresenter @Inject constructor( private val oppiaLogger: OppiaLogger, private val analyticsController: AnalyticsController, private val topicController: TopicController, - private val translationController: TranslationController + private val translationController: TranslationController, + private val profileManagementController: ProfileManagementController, + private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, ) { @Inject lateinit var accessibilityService: AccessibilityService @@ -40,6 +48,7 @@ class RevisionCardActivityPresenter @Inject constructor( private lateinit var profileId: ProfileId private lateinit var topicId: String private var subtopicId: Int = 0 + private var subtopicListSize: Int = 0 fun handleOnCreate( internalProfileId: Int, @@ -54,11 +63,18 @@ class RevisionCardActivityPresenter @Inject constructor( profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() this.topicId = topicId this.subtopicId = subtopicId + this.subtopicListSize = subtopicListSize binding.apply { lifecycleOwner = activity } + retrieveReadingTextSize().observe( + activity + ) { result -> + (activity as DefaultFontSizeStateListener).onDefaultFontSizeLoaded(result) + } + revisionCardToolbar = binding.revisionCardToolbar revisionCardToolbarTitle = binding.revisionCardToolbarTitle activity.setSupportActionBar(revisionCardToolbar) @@ -66,6 +82,9 @@ class RevisionCardActivityPresenter @Inject constructor( binding.revisionCardToolbar.setNavigationOnClickListener { (activity as ReturnToTopicClickListener).onReturnToTopicRequested() + fontScaleConfigurationUtil.adjustFontScale(activity, ReadingTextSize.MEDIUM_TEXT_SIZE) + @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. + activity.onBackPressed() } if (!accessibilityService.isScreenReaderEnabled()) { binding.revisionCardToolbarTitle.setOnClickListener { @@ -79,17 +98,41 @@ class RevisionCardActivityPresenter @Inject constructor( val bottomSheetOptionsMenu = BottomSheetOptionsMenu() bottomSheetOptionsMenu.showNow(activity.supportFragmentManager, bottomSheetOptionsMenu.tag) } + } - if (getReviewCardFragment() == null) { - activity.supportFragmentManager.beginTransaction().add( - R.id.revision_card_fragment_placeholder, - RevisionCardFragment.newInstance(topicId, subtopicId, profileId, subtopicListSize) - ).commitNow() - } + private fun retrieveReadingTextSize(): LiveData { + return Transformations.map( + profileManagementController.getProfile(profileId).toLiveData(), + ::processReadingTextSizeResult + ) + } + + private fun processReadingTextSizeResult( + profileResult: AsyncResult + ): ReadingTextSize { + return when (profileResult) { + is AsyncResult.Failure -> { + oppiaLogger.e( + "RevisionCardActivity", + "Failed to retrieve profile", + profileResult.error + ) + Profile.getDefaultInstance() + } + is AsyncResult.Pending -> { + oppiaLogger.d( + "RevisionCardActivity", + "Result is pending" + ) + Profile.getDefaultInstance() + } + is AsyncResult.Success -> profileResult.value + }.readingTextSize } /** Action for onOptionsItemSelected. */ fun handleOnOptionsItemSelected(itemId: Int): Boolean { + setReadingTextSizeMedium() return when (itemId) { R.id.action_options -> { val intent = OptionsActivity.createOptionsActivity( @@ -172,4 +215,25 @@ class RevisionCardActivityPresenter @Inject constructor( R.id.revision_card_fragment_placeholder ) as RevisionCardFragment? } + + fun loadRevisionCardFragment(readingTextSize: ReadingTextSize) { + if (getReviewCardFragment() != null) + activity.supportFragmentManager.beginTransaction() + .remove(getReviewCardFragment() as Fragment).commitNow() + + activity.supportFragmentManager.beginTransaction().add( + R.id.revision_card_fragment_placeholder, + RevisionCardFragment.newInstance( + topicId, + subtopicId, + profileId, + subtopicListSize, + readingTextSize + ) + ).commitNow() + } + + fun setReadingTextSizeMedium() { + fontScaleConfigurationUtil.adjustFontScale(activity, ReadingTextSize.MEDIUM_TEXT_SIZE) + } } diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt index 303c9c04478..6066df39ab0 100755 --- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt +++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableDialogFragment import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.RevisionCardFragmentArguments import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto @@ -25,12 +26,19 @@ class RevisionCardFragment : InjectableDialogFragment() { * Returns a new [RevisionCardFragment] to display the specific subtopic for the given topic & * profile. */ - fun newInstance(topicId: String, subtopicId: Int, profileId: ProfileId, subtopicListSize: Int): + fun newInstance( + topicId: String, + subtopicId: Int, + profileId: ProfileId, + subtopicListSize: Int, + readingTextSize: ReadingTextSize + ): RevisionCardFragment { val args = RevisionCardFragmentArguments.newBuilder().apply { this.topicId = topicId this.subtopicId = subtopicId this.subtopicListSize = subtopicListSize + this.readingTextSize = readingTextSize }.build() return RevisionCardFragment().apply { arguments = Bundle().apply { @@ -47,6 +55,7 @@ class RevisionCardFragment : InjectableDialogFragment() { override fun onAttach(context: Context) { super.onAttach(context) (fragmentComponent as FragmentComponentImpl).inject(this) + revisionCardFragmentPresenter.handleAttach(context) } override fun onCreateView( @@ -70,7 +79,6 @@ class RevisionCardFragment : InjectableDialogFragment() { val subtopicId = args?.subtopicId ?: -1 val profileId = arguments.extractCurrentUserProfileId() val subtopicListSize = args?.subtopicListSize ?: -1 - return revisionCardFragmentPresenter.handleCreateView( inflater, container, topicId, subtopicId, profileId, subtopicListSize ) diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt index 5ac95363353..3475d1bdeac 100755 --- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt @@ -1,17 +1,26 @@ package org.oppia.android.app.topic.revisioncard +import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize +import org.oppia.android.app.model.RevisionCardFragmentArguments import org.oppia.android.app.topic.conceptcard.ConceptCardFragment +import org.oppia.android.app.topic.revisioncard.RevisionCardFragment.Companion.REVISION_CARD_FRAGMENT_ARGUMENTS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.databinding.RevisionCardFragmentBinding import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController +import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.translation.TranslationController +import org.oppia.android.util.data.AsyncResult +import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.HtmlParser import org.oppia.android.util.parser.html.TopicHtmlParserEntityType @@ -28,10 +37,18 @@ class RevisionCardFragmentPresenter @Inject constructor( @TopicHtmlParserEntityType private val entityType: String, private val translationController: TranslationController, private val appLanguageResourceHandler: AppLanguageResourceHandler, - private val revisionCardViewModelFactory: RevisionCardViewModel.Factory + private val revisionCardViewModelFactory: RevisionCardViewModel.Factory, + private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, + private val profileManagementController: ProfileManagementController ) : HtmlParser.CustomOppiaTagActionListener { private lateinit var profileId: ProfileId + /** Handles the [Fragment.onAttach] portion of [RevisionCardFragment]'s lifecycle. */ + fun handleAttach(context: Context) { + fontScaleConfigurationUtil.adjustFontScale(context, retrieveReadingTextSize()) + } + + /** Handles the [Fragment.onCreateView] portion of [RevisionCardFragment]'s lifecycle. */ fun handleCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -80,6 +97,19 @@ class RevisionCardFragmentPresenter @Inject constructor( ) } + profileManagementController.getProfile(profileId) + .toLiveData().observe( + fragment + ) { result -> + val readingTextSize = retrieveReadingTextSize() + if (result is AsyncResult.Success) { + if (result.value.readingTextSize != readingTextSize) { + // Since text views are based on sp for sizing, the activity needs to be recreated so that + // sp can be correctly recomputed. + fragment.requireActivity().recreate() + } + } + } return binding.root } @@ -98,4 +128,12 @@ class RevisionCardFragmentPresenter @Inject constructor( override fun onConceptCardLinkClicked(view: View, skillId: String) { ConceptCardFragment.bringToFrontOrCreateIfNew(skillId, profileId, fragment.childFragmentManager) } + + private fun retrieveReadingTextSize(): ReadingTextSize { + return fragment.requireArguments() + .getProto( + REVISION_CARD_FRAGMENT_ARGUMENTS_KEY, + RevisionCardFragmentArguments.getDefaultInstance() + ).readingTextSize + } } diff --git a/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt index d30099e4ee0..1a28e628594 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt @@ -40,10 +40,12 @@ import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.ExplorationActivityParams import org.oppia.android.app.model.ExplorationCheckpoint import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule import org.oppia.android.app.utility.EspressoTestsMatchers.withDrawable +import org.oppia.android.app.utility.FontSizeMatcher.Companion.withFontSize import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape import org.oppia.android.data.backends.gae.NetworkConfigProdModule import org.oppia.android.data.backends.gae.NetworkModule @@ -84,8 +86,10 @@ import org.oppia.android.domain.topic.RATIOS_STORY_ID_0 import org.oppia.android.domain.topic.RATIOS_TOPIC_ID import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule import org.oppia.android.testing.OppiaTestRule +import org.oppia.android.testing.RunOn import org.oppia.android.testing.TestImageLoaderModule import org.oppia.android.testing.TestLogReportingModule +import org.oppia.android.testing.TestPlatform import org.oppia.android.testing.firebase.TestAuthenticationModule import org.oppia.android.testing.junit.InitializeDefaultLocaleRule import org.oppia.android.testing.robolectric.RobolectricModule @@ -295,6 +299,62 @@ class ResumeLessonFragmentTest { ) } + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testResumeLessonFragment_extraLargeTextSize_hasCorrectDimension() { + launch(createResumeLessonActivityIntent()).use { + it.onActivity { activity -> + activity.resumeLessonActivityPresenter + .loadResumeLessonFragment(ReadingTextSize.EXTRA_LARGE_TEXT_SIZE) + } + onView(withId(R.id.resume_lesson_chapter_description_text_view)).check( + matches(withFontSize(67F)) + ) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testResumeLessonFragment_largeTextSize_hasCorrectDimension() { + launch(createResumeLessonActivityIntent()).use { + it.onActivity { activity -> + activity.resumeLessonActivityPresenter + .loadResumeLessonFragment(ReadingTextSize.LARGE_TEXT_SIZE) + } + onView(withId(R.id.resume_lesson_chapter_description_text_view)).check( + matches(withFontSize(58F)) + ) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testResumeLessonFragment_mediumTextSize_hasCorrectDimension() { + launch(createResumeLessonActivityIntent()).use { + it.onActivity { activity -> + activity.resumeLessonActivityPresenter + .loadResumeLessonFragment(ReadingTextSize.MEDIUM_TEXT_SIZE) + } + onView(withId(R.id.resume_lesson_chapter_description_text_view)).check( + matches(withFontSize(48F)) + ) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testResumeLessonFragment_smallTextSize_hasCorrectDimension() { + launch(createResumeLessonActivityIntent()).use { + it.onActivity { activity -> + activity.resumeLessonActivityPresenter + .loadResumeLessonFragment(ReadingTextSize.SMALL_TEXT_SIZE) + } + onView(withId(R.id.resume_lesson_chapter_description_text_view)).check( + matches(withFontSize(38F)) + ) + } + } + // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. @Singleton @Component( diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt index c2aacc1d4b0..e612dacc954 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt @@ -63,6 +63,7 @@ import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.ScreenName import org.oppia.android.app.model.WrittenTranslationLanguageSelection import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule @@ -77,6 +78,7 @@ import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositi import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.hasItemCount import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule +import org.oppia.android.app.utility.FontSizeMatcher import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape import org.oppia.android.data.backends.gae.NetworkConfigProdModule import org.oppia.android.data.backends.gae.NetworkModule @@ -556,6 +558,75 @@ class QuestionPlayerActivityTest { } } + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testQuestionPlayer_extraLargeTextSize_hasCorrectDimension() { + launchForSkillList(SKILL_ID_LIST).use { + it.onActivity { activity -> + activity.questionPlayerActivityPresenter + .loadFragments(ReadingTextSize.EXTRA_LARGE_TEXT_SIZE) + } + testCoroutineDispatchers.runCurrent() + verifyFontSizeMatches(67F) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testQuestionPlayer_largeTextSize_hasCorrectDimension() { + launchForSkillList(SKILL_ID_LIST).use { + it.onActivity { activity -> + activity.questionPlayerActivityPresenter + .loadFragments(ReadingTextSize.LARGE_TEXT_SIZE) + } + testCoroutineDispatchers.runCurrent() + verifyFontSizeMatches(58F) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testQuestionPlayer_mediumTextSize_hasCorrectDimension() { + launchForSkillList(SKILL_ID_LIST).use { + it.onActivity { activity -> + activity.questionPlayerActivityPresenter + .loadFragments(ReadingTextSize.MEDIUM_TEXT_SIZE) + } + testCoroutineDispatchers.runCurrent() + verifyFontSizeMatches(48F) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testQuestionPlayer_smallTextSize_hasCorrectDimension() { + launchForSkillList(SKILL_ID_LIST).use { + it.onActivity { activity -> + activity.questionPlayerActivityPresenter + .loadFragments(ReadingTextSize.SMALL_TEXT_SIZE) + } + testCoroutineDispatchers.runCurrent() + verifyFontSizeMatches(38F) + } + } + + private fun verifyFontSizeMatches(fontSize: Float) { + scrollToViewType(CONTENT) + onView( + atPositionOnView( + recyclerViewId = R.id.question_recycler_view, + position = 0, + targetViewId = R.id.content_text_view + ) + ).check( + matches( + FontSizeMatcher.withFontSize( + fontSize = fontSize + ) + ) + ) + } + private fun setUpTestApplicationComponent() { ApplicationProvider.getApplicationContext().inject(this) } diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt index b57ef1fb0e3..29dde65b725 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt @@ -56,6 +56,7 @@ import org.oppia.android.app.model.HelpActivityParams import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.model.OptionsActivityParams import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.RevisionCardActivityParams import org.oppia.android.app.model.WrittenTranslationLanguageSelection import org.oppia.android.app.options.OptionsActivity @@ -65,6 +66,7 @@ import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.topic.revisioncard.RevisionCardActivity.Companion.createRevisionCardActivityIntent import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule import org.oppia.android.app.utility.EspressoTestsMatchers.hasProtoExtra +import org.oppia.android.app.utility.FontSizeMatcher.Companion.withFontSize import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape import org.oppia.android.data.backends.gae.NetworkConfigProdModule import org.oppia.android.data.backends.gae.NetworkModule @@ -229,6 +231,96 @@ class RevisionCardFragmentTest { } } + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testRevisionCard_extraLargeTextSize_hasCorrectDimension() { + launch( + createRevisionCardActivityIntent( + context, + profileId.internalId, + FRACTIONS_TOPIC_ID, + subtopicId = 2, + FRACTIONS_SUBTOPIC_LIST_SIZE + ) + ).use { + it.onActivity { activity -> + activity.revisionCardActivityPresenter + .loadRevisionCardFragment(ReadingTextSize.EXTRA_LARGE_TEXT_SIZE) + } + onView(withId(R.id.revision_card_explanation_text)).check( + matches(withFontSize(67F)) + ) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testRevisionCard_largeTextSize_hasCorrectDimension() { + launch( + createRevisionCardActivityIntent( + context, + profileId.internalId, + FRACTIONS_TOPIC_ID, + subtopicId = 2, + FRACTIONS_SUBTOPIC_LIST_SIZE + ) + ).use { + it.onActivity { activity -> + activity.revisionCardActivityPresenter + .loadRevisionCardFragment(ReadingTextSize.LARGE_TEXT_SIZE) + } + onView(withId(R.id.revision_card_explanation_text)).check( + matches(withFontSize(58F)) + ) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testRevisionCard_mediumTextSize_hasCorrectDimension() { + launch( + createRevisionCardActivityIntent( + context, + profileId.internalId, + FRACTIONS_TOPIC_ID, + subtopicId = 2, + FRACTIONS_SUBTOPIC_LIST_SIZE + ) + ).use { + it.onActivity { activity -> + activity.revisionCardActivityPresenter + .loadRevisionCardFragment( + ReadingTextSize.MEDIUM_TEXT_SIZE + ) + } + onView(withId(R.id.revision_card_explanation_text)).check( + matches(withFontSize(48F)) + ) + } + } + + @Test + @RunOn(TestPlatform.ROBOLECTRIC) + fun testRevisionCard_smallTextSize_hasCorrectDimension() { + launch( + createRevisionCardActivityIntent( + context, + profileId.internalId, + FRACTIONS_TOPIC_ID, + subtopicId = 2, + FRACTIONS_SUBTOPIC_LIST_SIZE + ) + ).use { + it.onActivity { activity -> + activity.revisionCardActivityPresenter + .loadRevisionCardFragment(ReadingTextSize.SMALL_TEXT_SIZE) + } + onView(withId(R.id.revision_card_explanation_text)).check( + matches(withFontSize(38F)) + ) + } + } + @Test fun testRevisionCardTest_initialise_openBottomSheet_showsBottomSheet() { launch( diff --git a/model/src/main/proto/arguments.proto b/model/src/main/proto/arguments.proto index 2dab9440c9a..5b9e2c24af6 100644 --- a/model/src/main/proto/arguments.proto +++ b/model/src/main/proto/arguments.proto @@ -218,6 +218,9 @@ message ResumeLessonFragmentArguments { // The checkpoint that may be used to resume the exploration play session. ExplorationCheckpoint checkpoint = 6; + + // The text size at which content in the resume lesson should be rendered. + ReadingTextSize reading_text_size = 8; } // Params required when creating a new ReadingTextSizeActivity. @@ -340,6 +343,15 @@ message SurveyActivityParams { string exploration_id = 3; } +// Arguments required when creating a QuestionPlayerFragment. +message QuestionPlayerFragmentArguments{ + // The ID of current profile. + ProfileId profile_id = 1; + + // The ReadingTextSize previously selected by the user (upon opening the fragment). + ReadingTextSize reading_text_size = 2; +} + // Represents the parameters needed to create TopicActivity. message TopicActivityParams { // The ID of the classroom to which the topic to be loaded belongs. @@ -831,6 +843,9 @@ message RevisionCardFragmentArguments { // The sub topic list size. int32 subtopic_list_size = 3; + + // The text size at which content in the revision card should be rendered. + ReadingTextSize reading_text_size = 7; } // Represents the parameters needed to create TestFontScaleConfigurationUtilActivity. diff --git a/scripts/assets/kdoc_validity_exemptions.textproto b/scripts/assets/kdoc_validity_exemptions.textproto index a41ece30e6e..c82f70cd255 100644 --- a/scripts/assets/kdoc_validity_exemptions.textproto +++ b/scripts/assets/kdoc_validity_exemptions.textproto @@ -250,7 +250,6 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/topic/revision/Topi exempted_file_path: "app/src/main/java/org/oppia/android/app/topic/revision/TopicRevisionViewModel.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt" -exempted_file_path: "app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/view/ViewComponentImpl.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/walkthrough/WalkthroughActivity.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/walkthrough/WalkthroughActivityPresenter.kt"