diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivity.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivity.kt index 2cda2286d02..64beda78f53 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivity.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivity.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.administratorcontrols import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.activity.OnBackPressedCallback import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity @@ -83,6 +84,15 @@ class AdministratorControlsActivity : isProfileDeletionDialogVisible ) title = resourceHandler.getStringInLocale(R.string.administrator_controls) + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + this@AdministratorControlsActivity.handleBackPress() + } + } + ) } override fun routeToAppVersion() { @@ -121,7 +131,7 @@ class AdministratorControlsActivity : } } - override fun onBackPressed() { + private fun handleBackPress() { val fragment = supportFragmentManager.findFragmentById( R.id.administrator_controls_fragment_multipane_placeholder @@ -134,8 +144,7 @@ class AdministratorControlsActivity : if (fragment is ProfileEditFragment) { administratorControlsActivityPresenter.handleOnBackPressed() } else { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - super.onBackPressed() + finish() } } diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt index e41600bb929..f78d770df1d 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem +import androidx.activity.OnBackPressedCallback import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.model.ScreenName.APP_VERSION_ACTIVITY @@ -19,12 +20,20 @@ class AppVersionActivity : InjectableAutoLocalizedAppCompatActivity() { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) appVersionActivityPresenter.handleOnCreate() + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + finish() + } + } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - onBackPressed() + onBackPressedDispatcher.onBackPressed() } return super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt index cf6fb025db6..45a27fac5b8 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem +import androidx.activity.OnBackPressedCallback import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.model.ScreenName.PROFILE_AND_DEVICE_ID_ACTIVITY @@ -18,18 +19,27 @@ import javax.inject.Inject * a particular user or group. */ class ProfileAndDeviceIdActivity : InjectableAutoLocalizedAppCompatActivity() { - @Inject lateinit var profileAndDeviceIdActivityPresenter: ProfileAndDeviceIdActivityPresenter + @Inject + lateinit var profileAndDeviceIdActivityPresenter: ProfileAndDeviceIdActivityPresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) profileAndDeviceIdActivityPresenter.handleOnCreate() + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + finish() + } + } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - onBackPressed() + onBackPressedDispatcher.onBackPressed() } return super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/oppia/android/app/databinding/TextViewBindingAdapters.java b/app/src/main/java/org/oppia/android/app/databinding/TextViewBindingAdapters.java index e4584d7cad1..284e1332b2d 100644 --- a/app/src/main/java/org/oppia/android/app/databinding/TextViewBindingAdapters.java +++ b/app/src/main/java/org/oppia/android/app/databinding/TextViewBindingAdapters.java @@ -68,15 +68,14 @@ public static void setDrawableEndCompat( } private static String getTimeAgo(View view, long lastVisitedTimestamp) { - long timeStampMillis = ensureTimestampIsInMilliseconds(lastVisitedTimestamp); long currentTimeMillis = getOppiaClock(view).getCurrentTimeMs(); AppLanguageResourceHandler resourceHandler = getResourceHandler(view); - if (timeStampMillis > currentTimeMillis || timeStampMillis <= 0) { + if (lastVisitedTimestamp > currentTimeMillis || lastVisitedTimestamp <= 0) { return resourceHandler.getStringInLocale(R.string.last_logged_in_recently); } - long timeDifferenceMillis = currentTimeMillis - timeStampMillis; + long timeDifferenceMillis = currentTimeMillis - lastVisitedTimestamp; if (timeDifferenceMillis < (int) TimeUnit.MINUTES.toMillis(1)) { return resourceHandler.getStringInLocale(R.string.just_now); @@ -112,15 +111,6 @@ private static String getPluralString( ); } - private static long ensureTimestampIsInMilliseconds(long lastVisitedTimestamp) { - // TODO(#3842): Investigate & remove this check. - if (lastVisitedTimestamp < 1000000000000L) { - // If timestamp is given in seconds, convert that to milliseconds. - return TimeUnit.SECONDS.toMillis(lastVisitedTimestamp); - } - return lastVisitedTimestamp; - } - private static AppLanguageResourceHandler getResourceHandler(View view) { AppLanguageActivityInjectorProvider provider = (AppLanguageActivityInjectorProvider) getAttachedActivity(view); diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivity.kt index ec8979dad41..cc1a7897150 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivity.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem +import androidx.activity.OnBackPressedCallback import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity @@ -37,12 +38,20 @@ class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity() val showConfirmationNotice = args?.showConfirmationNotice ?: false markChaptersCompletedActivityPresenter.handleOnCreate(internalProfileId, showConfirmationNotice) title = resourceHandler.getStringInLocale(R.string.mark_chapters_completed_activity_title) + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + finish() + } + } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - onBackPressed() + onBackPressedDispatcher.onBackPressed() } return super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivity.kt index e3373fc7df6..86bb96b2f8d 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivity.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem +import androidx.activity.OnBackPressedCallback import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity @@ -34,12 +35,20 @@ class MarkStoriesCompletedActivity : InjectableAutoLocalizedAppCompatActivity() internalProfileId = profileId?.internalId ?: -1 markStoriesCompletedActivityPresenter.handleOnCreate(internalProfileId) title = resourceHandler.getStringInLocale(R.string.mark_stories_completed_activity_title) + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + finish() + } + } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - onBackPressed() + onBackPressedDispatcher.onBackPressed() } return super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivity.kt index a891de3a059..606c4f70bd0 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivity.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem +import androidx.activity.OnBackPressedCallback import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity @@ -34,12 +35,20 @@ class MarkTopicsCompletedActivity : InjectableAutoLocalizedAppCompatActivity() { internalProfileId = profileId?.internalId ?: -1 markTopicsCompletedActivityPresenter.handleOnCreate(internalProfileId) title = resourceHandler.getStringInLocale(R.string.mark_topics_completed_activity_title) + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + finish() + } + } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - onBackPressed() + onBackPressedDispatcher.onBackPressed() } return super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivity.kt b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivity.kt index e88525841b5..054a4d8e54b 100644 --- a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivity.kt +++ b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivity.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.options import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.activity.OnBackPressedCallback import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.model.ReadingTextSize @@ -34,6 +35,22 @@ class ReadingTextSizeActivity : InjectableAutoLocalizedAppCompatActivity() { savedInstanceState?.retrieveStateBundle()?.selectedReadingTextSize ?: retrieveActivityParams().readingTextSize readingTextSizeActivityPresenter.handleOnCreate(readingTextSize) + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + val resultBundle = ReadingTextSizeActivityResultBundle.newBuilder().apply { + selectedReadingTextSize = readingTextSizeActivityPresenter.getSelectedReadingTextSize() + }.build() + val intent = Intent().apply { + putProtoExtra(MESSAGE_READING_TEXT_SIZE_RESULTS_KEY, resultBundle) + } + setResult(RESULT_OK, intent) + finish() + } + } + ) } companion object { @@ -60,17 +77,6 @@ class ReadingTextSizeActivity : InjectableAutoLocalizedAppCompatActivity() { outState.putProto(ACTIVITY_SAVED_STATE_KEY, stateBundle) } - override fun onBackPressed() { - val resultBundle = ReadingTextSizeActivityResultBundle.newBuilder().apply { - selectedReadingTextSize = readingTextSizeActivityPresenter.getSelectedReadingTextSize() - }.build() - val intent = Intent().apply { - putProtoExtra(MESSAGE_READING_TEXT_SIZE_RESULTS_KEY, resultBundle) - } - setResult(RESULT_OK, intent) - finish() - } - private fun retrieveActivityParams() = intent.getProtoExtra(ACTIVITY_PARAMS_KEY, ReadingTextSizeActivityParams.getDefaultInstance()) diff --git a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt index 85eedd9d379..491ad80fb11 100644 --- a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt @@ -28,8 +28,7 @@ class ReadingTextSizeActivityPresenter @Inject constructor( private fun setToolbar() { val readingTextSizeToolbar: Toolbar = activity.findViewById(R.id.reading_text_size_toolbar) readingTextSizeToolbar.setNavigationOnClickListener { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - activity.onBackPressed() + activity.onBackPressedDispatcher.onBackPressed() } } diff --git a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragment.kt b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragment.kt index 1cdae8c8578..02c9a456e74 100644 --- a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragment.kt +++ b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragment.kt @@ -63,7 +63,8 @@ class ReadingTextSizeFragment : InjectableFragment(), TextSizeRadioButtonListene readingTextSizeFragmentPresenter.onTextSizeSelected(selectedTextSize) } - private fun retrieveFragmentArguments(): ReadingTextSizeFragmentArguments { + /** Returns the [ReadingTextSizeFragmentArguments] stored in the fragment's arguments. */ + fun retrieveFragmentArguments(): ReadingTextSizeFragmentArguments { return checkNotNull(arguments) { "Expected arguments to be passed to ReadingTextSizeFragment" }.getProto(FRAGMENT_ARGUMENTS_KEY, ReadingTextSizeFragmentArguments.getDefaultInstance()) diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt index 6912d639ed9..d9519c01e2a 100755 --- a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt +++ b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.player.exploration import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.activity.OnBackPressedCallback import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.hintsandsolution.HintsAndSolutionDialogFragment @@ -48,7 +49,8 @@ class ExplorationActivity : BottomSheetOptionsMenuItemClickListener, RequestVoiceOverIconSpotlightListener { - @Inject lateinit var explorationActivityPresenter: ExplorationActivityPresenter + @Inject + lateinit var explorationActivityPresenter: ExplorationActivityPresenter private lateinit var state: State private lateinit var writtenTranslationContext: WrittenTranslationContext @@ -67,6 +69,14 @@ class ExplorationActivity : params.parentScreen, params.isCheckpointingEnabled ) + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + explorationActivityPresenter.backButtonPressed() + } + } + ) } // TODO(#1655): Re-restrict access to fields in tests post-Gradle. @@ -114,10 +124,6 @@ class ExplorationActivity : getProtoExtra(PARAMS_KEY, ExplorationActivityParams.getDefaultInstance()) } - override fun onBackPressed() { - explorationActivityPresenter.backButtonPressed() - } - override fun deleteCurrentProgressAndStopSession(isCompletion: Boolean) { explorationActivityPresenter.deleteCurrentProgressAndStopExploration(isCompletion) } diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt index 1c493c19bfa..4049f5dc4a2 100644 --- a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt @@ -113,8 +113,7 @@ class ExplorationActivityPresenter @Inject constructor( } binding.explorationToolbar.setNavigationOnClickListener { - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - activity.onBackPressed() + activity.onBackPressedDispatcher.onBackPressed() } binding.actionAudioPlayer.setOnClickListener { 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 fc63898ac40..4e268b486c2 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 @@ -3,6 +3,7 @@ package org.oppia.android.app.resumelesson import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.activity.OnBackPressedCallback import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.home.RouteToExplorationListener @@ -41,6 +42,15 @@ class ResumeLessonActivity : params.parentScreen, params.checkpoint ) + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(/* enabled = */ true) { + override fun handleOnBackPressed() { + resumeLessonActivityPresenter.setReadingTextSizeNormal() + finish() + } + } + ) } // TODO(#1655): Re-restrict access to fields in tests post-Gradle. @@ -113,9 +123,4 @@ class ResumeLessonActivity : 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 210d49c887c..b09e77fdfce 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 @@ -73,8 +73,7 @@ class ResumeLessonActivityPresenter @Inject constructor( context = activity, ReadingTextSize.MEDIUM_TEXT_SIZE ) - @Suppress("DEPRECATION") // TODO(#5404): Migrate to a back pressed dispatcher. - activity.onBackPressed() + activity.onBackPressedDispatcher.onBackPressed() } } diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt index 5e4b14be4aa..1883d2459bf 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt @@ -16,6 +16,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isChecked import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import dagger.Component import org.hamcrest.Description import org.hamcrest.TypeSafeMatcher @@ -37,6 +38,7 @@ import org.oppia.android.app.application.testing.TestingBuildFlavorModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.ReadingTextSize.MEDIUM_TEXT_SIZE import org.oppia.android.app.model.ReadingTextSize.SMALL_TEXT_SIZE import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView @@ -194,6 +196,47 @@ class ReadingTextSizeFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + launch(createReadingTextSizeActivityIntent()).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val readingTextSizeFragment = activity.supportFragmentManager + .findFragmentById(R.id.reading_text_size_container) as ReadingTextSizeFragment + val receivedReadingTextSize = readingTextSizeFragment.retrieveFragmentArguments() + .readingTextSize + + assertThat(receivedReadingTextSize).isEqualTo(SMALL_TEXT_SIZE) + } + } + } + + @Test + fun testFragment_saveInstanceState_verifyCorrectStateRestored() { + launch(createReadingTextSizeActivityIntent()).use { scenario -> + testCoroutineDispatchers.runCurrent() + + scenario.onActivity { activity -> + val readingTextSizeFragment = activity.supportFragmentManager + .findFragmentById(R.id.reading_text_size_container) as ReadingTextSizeFragment + readingTextSizeFragment.readingTextSizeFragmentPresenter + .onTextSizeSelected(MEDIUM_TEXT_SIZE) + } + + scenario.recreate() + + scenario.onActivity { activity -> + val newReadingTextSizeFragment = activity.supportFragmentManager + .findFragmentById(R.id.reading_text_size_container) as ReadingTextSizeFragment + val restoredTopicIdList = + newReadingTextSizeFragment.readingTextSizeFragmentPresenter.getTextSizeSelected() + + assertThat(restoredTopicIdList).isEqualTo(MEDIUM_TEXT_SIZE) + } + } + } + private fun createReadingTextSizeActivityIntent() = ReadingTextSizeActivity.createReadingTextSizeActivityIntent(context, SMALL_TEXT_SIZE) 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 1a28e628594..33da1eeed36 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 @@ -41,6 +41,7 @@ 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.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule @@ -99,6 +100,7 @@ import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.EventLoggingConfigurationModule @@ -273,6 +275,41 @@ class ResumeLessonFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + launch(createResumeLessonActivityIntent()).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val resumeLessonFragment = activity.supportFragmentManager + .findFragmentById(R.id.resume_lesson_fragment_placeholder) as ResumeLessonFragment + val args = checkNotNull(resumeLessonFragment.arguments) { + "Expected arguments to be provided for fragment." + }.getProto( + ResumeLessonFragment.RESUME_LESSON_FRAGMENT_ARGUMENTS_KEY, + ResumeLessonFragmentArguments.getDefaultInstance() + ) + val receivedProfileId = args.profileId + val receivedClassroomId = args.classroomId + val receivedTopicId = args.topicId + val receivedStoryId = args.storyId + val receivedExplorationId = args.explorationId + val receivedParentScreen = args.parentScreen + val receivedCheckpoint = args.checkpoint + + assertThat(receivedProfileId) + .isEqualTo(ProfileId.newBuilder().apply { internalId = 1 }.build()) + assertThat(receivedClassroomId).isEqualTo(TEST_CLASSROOM_ID_1) + assertThat(receivedTopicId).isEqualTo(FRACTIONS_TOPIC_ID) + assertThat(receivedStoryId).isEqualTo(FRACTIONS_STORY_ID_0) + assertThat(receivedExplorationId).isEqualTo(FRACTIONS_EXPLORATION_ID_0) + assertThat(receivedParentScreen) + .isEqualTo(ExplorationActivityParams.ParentScreen.PARENT_SCREEN_UNSPECIFIED) + assertThat(receivedCheckpoint).isEqualTo(ExplorationCheckpoint.getDefaultInstance()) + } + } + } + private fun createResumeLessonActivityIntent(): Intent { return ResumeLessonActivity.createResumeLessonActivityIntent( context, diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt index 5b7bd78bb3e..742c20bd0f5 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt @@ -18,6 +18,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import dagger.Component import org.hamcrest.Matchers.not import org.junit.After @@ -37,6 +38,7 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule +import org.oppia.android.app.model.ProfileListFragmentArguments import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPosition import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView @@ -86,6 +88,7 @@ import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.EventLoggingConfigurationModule @@ -367,6 +370,30 @@ class ProfileListFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + profileTestHelper.initializeProfiles() + launch(ProfileListActivity::class.java).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val profileListFragment = activity.supportFragmentManager + .findFragmentById(R.id.profile_list_container) as ProfileListFragment + + val arguments = checkNotNull(profileListFragment.arguments) { + "Expected variables to be passed to ProfileListFragment" + } + val args = arguments.getProto( + ProfileListFragment.PROFILE_LIST_FRAGMENT_ARGUMENTS_KEY, + ProfileListFragmentArguments.getDefaultInstance() + ) + val receivedIsMultipane = args.isMultipane + + assertThat(receivedIsMultipane).isEqualTo(false) + } + } + } + // 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/settings/profile/ProfileRenameFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt index fd4bf50712b..1fc5d9316d3 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt @@ -21,6 +21,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule +import com.google.common.truth.Truth.assertThat import dagger.Component import org.hamcrest.CoreMatchers.not import org.hamcrest.core.AllOf.allOf @@ -102,6 +103,7 @@ import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule import org.oppia.android.util.parser.image.GlideImageLoaderModule import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject @@ -441,6 +443,30 @@ class ProfileRenameFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + ActivityScenario.launch( + ProfileRenameActivity.createProfileRenameActivity( + context = context, + internalProfileId = 1 + ) + ).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val profileRenameFragment = activity.supportFragmentManager + .findFragmentById(R.id.profile_rename_fragment_placeholder) as ProfileRenameFragment + val args = + checkNotNull(profileRenameFragment.arguments) { + "Expected arguments to be passed to ProfileRenameFragment" + } + val receivedProfileId = args.extractCurrentUserProfileId().internalId + + assertThat(receivedProfileId).isEqualTo(1) + } + } + } + // 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/settings/profile/ProfileResetPinFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt index c306e9a6206..c0e1541d8a3 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt @@ -22,6 +22,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule +import com.google.common.truth.Truth.assertThat import dagger.Component import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.not @@ -42,6 +43,7 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule +import org.oppia.android.app.model.ProfileResetPinFragmentArguments import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule @@ -92,6 +94,7 @@ import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.EventLoggingConfigurationModule @@ -1006,6 +1009,70 @@ class ProfileResetPinFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + ActivityScenario.launch( + ProfileResetPinActivity.createProfileResetPinActivity( + context = context, + profileId = 0, + isAdmin = true + ) + ).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val profileResetPinFragment = activity.supportFragmentManager + .findFragmentById(R.id.profile_reset_pin_fragment_placeholder) as ProfileResetPinFragment + + val arguments = checkNotNull(profileResetPinFragment.arguments) { + "Expected arguments to be passed to ProfileResetPinFragment" + } + val args = + arguments.getProto( + ProfileResetPinFragment.PROFILE_RESET_PIN_FRAGMENT_ARGUMENTS_KEY, + ProfileResetPinFragmentArguments.getDefaultInstance() + ) + val receivedProfileResetPinProfileId = args.internalProfileId + val receivedProfileResetPinIsAdmin = args.isAdmin + + assertThat(receivedProfileResetPinProfileId).isEqualTo(0) + assertThat(receivedProfileResetPinIsAdmin).isEqualTo(true) + } + } + } + + @Test + fun testFragment_fragmentLoaded_whenIsAdminFalse_verifyCorrectArgumentsPassed() { + ActivityScenario.launch( + ProfileResetPinActivity.createProfileResetPinActivity( + context = context, + profileId = 0, + isAdmin = false + ) + ).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val profileResetPinFragment = activity.supportFragmentManager + .findFragmentById(R.id.profile_reset_pin_fragment_placeholder) as ProfileResetPinFragment + + val arguments = checkNotNull(profileResetPinFragment.arguments) { + "Expected arguments to be passed to ProfileResetPinFragment" + } + val args = + arguments.getProto( + ProfileResetPinFragment.PROFILE_RESET_PIN_FRAGMENT_ARGUMENTS_KEY, + ProfileResetPinFragmentArguments.getDefaultInstance() + ) + val receivedProfileResetPinProfileId = args.internalProfileId + val receivedProfileResetPinIsAdmin = args.isAdmin + + assertThat(receivedProfileResetPinProfileId).isEqualTo(0) + assertThat(receivedProfileResetPinIsAdmin).isEqualTo(false) + } + } + } + // 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/spotlight/SpotlightFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/spotlight/SpotlightFragmentTest.kt index 1e37adc5e68..9fb017111e3 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/spotlight/SpotlightFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/spotlight/SpotlightFragmentTest.kt @@ -14,6 +14,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import dagger.Component import org.junit.After import org.junit.Before @@ -93,6 +94,7 @@ import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject @@ -339,6 +341,25 @@ class SpotlightFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + TestPlatformParameterModule.forceEnableSpotlightUi(true) + launch( + createSpotlightFragmentTestActivity(context) + ).use { scenario -> + testCoroutineDispatchers.runCurrent() + + scenario.onActivity { activity -> + val spotlightFragment = activity.supportFragmentManager + .findFragmentByTag(SpotlightManager.SPOTLIGHT_FRAGMENT_TAG) as SpotlightFragment + val receivedInternalProfileId = spotlightFragment + .arguments?.extractCurrentUserProfileId()?.internalId ?: -1 + + assertThat(receivedInternalProfileId).isEqualTo(0) + } + } + } + private fun setUpTestApplicationComponent() { ApplicationProvider.getApplicationContext().inject(this) } diff --git a/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt index c93e0f8460a..c8013552937 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt @@ -67,6 +67,7 @@ import org.oppia.android.app.customview.LessonThumbnailImageView import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.StoryFragmentArguments import org.oppia.android.app.player.exploration.ExplorationActivity import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPosition @@ -132,6 +133,7 @@ import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.accessibility.FakeAccessibilityService import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.EventLoggingConfigurationModule @@ -144,6 +146,7 @@ import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule import org.oppia.android.util.parser.image.ImageLoader import org.oppia.android.util.parser.image.ImageParsingModule import org.oppia.android.util.parser.image.ImageTransformation +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject @@ -772,6 +775,45 @@ class StoryFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + launch(createFractionsStoryActivityIntent()).use { scenario -> + testCoroutineDispatchers.runCurrent() + + scenario.onActivity { activity -> + val storyFragment = activity.supportFragmentManager + .findFragmentById(R.id.story_fragment_placeholder) as StoryFragment + + val arguments = checkNotNull(storyFragment.arguments) { + "Expected arguments to be passed to StoryFragment." + } + val args = arguments.getProto( + StoryFragment.STORY_FRAGMENT_ARGUMENTS_KEY, + StoryFragmentArguments.getDefaultInstance() + ) + + val receivedInternalProfileId = arguments.extractCurrentUserProfileId().internalId + val receivedClassroomId = + checkNotNull(args.classroomId) { + "Expected classroomId to be passed to StoryFragment." + } + val receivedTopicId = + checkNotNull(args.topicId) { + "Expected topicId to be passed to StoryFragment." + } + val receivedStoryId = + checkNotNull(args.storyId) { + "Expected storyId to be passed to StoryFragment." + } + + assertThat(receivedInternalProfileId).isEqualTo(internalProfileId) + assertThat(receivedClassroomId).isEqualTo(TEST_CLASSROOM_ID_1) + assertThat(receivedTopicId).isEqualTo(FRACTIONS_TOPIC_ID) + assertThat(receivedStoryId).isEqualTo(FRACTIONS_STORY_ID_0) + } + } + } + @Config(qualifiers = "+sw600dp") @Test // TODO(#4212): Error -> No views in hierarchy found matching fun testStoryFragment_completedChapter_checkProgressDrawableIsCorrect() { diff --git a/app/src/sharedTest/java/org/oppia/android/app/survey/SurveyFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/survey/SurveyFragmentTest.kt index b600558c0da..332914c0c7c 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/survey/SurveyFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/survey/SurveyFragmentTest.kt @@ -48,6 +48,7 @@ import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ScreenName +import org.oppia.android.app.model.SurveyFragmentArguments import org.oppia.android.app.model.SurveyQuestionName import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView @@ -101,6 +102,7 @@ import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.extractCurrentAppScreenName @@ -113,6 +115,7 @@ import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule import org.oppia.android.util.parser.image.GlideImageLoaderModule import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject @@ -535,6 +538,32 @@ class SurveyFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + launch( + createSurveyActivityIntent() + ).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val surveyFragment = activity.supportFragmentManager + .findFragmentById(R.id.survey_fragment_placeholder) as SurveyFragment + val args = surveyFragment.arguments!!.getProto( + SurveyFragment.SURVEY_FRAGMENT_ARGUMENTS_KEY, + SurveyFragmentArguments.getDefaultInstance() + ) + val receivedInternalProfileId = surveyFragment.arguments!! + .extractCurrentUserProfileId().internalId + val receivedTopicId = args.topicId!! + val receivedExplorationId = args.explorationId!! + + assertThat(receivedInternalProfileId).isEqualTo(0) + assertThat(receivedTopicId).isEqualTo(TEST_TOPIC_ID_0) + assertThat(receivedExplorationId).isEqualTo(TEST_EXPLORATION_ID_2) + } + } + } + private fun selectNpsAnswerAndMoveToNextQuestion(npsScore: Int) { onView( allOf( diff --git a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt index 89403f4efe2..08c49683c59 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt @@ -18,6 +18,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import dagger.Component import org.hamcrest.Matchers.allOf import org.junit.After @@ -39,6 +40,8 @@ import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.help.thirdparty.LicenseListActivity import org.oppia.android.app.help.thirdparty.ThirdPartyDependencyListActivity +import org.oppia.android.app.help.thirdparty.ThirdPartyDependencyListFragment +import org.oppia.android.app.model.ThirdPartyDependencyListFragmentArguments import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPosition import org.oppia.android.app.shim.ViewBindingShimModule @@ -86,6 +89,7 @@ import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.EventLoggingConfigurationModule @@ -447,6 +451,30 @@ class ThirdPartyDependencyListFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + launch(ThirdPartyDependencyListActivity::class.java).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val thirdPartyDependencyListFragment = activity.supportFragmentManager + .findFragmentById(R.id.third_party_dependency_list_fragment_placeholder) + as ThirdPartyDependencyListFragment + + val arguments = checkNotNull(thirdPartyDependencyListFragment.arguments) { + "Expected arguments to be passed to ThirdPartyDependencyListFragment" + } + val args = arguments.getProto( + "ThirdPartyDependencyListFragment.arguments", + ThirdPartyDependencyListFragmentArguments.getDefaultInstance() + ) + val receivedIsMultipane = args?.isMultipane ?: false + + assertThat(receivedIsMultipane).isEqualTo(false) + } + } + } + private fun retrieveDependencyName(id: Int): String { return ApplicationProvider.getApplicationContext() .resources.getString(id) 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 29dde65b725..8be20c6dc55 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 @@ -26,6 +26,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import dagger.Component import dagger.Module import dagger.Provides @@ -58,6 +59,7 @@ 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.RevisionCardFragmentArguments import org.oppia.android.app.model.WrittenTranslationLanguageSelection import org.oppia.android.app.options.OptionsActivity import org.oppia.android.app.player.exploration.ExplorationActivity @@ -122,6 +124,7 @@ import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.LoadImagesFromAssets import org.oppia.android.util.caching.LoadLessonProtosFromAssets +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.EventLoggingConfigurationModule @@ -133,6 +136,7 @@ import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule import org.oppia.android.util.parser.image.GlideImageLoaderModule import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject @@ -810,6 +814,45 @@ class RevisionCardFragmentTest { } } + @Test + fun testFragment_fragmentLoaded_verifyCorrectArgumentsPassed() { + launch( + createRevisionCardActivityIntent( + context, + profileId.internalId, + FRACTIONS_TOPIC_ID, + subtopicId = 2, + FRACTIONS_SUBTOPIC_LIST_SIZE + ) + ).use { scenario -> + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + + val revisionCardFragment = activity.supportFragmentManager + .findFragmentById(R.id.revision_card_fragment_placeholder) as RevisionCardFragment + val arguments = checkNotNull(revisionCardFragment.arguments) { + "Expected arguments to be passed to StoryFragment" + } + val args = arguments.getProto( + RevisionCardFragment.REVISION_CARD_FRAGMENT_ARGUMENTS_KEY, + RevisionCardFragmentArguments.getDefaultInstance() + ) + val receivedTopicId = + checkNotNull(args?.topicId) { + "Expected topicId to be passed to RevisionCardFragment" + } + val receivedSubtopicId = args?.subtopicId ?: -1 + val receivedProfileId = arguments.extractCurrentUserProfileId() + val receivedSubtopicListSize = args?.subtopicListSize ?: -1 + + assertThat(receivedTopicId).isEqualTo(FRACTIONS_TOPIC_ID) + assertThat(receivedSubtopicId).isEqualTo(2) + assertThat(receivedProfileId).isEqualTo(profileId) + assertThat(receivedSubtopicListSize).isEqualTo(FRACTIONS_SUBTOPIC_LIST_SIZE) + } + } + } + /** See the version in StateFragmentTest for documentation details. */ @Suppress("SameParameterValue") private fun openClickableSpan(text: String): ViewAction { diff --git a/domain/src/main/java/org/oppia/android/domain/classroom/ClassroomController.kt b/domain/src/main/java/org/oppia/android/domain/classroom/ClassroomController.kt index 20cc3ee832e..dc99f75c5ce 100644 --- a/domain/src/main/java/org/oppia/android/domain/classroom/ClassroomController.kt +++ b/domain/src/main/java/org/oppia/android/domain/classroom/ClassroomController.kt @@ -72,6 +72,32 @@ class ClassroomController @Inject constructor( ) } + /** + * Returns the list of [ClassroomRecord]s currently available in the app. + */ + fun getClassrooms(): List { + return if (loadLessonProtosFromAssets) { + assetRepository.loadProtoFromLocalAssets( + assetName = "classrooms", + baseMessage = ClassroomIdList.getDefaultInstance() + ).classroomIdsList.map { classroomId -> + getClassroomById(classroomId) + } + } else loadClassroomsFromJson() + } + + /** + * Returns the [ClassroomRecord] associated with the given [classroomId]. + */ + fun getClassroomById(classroomId: String): ClassroomRecord { + return if (loadLessonProtosFromAssets) { + assetRepository.tryLoadProtoFromLocalAssets( + assetName = classroomId, + defaultMessage = ClassroomRecord.getDefaultInstance() + ) ?: ClassroomRecord.getDefaultInstance() + } else loadClassroomByIdFromJson(classroomId) + } + /** * Returns the list of [TopicSummary]s currently tracked by the app, possibly up to * [EVICTION_TIME_MILLIS] old. @@ -90,7 +116,7 @@ class ClassroomController @Inject constructor( */ fun getClassroomIdByTopicId(topicId: String): String { var classroomId = "" - loadClassrooms().forEach { + getClassrooms().forEach { if (it.topicPrerequisitesMap.keys.contains(topicId)) { classroomId = it.id } @@ -333,17 +359,6 @@ class ClassroomController @Inject constructor( .build() } - private fun loadClassrooms(): List { - return if (loadLessonProtosFromAssets) { - assetRepository.loadProtoFromLocalAssets( - assetName = "classrooms", - baseMessage = ClassroomIdList.getDefaultInstance() - ).classroomIdsList.map { classroomId -> - loadClassroomById(classroomId) - } - } else loadClassroomsFromJson() - } - private fun loadClassroomsFromJson(): List { // Load the classrooms.json file. val classroomIdsObj = jsonAssetRetriever.loadJsonFromAsset("classrooms.json") @@ -359,27 +374,20 @@ class ClassroomController @Inject constructor( val classroomId = checkNotNull(classroomIds.optString(i)) { "Expected non-null classroom ID at index $i." } - val classroomRecord = loadClassroomById(classroomId) + val classroomRecord = getClassroomById(classroomId) classroomRecords.add(classroomRecord) } return classroomRecords } - private fun loadClassroomById(classroomId: String): ClassroomRecord { - return if (loadLessonProtosFromAssets) { - assetRepository.tryLoadProtoFromLocalAssets( - assetName = classroomId, - defaultMessage = ClassroomRecord.getDefaultInstance() - ) ?: ClassroomRecord.getDefaultInstance() - } else loadClassroomByIdFromJson(classroomId) - } - private fun loadClassroomByIdFromJson(classroomId: String): ClassroomRecord { // Load the classroom obj. val classroomObj = jsonAssetRetriever.loadJsonFromAsset("$classroomId.json") checkNotNull(classroomObj) { "Failed to load $classroomId.json." } + val classroomTitle = classroomObj.getJSONObject("classroom_title") + // Load the topic prerequisite map. val topicPrereqsObj = checkNotNull(classroomObj.optJSONObject("topic_prerequisites")) { "Expected classroom to have non-null topic_prerequisites." @@ -398,6 +406,10 @@ class ClassroomController @Inject constructor( id = checkNotNull(classroomObj.optString("classroom_id")) { "Expected classroom to have ID." } + translatableTitle = SubtitledHtml.newBuilder().apply { + contentId = classroomTitle.getStringFromObject("content_id") + html = classroomTitle.getStringFromObject("html") + }.build() putAllTopicPrerequisites( topicPrereqs.mapValues { (_, topicIds) -> ClassroomRecord.TopicIdList.newBuilder().apply { diff --git a/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt b/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt index 4fa2ed9edef..b82f8f56f0f 100644 --- a/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt +++ b/domain/src/main/java/org/oppia/android/domain/topic/TopicListController.kt @@ -5,9 +5,7 @@ import org.json.JSONObject import org.oppia.android.app.model.ChapterPlayState import org.oppia.android.app.model.ChapterProgress import org.oppia.android.app.model.ChapterSummary -import org.oppia.android.app.model.ClassroomIdList import org.oppia.android.app.model.ClassroomRecord -import org.oppia.android.app.model.ClassroomRecord.TopicIdList import org.oppia.android.app.model.ComingSoonTopicList import org.oppia.android.app.model.EphemeralTopicSummary import org.oppia.android.app.model.LessonThumbnail @@ -29,7 +27,7 @@ import org.oppia.android.app.model.TopicProgress import org.oppia.android.app.model.TopicRecord import org.oppia.android.app.model.TopicSummary import org.oppia.android.app.model.UpcomingTopic -import org.oppia.android.domain.classroom.TEST_CLASSROOM_ID_0 +import org.oppia.android.domain.classroom.ClassroomController import org.oppia.android.domain.translation.TranslationController import org.oppia.android.domain.util.JsonAssetRetriever import org.oppia.android.domain.util.getStringFromObject @@ -101,6 +99,7 @@ class TopicListController @Inject constructor( private val oppiaClock: OppiaClock, private val assetRepository: AssetRepository, private val translationController: TranslationController, + private val classroomController: ClassroomController, @LoadLessonProtosFromAssets private val loadLessonProtosFromAssets: Boolean ) { @@ -137,7 +136,7 @@ class TopicListController @Inject constructor( private fun createTopicList(contentLocale: OppiaLocale.ContentLocale): TopicList { return if (loadLessonProtosFromAssets) { - val topicIdList = loadCombinedClassroomsTopicIdList() + val topicIdList = loadCombinedTopicIdList() return TopicList.newBuilder().apply { // Only include topics currently playable in the topic list. addAllTopicSummary( @@ -152,7 +151,7 @@ class TopicListController @Inject constructor( } private fun loadTopicListFromJson(contentLocale: OppiaLocale.ContentLocale): TopicList { - val topicIdList = loadCombinedClassroomsTopicIdList() + val topicIdList = loadCombinedTopicIdList() val topicListBuilder = TopicList.newBuilder() for (topicId in topicIdList) { val ephemeralSummary = createEphemeralTopicSummary(topicId, contentLocale) @@ -166,7 +165,7 @@ class TopicListController @Inject constructor( } private fun computeComingSoonTopicList(): ComingSoonTopicList { - val topicIdList = loadCombinedClassroomsTopicIdList() + val topicIdList = loadCombinedTopicIdList() val comingSoonTopicListBuilder = ComingSoonTopicList.newBuilder() for (topicId in topicIdList) { val upcomingTopicSummary = createUpcomingTopicSummary(topicId) @@ -185,7 +184,7 @@ class TopicListController @Inject constructor( contentLocale: OppiaLocale.ContentLocale ): EphemeralTopicSummary { val topicSummary = createTopicSummary(topicId) - val classroomRecord = loadClassroomById(topicSummary.classroomId) + val classroomRecord = classroomController.getClassroomById(topicSummary.classroomId) return EphemeralTopicSummary.newBuilder().apply { this.topicSummary = topicSummary writtenTranslationContext = @@ -217,7 +216,7 @@ class TopicListController @Inject constructor( this.topicId = topicId putAllWrittenTranslations(topicRecord.writtenTranslationsMap) title = topicRecord.translatableTitle - classroomId = getClassroomIdByTopicId(topicId) + classroomId = classroomController.getClassroomIdByTopicId(topicId) totalChapterCount = storyRecords.map { it.chaptersList.size }.sum() topicThumbnail = topicRecord.topicThumbnail topicPlayAvailability = if (topicRecord.isPublished) { @@ -259,7 +258,7 @@ class TopicListController @Inject constructor( contentId = "title" html = jsonObject.getStringFromObject("topic_name") }.build() - val classroomId = getClassroomIdByTopicId(topicId) + val classroomId = classroomController.getClassroomIdByTopicId(topicId) // No written translations are included since none are retrieved from JSON. return TopicSummary.newBuilder() .setTopicId(topicId) @@ -296,7 +295,7 @@ class TopicListController @Inject constructor( html = jsonObject.getStringFromObject("topic_name") }.build() - val classroomId = getClassroomIdByTopicId(topicId) + val classroomId = classroomController.getClassroomIdByTopicId(topicId) val classroomJsonObject = jsonAssetRetriever.loadJsonFromAsset("$classroomId.json")!! val classroomTitle = classroomJsonObject.getJSONObject("classroom_title").let { @@ -369,8 +368,8 @@ class TopicListController @Inject constructor( sortedTopicProgressList.forEach { topicProgress -> val topic = topicController.retrieveTopic(topicProgress.topicId) val classroom = topic?.topicId?.let { topicId -> - val classroomId = getClassroomIdByTopicId(topicId) - loadClassroomById(classroomId) + val classroomId = classroomController.getClassroomIdByTopicId(topicId) + classroomController.getClassroomById(classroomId) } ?: ClassroomRecord.getDefaultInstance() // Ignore topics that are no longer on the device, or that have been unpublished. if (topic?.topicPlayAvailability?.availabilityCase == AVAILABLE_TO_PLAY_NOW) { @@ -556,7 +555,7 @@ class TopicListController @Inject constructor( * being suggested. */ private fun retrieveTopicDependencies(topicId: String): List { - val classrooms = loadClassrooms() + val classrooms = classroomController.getClassrooms() for (classroom in classrooms) { if (classroom.topicPrerequisitesMap.containsKey(topicId)) { return classroom.topicPrerequisitesMap.getValue(topicId).topicIdsList @@ -589,7 +588,7 @@ class TopicListController @Inject constructor( contentLocale: OppiaLocale.ContentLocale ): List { return if (loadLessonProtosFromAssets) { - val topicIdList = loadCombinedClassroomsTopicIdList() + val topicIdList = loadCombinedTopicIdList() return computeSuggestedStoriesForTopicIds(topicProgressList, topicIdList, contentLocale) } else computeSuggestedStoriesFromJson(topicProgressList, contentLocale) } @@ -599,7 +598,7 @@ class TopicListController @Inject constructor( contentLocale: OppiaLocale.ContentLocale ): List { // All topics that could potentially be recommended. - val topicIdList = loadCombinedClassroomsTopicIdList() + val topicIdList = loadCombinedTopicIdList() return computeSuggestedStoriesForTopicIds(topicProgressList, topicIdList, contentLocale) } @@ -713,7 +712,7 @@ class TopicListController @Inject constructor( ) val classroomRecord = assetRepository.loadProtoFromLocalAssets( - assetName = getClassroomIdByTopicId(topicId), + assetName = classroomController.getClassroomIdByTopicId(topicId), baseMessage = ClassroomRecord.getDefaultInstance() ) return PromotedStory.newBuilder().apply { @@ -783,7 +782,7 @@ class TopicListController @Inject constructor( }.build() } ?: SubtitledHtml.getDefaultInstance() - val classroomId = getClassroomIdByTopicId(topicId) + val classroomId = classroomController.getClassroomIdByTopicId(topicId) val classroomJson = jsonAssetRetriever.loadJsonFromAsset("$classroomId.json") if (classroomJson!!.optString("classroom_title").isNullOrEmpty()) return null @@ -867,104 +866,8 @@ class TopicListController @Inject constructor( .build() } - private fun getClassroomIdByTopicId(topicId: String): String { - var classroomId = TEST_CLASSROOM_ID_0 - loadClassrooms().forEach { - if (it.topicPrerequisitesMap.keys.contains(topicId)) { - classroomId = it.id - } - } - return classroomId - } - - // TODO(#5344): Remove this in favor of per-classroom data handling. - private fun loadClassrooms(): List { - return if (loadLessonProtosFromAssets) { - assetRepository.loadProtoFromLocalAssets( - assetName = "classrooms", - baseMessage = ClassroomIdList.getDefaultInstance() - ).classroomIdsList.map { classroomId -> - loadClassroomById(classroomId) - } - } else loadClassroomsFromJson() - } - - // TODO(#5344): Remove this in favor of per-classroom data handling. - private fun loadClassroomsFromJson(): List { - // Load the classrooms.json file. - val classroomIdsObj = jsonAssetRetriever.loadJsonFromAsset("classrooms.json") - checkNotNull(classroomIdsObj) { "Failed to load classrooms.json." } - val classroomIds = classroomIdsObj.optJSONArray("classroom_id_list") - checkNotNull(classroomIds) { "classrooms.json is missing classroom IDs." } - - // Initialize a list to store the [ClassroomRecord]s. - val classroomRecords = mutableListOf() - - // Iterate over all classroomIds and load each classroom's JSON. - for (i in 0 until classroomIds.length()) { - val classroomId = checkNotNull(classroomIds.optString(i)) { - "Expected non-null classroom ID at index $i." - } - val classroomRecord = loadClassroomById(classroomId) - classroomRecords.add(classroomRecord) - } - - return classroomRecords - } - - // TODO(#5344): Move this to classroom controller. - private fun loadClassroomById(classroomId: String): ClassroomRecord { - return if (loadLessonProtosFromAssets) { - assetRepository.tryLoadProtoFromLocalAssets( - assetName = classroomId, - defaultMessage = ClassroomRecord.getDefaultInstance() - ) ?: ClassroomRecord.getDefaultInstance() - } else loadClassroomByIdFromJson(classroomId) - } - - // TODO(#5344): Remove this in favor of per-classroom data handling. - private fun loadClassroomByIdFromJson(classroomId: String): ClassroomRecord { - // Load the classroom obj. - val classroomObj = jsonAssetRetriever.loadJsonFromAsset("$classroomId.json") - checkNotNull(classroomObj) { "Failed to load $classroomId.json." } - - val classroomTitle = classroomObj.getJSONObject("classroom_title") - - // Load the topic prerequisite map. - val topicPrereqsObj = checkNotNull(classroomObj.optJSONObject("topic_prerequisites")) { - "Expected classroom to have non-null topic_prerequisites." - } - val topicPrereqs = topicPrereqsObj.keys().asSequence().associateWith { topicId -> - val topicIdArray = checkNotNull(topicPrereqsObj.optJSONArray(topicId)) { - "Expected topic $topicId to have a non-null string list." - } - return@associateWith List(topicIdArray.length()) { index -> - checkNotNull(topicIdArray.optString(index)) { - "Expected topic $topicId to have non-null string at index $index." - } - } - } - return ClassroomRecord.newBuilder().apply { - id = checkNotNull(classroomObj.optString("classroom_id")) { - "Expected classroom to have ID." - } - translatableTitle = SubtitledHtml.newBuilder().apply { - contentId = classroomTitle.getStringFromObject("content_id") - html = classroomTitle.getStringFromObject("html") - }.build() - putAllTopicPrerequisites( - topicPrereqs.mapValues { (_, topicIds) -> - TopicIdList.newBuilder().apply { - addAllTopicIds(topicIds) - }.build() - } - ) - }.build() - } - - // TODO(#5344): Remove this in favor of per-classroom data handling. - private fun loadCombinedClassroomsTopicIdList(): List = - loadClassrooms().flatMap { it.topicPrerequisitesMap.keys.toList() } + private fun loadCombinedTopicIdList(): List = + classroomController.getClassrooms().flatMap { it.topicPrerequisitesMap.keys.toList() } } internal fun createTopicThumbnailFromJson(topicJsonObject: JSONObject): LessonThumbnail { diff --git a/domain/src/test/java/org/oppia/android/domain/classroom/ClassroomControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/classroom/ClassroomControllerTest.kt index 24129abc307..6c1cee650bf 100644 --- a/domain/src/test/java/org/oppia/android/domain/classroom/ClassroomControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/classroom/ClassroomControllerTest.kt @@ -131,6 +131,23 @@ class ClassroomControllerTest { assertThat(classroomList.classroomSummaryList.size).isEqualTo(2) } + @Test + fun testGetClassrooms_returnsAllClassrooms() { + val classrooms = classroomController.getClassrooms() + + assertThat(classrooms[0].id).isEqualTo(TEST_CLASSROOM_ID_0) + assertThat(classrooms[1].id).isEqualTo(TEST_CLASSROOM_ID_1) + assertThat(classrooms[2].id).isEqualTo(TEST_CLASSROOM_ID_2) + } + + @Test + fun testGetClassroomById_hasCorrectClassroomInfo() { + val classroom = classroomController.getClassroomById(TEST_CLASSROOM_ID_0) + + assertThat(classroom.id).isEqualTo(TEST_CLASSROOM_ID_0) + assertThat(classroom.translatableTitle.html).isEqualTo("Science") + } + @Test fun testRetrieveTopicList_isSuccessful() { val topicListProvider = classroomController.getTopicList(profileId0, TEST_CLASSROOM_ID_0)