From dd80399d78c63fced0a82a25ee88095374622e5c Mon Sep 17 00:00:00 2001 From: Vishwajith Shettigar <76042077+Vishwajith-Shettigar@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:08:47 +0530 Subject: [PATCH] Fix part of #4865 and Fix #4986 : Move all fragment arguments, activity intent extras, and saved instance state over to protos (#5248) ## Explanation Fixes #4986 Move all fragment arguments, activity extras, and saved instance state bundle usage over to using protos entirely. Fix part of #4865 Introduce a ProfileID decorator to pack ProfileId into all Intents so that it can be easily accessed by underlying fragments and child classes. Some classes already use this utility where it was not easy to add profileId to the intent proto bundle. ## 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 --------- Co-authored-by: Vishwajith-Shettigar --- app/BUILD.bazel | 1 + .../AdministratorControlsActivity.kt | 54 +- .../AdministratorControlsActivityPresenter.kt | 23 +- .../AdministratorControlsFragment.kt | 27 +- .../AdministratorControlsFragmentPresenter.kt | 9 +- .../CompletedStoryListActivity.kt | 18 +- .../CompletedStoryListFragment.kt | 24 +- .../devoptions/DeveloperOptionsActivity.kt | 12 +- .../app/devoptions/DeveloperOptionsStarter.kt | 3 +- .../devoptions/DeveloperOptionsStarterImpl.kt | 5 +- .../MarkChaptersCompletedActivity.kt | 34 +- .../MarkChaptersCompletedFragment.kt | 72 ++- .../MarkChaptersCompletedTestActivity.kt | 30 +- .../MarkStoriesCompletedActivity.kt | 12 +- .../MarkStoriesCompletedFragment.kt | 48 +- .../MarkStoriesCompletedTestActivity.kt | 13 +- .../MarkTopicsCompletedActivity.kt | 12 +- .../MarkTopicsCompletedFragment.kt | 46 +- .../MarkTopicsCompletedTestActivity.kt | 14 +- .../testing/DeveloperOptionsTestActivity.kt | 13 +- .../NavigationDrawerFragmentPresenter.kt | 17 +- .../oppia/android/app/help/HelpActivity.kt | 63 ++- .../android/app/help/HelpActivityPresenter.kt | 22 +- .../oppia/android/app/help/HelpFragment.kt | 23 +- .../help/faq/faqsingle/FAQSingleActivity.kt | 29 +- .../help/thirdparty/LicenseListActivity.kt | 22 +- .../help/thirdparty/LicenseListFragment.kt | 34 +- .../thirdparty/LicenseTextViewerActivity.kt | 31 +- .../thirdparty/LicenseTextViewerFragment.kt | 36 +- .../ThirdPartyDependencyListFragment.kt | 29 +- .../HintsAndSolutionDialogFragment.kt | 97 ++-- .../oppia/android/app/home/HomeActivity.kt | 14 +- .../android/app/home/HomeFragmentPresenter.kt | 5 +- .../recentlyplayed/RecentlyPlayedFragment.kt | 24 +- .../app/mydownloads/MyDownloadsActivity.kt | 20 +- .../OngoingTopicListActivity.kt | 13 +- .../OngoingTopicListFragment.kt | 12 +- .../app/options/AppLanguageActivity.kt | 11 +- .../app/options/AppLanguageFragment.kt | 11 +- .../android/app/options/OptionsActivity.kt | 64 ++- .../app/options/OptionsActivityPresenter.kt | 2 +- .../android/app/options/OptionsFragment.kt | 45 +- .../app/options/OptionsFragmentPresenter.kt | 6 +- .../android/app/player/audio/AudioFragment.kt | 11 +- .../player/audio/LanguageDialogFragment.kt | 45 +- .../ExplorationActivityPresenter.kt | 4 +- .../android/app/player/state/StateFragment.kt | 38 +- .../testing/StateFragmentTestActivity.kt | 41 +- .../StateFragmentTestActivityPresenter.kt | 18 +- .../ProgressDatabaseFullDialogFragment.kt | 32 +- .../android/app/profile/AddProfileActivity.kt | 9 +- .../profile/AddProfileActivityPresenter.kt | 10 +- .../android/app/profile/AdminAuthActivity.kt | 24 +- .../app/profile/AdminAuthActivityPresenter.kt | 25 +- .../android/app/profile/AdminPinActivity.kt | 17 +- .../app/profile/AdminPinActivityPresenter.kt | 22 +- .../profile/AdminSettingsDialogFragment.kt | 25 +- .../app/profile/PinPasswordActivity.kt | 14 +- .../profile/PinPasswordActivityPresenter.kt | 16 +- .../ProfileChooserFragmentPresenter.kt | 7 +- .../app/profile/ResetPinDialogFragment.kt | 36 +- .../profileprogress/ProfilePictureActivity.kt | 13 +- .../ProfileProgressActivity.kt | 8 +- .../ProfileProgressFragment.kt | 10 +- .../settings/profile/ProfileEditActivity.kt | 30 +- .../profile/ProfileEditActivityPresenter.kt | 11 +- .../ProfileEditDeletionDialogFragment.kt | 9 +- .../settings/profile/ProfileEditFragment.kt | 34 +- .../settings/profile/ProfileListFragment.kt | 26 +- .../settings/profile/ProfileRenameActivity.kt | 16 +- .../settings/profile/ProfileRenameFragment.kt | 19 +- .../profile/ProfileResetPinActivity.kt | 20 +- .../ProfileResetPinActivityPresenter.kt | 15 +- .../profile/ProfileResetPinFragment.kt | 38 +- .../org/oppia/android/app/shim/BUILD.bazel | 1 + .../app/spotlight/SpotlightFragment.kt | 19 +- .../oppia/android/app/story/StoryActivity.kt | 31 +- .../oppia/android/app/story/StoryFragment.kt | 43 +- .../ExitSurveyConfirmationDialogFragment.kt | 6 +- .../app/survey/SurveyActivityPresenter.kt | 23 +- .../android/app/survey/SurveyFragment.kt | 38 +- .../app/survey/SurveyWelcomeDialogFragment.kt | 54 +- ...ministratorControlsFragmentTestActivity.kt | 8 +- .../app/testing/AudioFragmentTestActivity.kt | 13 +- .../testing/NavigationDrawerTestActivity.kt | 12 +- .../ProfileEditFragmentTestActivity.kt | 9 +- ...rofileEditFragmentTestActivityPresenter.kt | 4 +- .../testing/SpotlightFragmentTestActivity.kt | 11 +- .../SpotlightFragmentTestActivityPresenter.kt | 6 +- .../TestFontScaleConfigurationUtilActivity.kt | 16 +- .../oppia/android/app/topic/TopicActivity.kt | 46 +- .../app/topic/TopicActivityPresenter.kt | 23 +- .../oppia/android/app/topic/TopicFragment.kt | 15 +- .../topic/conceptcard/ConceptCardFragment.kt | 32 +- .../app/topic/info/TopicInfoFragment.kt | 37 +- .../app/topic/lessons/TopicLessonsFragment.kt | 73 ++- .../topic/practice/TopicPracticeFragment.kt | 75 ++- .../questionplayer/QuestionPlayerActivity.kt | 22 +- .../QuestionPlayerActivityPresenter.kt | 12 +- .../topic/revision/TopicRevisionFragment.kt | 36 +- .../revisioncard/RevisionCardActivity.kt | 39 +- .../RevisionCardActivityPresenter.kt | 4 +- .../revisioncard/RevisionCardFragment.kt | 37 +- .../app/walkthrough/WalkthroughActivity.kt | 7 +- .../end/WalkthroughFinalFragment.kt | 28 +- .../end/WalkthroughFinalFragmentPresenter.kt | 9 +- .../WalkthroughTopicListFragmentPresenter.kt | 9 +- .../WalkthroughWelcomeFragmentPresenter.kt | 7 +- .../AdministratorControlsActivityTest.kt | 76 +-- .../AppVersionActivityTest.kt | 4 +- .../CompletedStoryListActivityTest.kt | 38 +- .../DeveloperOptionsActivityTest.kt | 4 +- .../android/app/faq/FAQListFragmentTest.kt | 73 ++- .../android/app/help/HelpActivityTest.kt | 4 +- .../android/app/help/HelpFragmentTest.kt | 4 +- .../android/app/home/HomeActivityTest.kt | 47 +- .../OngoingTopicListActivityTest.kt | 35 +- .../app/options/OptionsActivityTest.kt | 4 +- .../app/options/OptionsFragmentTest.kt | 4 +- .../options/ReadingTextSizeFragmentTest.kt | 4 +- .../exploration/ExplorationActivityTest.kt | 37 +- .../app/profile/ProfileChooserFragmentTest.kt | 36 +- .../ProfileProgressFragmentTest.kt | 26 +- .../profile/ProfileEditFragmentTest.kt | 56 +- .../profile/ProfileRenameActivityTest.kt | 4 +- .../profile/ProfileRenameFragmentTest.kt | 22 +- .../NavigationDrawerActivityDebugTest.kt | 25 +- .../NavigationDrawerActivityProdTest.kt | 29 +- .../android/app/topic/TopicActivityTest.kt | 23 +- .../topic/lessons/TopicLessonsFragmentTest.kt | 16 +- .../practice/TopicPracticeFragmentTest.kt | 26 +- .../revisioncard/RevisionCardFragmentTest.kt | 54 +- .../activity/ActivityIntentFactoriesTest.kt | 30 +- .../android/app/home/HomeActivityLocalTest.kt | 11 +- .../oppia/android/app/testing/HomeSpanTest.kt | 4 +- .../AdministratorControlsFragmentTest.kt | 4 +- .../testing/options/OptionsFragmentTest.kt | 5 +- model/src/main/proto/arguments.proto | 515 +++++++++++++++++- .../assets/kdoc_validity_exemptions.textproto | 11 - .../oppia/android/util/profile/BUILD.bazel | 12 + .../CurrentUserProfileIdIntentDecorator.kt | 55 ++ ...CurrentUserProfileIdIntentDecoratorTest.kt | 76 +++ 142 files changed, 2812 insertions(+), 1110 deletions(-) create mode 100644 utility/src/main/java/org/oppia/android/util/profile/CurrentUserProfileIdIntentDecorator.kt create mode 100644 utility/src/test/java/org/oppia/android/util/profile/CurrentUserProfileIdIntentDecoratorTest.kt diff --git a/app/BUILD.bazel b/app/BUILD.bazel index cfa6c0b3c47..0ac39dc4ebf 100644 --- a/app/BUILD.bazel +++ b/app/BUILD.bazel @@ -804,6 +804,7 @@ kt_android_library( "//utility/src/main/java/org/oppia/android/util/extensions:bundle_extensions", "//utility/src/main/java/org/oppia/android/util/parser/image:image_loader", "//utility/src/main/java/org/oppia/android/util/parser/image:image_parsing_annonations", + "//utility/src/main/java/org/oppia/android/util/profile:current_user_profile_id_intent_decorator", "//utility/src/main/java/org/oppia/android/util/statusbar:status_bar_color", ], ) 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 f357a02a2ae..2cda2286d02 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 @@ -8,43 +8,33 @@ import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.administratorcontrols.appversion.AppVersionActivity import org.oppia.android.app.administratorcontrols.learneranalytics.ProfileAndDeviceIdActivity -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY +import org.oppia.android.app.model.AdministratorControlActivityStateBundle +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ScreenName.ADMINISTRATOR_CONTROLS_ACTIVITY import org.oppia.android.app.settings.profile.ProfileEditFragment import org.oppia.android.app.settings.profile.ProfileListActivity import org.oppia.android.app.settings.profile.ProfileListFragment import org.oppia.android.app.translation.AppLanguageResourceHandler -import org.oppia.android.util.extensions.getStringFromBundle +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId import javax.inject.Inject -/** Argument key for of title for selected controls in [AdministratorControlsActivity]. */ -const val SELECTED_CONTROLS_TITLE_SAVED_KEY = - "AdministratorControlsActivity.selected_controls_title" - -/** Argument key for of selected profile for selected controls in [AdministratorControlsActivity]. */ -const val SELECTED_PROFILE_ID_SAVED_KEY = - "AdministratorControlsActivity.selected_profile_id" - -/** Argument key for last loaded fragment in [AdministratorControlsActivity]. */ -const val LAST_LOADED_FRAGMENT_EXTRA_KEY = "AdministratorControlsActivity.last_loaded_fragment" - /** Argument key used to identify [ProfileListFragment] in the backstack. */ const val PROFILE_LIST_FRAGMENT = "PROFILE_LIST_FRAGMENT" /** Argument key used to identify [ProfileEditFragment] in the backstack. */ const val PROFILE_EDIT_FRAGMENT = "PROFILE_EDIT_FRAGMENT" -/** Argument key for the Profile deletion confirmation in [ProfileEditActivity]. */ -const val IS_PROFILE_DELETION_DIALOG_VISIBLE_KEY = - "ProfileEditActivity.is_profile_deletion_dialog_visible" - /** Argument key used to identify [AppVersionFragment] in the backstack. */ const val APP_VERSION_FRAGMENT = "APP_VERSION_FRAGMENT" /** Argument key used to identify [ProfileAndDeviceIdFragment] in the backstack. */ const val PROFILE_AND_DEVICE_ID_FRAGMENT = "PROFILE_AND_DEVICE_ID_FRAGMENT" +/** Argument key for Administrator Controls Activity saved state. */ +const val ADMINISTRATOR_CONTROLS_ACTIVITY_STATE_KEY = "ADMINISTRATOR_CONTROLS_ACTIVITY_STATE_KEY" + /** Activity [AdministratorControlsActivity] that allows user to change admin controls. */ class AdministratorControlsActivity : InjectableAutoLocalizedAppCompatActivity(), @@ -68,17 +58,24 @@ class AdministratorControlsActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val extraControlsTitle = - savedInstanceState?.getStringFromBundle(SELECTED_CONTROLS_TITLE_SAVED_KEY) + + val args = savedInstanceState?.getProto( + ADMINISTRATOR_CONTROLS_ACTIVITY_STATE_KEY, + AdministratorControlActivityStateBundle.getDefaultInstance() + ) + + val extraControlsTitle = args?.selectedControlsTitle + isProfileDeletionDialogVisible = - savedInstanceState?.getBoolean(IS_PROFILE_DELETION_DIALOG_VISIBLE_KEY) ?: false + args?.isProfileDeletionDialogVisible ?: false lastLoadedFragment = if (savedInstanceState != null) { - savedInstanceState.getStringFromBundle(LAST_LOADED_FRAGMENT_EXTRA_KEY) as String + args?.lastLoadedFragment as String } else { // TODO(#661): Change the default fragment in the right hand side to be EditAccount fragment in the case of multipane controls. PROFILE_LIST_FRAGMENT } - val selectedProfileId = savedInstanceState?.getInt(SELECTED_PROFILE_ID_SAVED_KEY) ?: -1 + val selectedProfileId = args?.selectedProfileId ?: -1 + administratorControlsActivityPresenter.handleOnCreate( extraControlsTitle, lastLoadedFragment, @@ -111,18 +108,17 @@ class AdministratorControlsActivity : } companion object { + /** Returns an [Intent] to start this activity. */ - fun createAdministratorControlsActivityIntent(context: Context, profileId: Int?): Intent { + fun createAdministratorControlsActivityIntent(context: Context, profileId: ProfileId?): Intent { + val intent = Intent(context, AdministratorControlsActivity::class.java) - intent.putExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, profileId) intent.decorateWithScreenName(ADMINISTRATOR_CONTROLS_ACTIVITY) + if (profileId != null) { + intent.decorateWithUserProfileId(profileId) + } return intent } - - /** Returns the argument key used to specify the user's internal profile ID. */ - fun getIntentKey(): String { - return NAVIGATION_PROFILE_ID_ARGUMENT_KEY - } } override fun onBackPressed() { diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt index c152e484dad..4802fc28ce9 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt @@ -9,11 +9,13 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.administratorcontrols.appversion.AppVersionFragment import org.oppia.android.app.administratorcontrols.learneranalytics.ProfileAndDeviceIdFragment import org.oppia.android.app.drawer.NavigationDrawerFragment +import org.oppia.android.app.model.AdministratorControlActivityStateBundle import org.oppia.android.app.settings.profile.LoadProfileEditDeletionDialogListener import org.oppia.android.app.settings.profile.ProfileEditFragment import org.oppia.android.app.settings.profile.ProfileListFragment import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.databinding.AdministratorControlsActivityBinding +import org.oppia.android.util.extensions.putProto import javax.inject.Inject /** The presenter for [AdministratorControlsActivity]. */ @@ -198,13 +200,18 @@ class AdministratorControlsActivityPresenter @Inject constructor( /** Saves the state of the views on configuration changes. */ fun handleOnSaveInstanceState(outState: Bundle) { val titleTextView = binding.extraControlsTitle - if (titleTextView != null) { - outState.putString(SELECTED_CONTROLS_TITLE_SAVED_KEY, titleTextView.text.toString()) - } - outState.putString(LAST_LOADED_FRAGMENT_EXTRA_KEY, lastLoadedFragment) - isProfileDeletionDialogVisible.let { - outState.putBoolean(IS_PROFILE_DELETION_DIALOG_VISIBLE_KEY, it) - } - selectedProfileId.let { outState.putInt(SELECTED_PROFILE_ID_SAVED_KEY, it) } + val args = AdministratorControlActivityStateBundle.newBuilder() + .apply { + if (titleTextView != null) { + selectedControlsTitle = titleTextView.text.toString() + } + lastLoadedFragment = this@AdministratorControlsActivityPresenter.lastLoadedFragment + this@AdministratorControlsActivityPresenter.isProfileDeletionDialogVisible.let { + isProfileDeletionDialogVisible = it + } + selectedProfileId = this@AdministratorControlsActivityPresenter.selectedProfileId + } + .build() + outState.putProto(ADMINISTRATOR_CONTROLS_ACTIVITY_STATE_KEY, args) } } diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragment.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragment.kt index 2cced887f7d..095316c008a 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragment.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragment.kt @@ -7,23 +7,30 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.AdministratorControlsFragmentArguments +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject +/** Argument key for Administrator Controls Fragment. */ +const val ADMINISTRATOR_CONTROLS_FRAGMENT_ARGUMENTS_KEY = "AdministratorControlsFragment.arguments" + /** Fragment that contains Administrator Controls of the application. */ class AdministratorControlsFragment : InjectableFragment() { @Inject lateinit var administratorControlsFragmentPresenter: AdministratorControlsFragmentPresenter companion object { - private const val IS_MULTIPANE_ARGUMENT_KEY = "AdministratorControlsFragment.is_multipane" /** Creates a new instance of [AdministratorControlsFragment]. */ fun newInstance(isMultipane: Boolean): AdministratorControlsFragment { - val args = Bundle() - args.putBoolean(IS_MULTIPANE_ARGUMENT_KEY, isMultipane) - val fragment = AdministratorControlsFragment() - fragment.arguments = args - return fragment + val args = + AdministratorControlsFragmentArguments.newBuilder().setIsMultipane(isMultipane).build() + return AdministratorControlsFragment().apply { + arguments = Bundle().apply { + putProto(ADMINISTRATOR_CONTROLS_FRAGMENT_ARGUMENTS_KEY, args) + } + } } } @@ -37,11 +44,15 @@ class AdministratorControlsFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to AdministratorControlsFragment" } - val isMultipane = args.getBoolean(IS_MULTIPANE_ARGUMENT_KEY) + val args = arguments.getProto( + ADMINISTRATOR_CONTROLS_FRAGMENT_ARGUMENTS_KEY, + AdministratorControlsFragmentArguments.getDefaultInstance() + ) + val isMultipane = args.isMultipane return administratorControlsFragmentPresenter .handleCreateView(inflater, container, isMultipane) } diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt index 52c193d536c..843b1dbc2fe 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentPresenter.kt @@ -13,7 +13,6 @@ import org.oppia.android.app.administratorcontrols.administratorcontrolsitemview import org.oppia.android.app.administratorcontrols.administratorcontrolsitemviewmodel.AdministratorControlsItemViewModel import org.oppia.android.app.administratorcontrols.administratorcontrolsitemviewmodel.AdministratorControlsProfileAndDeviceIdViewModel import org.oppia.android.app.administratorcontrols.administratorcontrolsitemviewmodel.AdministratorControlsProfileViewModel -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.ProfileId import org.oppia.android.app.recyclerview.BindableAdapter @@ -24,6 +23,7 @@ import org.oppia.android.databinding.AdministratorControlsFragmentBinding import org.oppia.android.databinding.AdministratorControlsGeneralViewBinding import org.oppia.android.databinding.AdministratorControlsLearnerAnalyticsViewBinding import org.oppia.android.databinding.AdministratorControlsProfileViewBinding +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import java.security.InvalidParameterException import javax.inject.Inject @@ -56,7 +56,7 @@ class AdministratorControlsFragmentPresenter @Inject constructor( /* attachToRoot= */ false ) - internalProfileId = activity.intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + internalProfileId = activity.intent.extractCurrentUserProfileId().internalId profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() administratorControlsViewModel.setProfileId(profileId) @@ -193,14 +193,19 @@ class AdministratorControlsFragmentPresenter @Inject constructor( private enum class ViewType { /** Represents [View] for the general section. */ VIEW_TYPE_GENERAL, + /** Represents [View] for the profile section. */ VIEW_TYPE_PROFILE, + /** Represents [View] for the download permissions section. */ VIEW_TYPE_DOWNLOAD_PERMISSIONS, + /** Represents [View] for the app information section. */ VIEW_TYPE_APP_INFORMATION, + /** Represents [View] for the account actions section. */ VIEW_TYPE_ACCOUNT_ACTIONS, + /** Represents [View] for the learner analytics section. */ VIEW_TYPE_LEARNER_ANALYTICS } diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivity.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivity.kt index 1705b66748f..fe2d803f0a7 100644 --- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivity.kt +++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivity.kt @@ -5,8 +5,11 @@ import android.content.Intent 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.ScreenName.COMPLETED_STORY_LIST_ACTIVITY import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Activity for completed stories. */ @@ -17,21 +20,22 @@ class CompletedStoryListActivity : InjectableAutoLocalizedAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val internalProfileId: Int = - intent.getIntExtra(PROFILE_ID_EXTRA_KEY, -1) + + val internalProfileId: Int = intent?.extractCurrentUserProfileId()?.internalId ?: -1 completedStoryListActivityPresenter.handleOnCreate(internalProfileId) } companion object { // TODO(#1655): Re-restrict access to fields in tests post-Gradle. - const val PROFILE_ID_EXTRA_KEY = - "CompletedStoryListActivity.profile_id" /** Returns a new [Intent] to route to [CompletedStoryListActivity] for a specified profile ID. */ fun createCompletedStoryListActivityIntent(context: Context, internalProfileId: Int): Intent { - val intent = Intent(context, CompletedStoryListActivity::class.java) - intent.putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) - intent.decorateWithScreenName(COMPLETED_STORY_LIST_ACTIVITY) + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + val intent = Intent(context, CompletedStoryListActivity::class.java).apply { + decorateWithUserProfileId(profileId) + decorateWithScreenName(COMPLETED_STORY_LIST_ACTIVITY) + } + return intent } } diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragment.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragment.kt index 9bc1ff69b0b..4f11f4df5e1 100644 --- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragment.kt +++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragment.kt @@ -7,6 +7,9 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.ProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Fragment for displaying completed stories. */ @@ -15,17 +18,15 @@ class CompletedStoryListFragment : InjectableFragment() { // TODO(#1655): Re-restrict access to fields in tests post-Gradle. /** Key for accessing [CompletedStoryListFragment]. */ const val COMPLETED_STORY_LIST_FRAGMENT_TAG = "COMPLETED_STORY_LIST_FRAGMENT_TAG" - /** [String] key for mapping internalProfileId in [Bundle]. */ - internal const val COMPLETED_STORY_LIST_FRAGMENT_PROFILE_ID_KEY = - "CompletedStoryListFragment.profile_id" /** Returns a new [CompletedStoryListFragment] to display corresponding to the specified profile ID. */ fun newInstance(internalProfileId: Int): CompletedStoryListFragment { - val completedStoryListFragment = CompletedStoryListFragment() - val args = Bundle() - args.putInt(COMPLETED_STORY_LIST_FRAGMENT_PROFILE_ID_KEY, internalProfileId) - completedStoryListFragment.arguments = args - return completedStoryListFragment + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + return CompletedStoryListFragment().apply { + arguments = Bundle().apply { + decorateWithUserProfileId(profileId) + } + } } } @@ -42,12 +43,11 @@ class CompletedStoryListFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = checkNotNull(arguments) { + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to CompletedStoryListFragment" } - val internalProfileId = args.getInt( - COMPLETED_STORY_LIST_FRAGMENT_PROFILE_ID_KEY, -1 - ) + val profileId = arguments.extractCurrentUserProfileId() + val internalProfileId = profileId.internalId return completedStoryListFragmentPresenter.handleCreateView( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivity.kt index 92897d6b9c2..eda9f03bc9a 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivity.kt @@ -12,10 +12,12 @@ import org.oppia.android.app.devoptions.markstoriescompleted.MarkStoriesComplete import org.oppia.android.app.devoptions.marktopicscompleted.MarkTopicsCompletedActivity import org.oppia.android.app.devoptions.mathexpressionparser.MathExpressionParserActivity import org.oppia.android.app.devoptions.vieweventlogs.ViewEventLogsActivity -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ScreenName.DEVELOPER_OPTIONS_ACTIVITY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Activity for Developer Options. */ @@ -40,7 +42,7 @@ class DeveloperOptionsActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - internalProfileId = intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + internalProfileId = intent.extractCurrentUserProfileId().internalId developerOptionsActivityPresenter.handleOnCreate() title = resourceHandler.getStringInLocale(R.string.developer_options_activity_title) } @@ -80,11 +82,13 @@ class DeveloperOptionsActivity : } companion object { + /** Function to create intent for DeveloperOptionsActivity. */ - fun createDeveloperOptionsActivityIntent(context: Context, internalProfileId: Int): Intent { + fun createDeveloperOptionsActivityIntent(context: Context, profileId: ProfileId): Intent { + return Intent(context, DeveloperOptionsActivity::class.java).apply { - putExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, internalProfileId) decorateWithScreenName(DEVELOPER_OPTIONS_ACTIVITY) + decorateWithUserProfileId(profileId) } } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarter.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarter.kt index 20732c19e7b..6a1f5aadeb7 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarter.kt @@ -2,8 +2,9 @@ package org.oppia.android.app.devoptions import android.content.Context import android.content.Intent +import org.oppia.android.app.model.ProfileId /** Interface to create intent for [DeveloperOptionsActivity]. */ interface DeveloperOptionsStarter { - fun createIntent(context: Context, internalProfileId: Int): Intent + fun createIntent(context: Context, profileId: ProfileId): Intent } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterImpl.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterImpl.kt index 0182cbe08be..5c18f62a718 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterImpl.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterImpl.kt @@ -2,10 +2,11 @@ package org.oppia.android.app.devoptions import android.content.Context import android.content.Intent +import org.oppia.android.app.model.ProfileId import javax.inject.Inject /** Binds implementation of DeveloperOptionsStarter. */ class DeveloperOptionsStarterImpl @Inject constructor() : DeveloperOptionsStarter { - override fun createIntent(context: Context, internalProfileId: Int): Intent = - DeveloperOptionsActivity.createDeveloperOptionsActivityIntent(context, internalProfileId) + override fun createIntent(context: Context, profileId: ProfileId): Intent = + DeveloperOptionsActivity.createDeveloperOptionsActivityIntent(context, profileId) } 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 0c93279a099..ec8979dad41 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 @@ -7,8 +7,11 @@ import android.view.MenuItem import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity +import org.oppia.android.app.model.MarkChaptersCompletedActivityParams import org.oppia.android.app.model.ScreenName.MARK_CHAPTERS_COMPLETED_ACTIVITY import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName import javax.inject.Inject @@ -24,9 +27,14 @@ class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val internalProfileId = intent.getIntExtra(PROFILE_ID_EXTRA_KEY, /* defaultValue= */ -1) - val showConfirmationNotice = - intent.getBooleanExtra(SHOW_CONFIRMATION_NOTICE_EXTRA_KEY, /* defaultValue= */ false) + + val args = intent.getProtoExtra( + MARK_CHAPTERS_COMPLETED_ACTIVITY_PARAMS, + MarkChaptersCompletedActivityParams.getDefaultInstance() + ) + + val internalProfileId = args?.internalProfileId ?: -1 + val showConfirmationNotice = args?.showConfirmationNotice ?: false markChaptersCompletedActivityPresenter.handleOnCreate(internalProfileId, showConfirmationNotice) title = resourceHandler.getStringInLocale(R.string.mark_chapters_completed_activity_title) } @@ -40,9 +48,9 @@ class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity() } companion object { - private const val PROFILE_ID_EXTRA_KEY = "MarkChaptersCompletedActivity.profile_id" - private const val SHOW_CONFIRMATION_NOTICE_EXTRA_KEY = - "MarkChaptersCompletedActivity.show_confirmation_notice" + /** Params key for [MarkChaptersCompletedActivity]. */ + const val MARK_CHAPTERS_COMPLETED_ACTIVITY_PARAMS = + "MarkChaptersCompletedActivity.params" /** Returns an [Intent] to start this activity. */ fun createMarkChaptersCompletedIntent( @@ -50,11 +58,17 @@ class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity() internalProfileId: Int, showConfirmationNotice: Boolean ): Intent { - return Intent(context, MarkChaptersCompletedActivity::class.java).apply { - putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) - putExtra(SHOW_CONFIRMATION_NOTICE_EXTRA_KEY, showConfirmationNotice) - decorateWithScreenName(MARK_CHAPTERS_COMPLETED_ACTIVITY) + val intent = Intent(context, MarkChaptersCompletedActivity::class.java) + + val args = MarkChaptersCompletedActivityParams.newBuilder().apply { + this.internalProfileId = internalProfileId + this.showConfirmationNotice = showConfirmationNotice } + .build() + + intent.putProtoExtra(MARK_CHAPTERS_COMPLETED_ACTIVITY_PARAMS, args) + intent.decorateWithScreenName(MARK_CHAPTERS_COMPLETED_ACTIVITY) + return intent } } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragment.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragment.kt index 9ea2d5af880..fb34bf79cea 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragment.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragment.kt @@ -7,6 +7,10 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.MarkChaptersCompletedFragmentArguments +import org.oppia.android.app.model.MarkChaptersCompletedFragmentStateBundle +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject /** Fragment to display all chapters and provide functionality to mark them completed. */ @@ -15,25 +19,26 @@ class MarkChaptersCompletedFragment : InjectableFragment() { lateinit var markChaptersCompletedFragmentPresenter: MarkChaptersCompletedFragmentPresenter companion object { - private const val PROFILE_ID_ARGUMENT_KEY = "MarkChaptersCompletedFragment.profile_id" - private const val SHOW_CONFIRMATION_NOTICE_ARGUMENT_KEY = - "MarkChaptersCompletedFragment.show_confirmation_notice" - private const val EXPLORATION_ID_LIST_ARGUMENT_KEY = - "MarkChaptersCompletedFragment.exploration_id_list" - private const val EXPLORATION_TITLE_LIST_ARGUMENT_KEY = - "MarkChaptersCompletedFragment.exploration_title_list" + private const val MARK_CHAPTERS_COMPLETED_FRAGMENT_ARGUMENTS_KEY = + "MarkChaptersCompletedFragment.arguments" + private const val MARK_CHAPTERS_COMPLETED_FRAGMENT_STATE_KEY = + "MarkChaptersCompletedFragment.state" /** Returns a new [MarkChaptersCompletedFragment]. */ fun newInstance( internalProfileId: Int, showConfirmationNotice: Boolean ): MarkChaptersCompletedFragment { - val markChaptersCompletedFragment = MarkChaptersCompletedFragment() - val args = Bundle() - args.putInt(PROFILE_ID_ARGUMENT_KEY, internalProfileId) - args.putBoolean(SHOW_CONFIRMATION_NOTICE_ARGUMENT_KEY, showConfirmationNotice) - markChaptersCompletedFragment.arguments = args - return markChaptersCompletedFragment + val args = MarkChaptersCompletedFragmentArguments.newBuilder().apply { + this.internalProfileId = internalProfileId + this.showConfirmationNotice = showConfirmationNotice + } + .build() + return MarkChaptersCompletedFragment().apply { + arguments = Bundle().apply { + putProto(MARK_CHAPTERS_COMPLETED_FRAGMENT_ARGUMENTS_KEY, args) + } + } } } @@ -47,30 +52,43 @@ class MarkChaptersCompletedFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to MarkChaptersCompletedFragment" } - val internalProfileId = args.getInt(PROFILE_ID_ARGUMENT_KEY, /* defaultValue= */ -1) - val showConfirmationNotice = - args.getBoolean(SHOW_CONFIRMATION_NOTICE_ARGUMENT_KEY, /* defaultValue= */ false) + val args = arguments.getProto( + MARK_CHAPTERS_COMPLETED_FRAGMENT_ARGUMENTS_KEY, + MarkChaptersCompletedFragmentArguments.getDefaultInstance() + ) + val internalProfileId = args?.internalProfileId ?: -1 + val showConfirmationNotice = args?.showConfirmationNotice ?: false + + val savedStateArgs = savedInstanceState?.getProto( + MARK_CHAPTERS_COMPLETED_FRAGMENT_STATE_KEY, + MarkChaptersCompletedFragmentStateBundle.getDefaultInstance() + ) return markChaptersCompletedFragmentPresenter.handleCreateView( inflater, container, internalProfileId, showConfirmationNotice, - savedInstanceState?.getStringArrayList(EXPLORATION_ID_LIST_ARGUMENT_KEY) ?: listOf(), - savedInstanceState?.getStringArrayList(EXPLORATION_TITLE_LIST_ARGUMENT_KEY) ?: listOf() + savedStateArgs?.explorationIdsList ?: listOf(), + savedStateArgs?.explorationTitlesList ?: listOf() ) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putStringArrayList( - EXPLORATION_ID_LIST_ARGUMENT_KEY, - markChaptersCompletedFragmentPresenter.serializableSelectedExplorationIds - ) - outState.putStringArrayList( - EXPLORATION_TITLE_LIST_ARGUMENT_KEY, - markChaptersCompletedFragmentPresenter.serializableSelectedExplorationTitles - ) + + outState.apply { + val args = MarkChaptersCompletedFragmentStateBundle.newBuilder().apply { + addAllExplorationIds( + markChaptersCompletedFragmentPresenter.serializableSelectedExplorationIds + ) + addAllExplorationTitles( + markChaptersCompletedFragmentPresenter.serializableSelectedExplorationTitles + ) + } + .build() + putProto(MARK_CHAPTERS_COMPLETED_FRAGMENT_STATE_KEY, args) + } } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/testing/MarkChaptersCompletedTestActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/testing/MarkChaptersCompletedTestActivity.kt index e869beaf93d..cbddc6676f5 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/testing/MarkChaptersCompletedTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/testing/MarkChaptersCompletedTestActivity.kt @@ -7,6 +7,9 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableSystemLocalizedAppCompatActivity import org.oppia.android.app.devoptions.markchapterscompleted.MarkChaptersCompletedFragment +import org.oppia.android.app.model.MarkChaptersCompletedTestActivityParams +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra /** The activity for testing [MarkChaptersCompletedFragment]. */ class MarkChaptersCompletedTestActivity : InjectableSystemLocalizedAppCompatActivity() { @@ -16,9 +19,14 @@ class MarkChaptersCompletedTestActivity : InjectableSystemLocalizedAppCompatActi supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) setContentView(R.layout.mark_chapters_completed_activity) - val internalProfileId = intent.getIntExtra(PROFILE_ID_EXTRA_KEY, /* default= */ -1) - val showConfirmationNotice = - intent.getBooleanExtra(SHOW_CONFIRMATION_NOTICE_EXTRA_KEY, /* default= */ false) + + val args = intent.getProtoExtra( + MARK_CHAPTERS_COMPLETED_TEST_ACTIVITY_PARAMS_KEY, + MarkChaptersCompletedTestActivityParams.getDefaultInstance() + ) + + val internalProfileId = args?.internalProfileId ?: -1 + val showConfirmationNotice = args?.showConfirmationNotice ?: false if (getMarkChaptersCompletedFragment() == null) { val markChaptersCompletedFragment = MarkChaptersCompletedFragment.newInstance(internalProfileId, showConfirmationNotice) @@ -35,9 +43,9 @@ class MarkChaptersCompletedTestActivity : InjectableSystemLocalizedAppCompatActi } companion object { - private const val PROFILE_ID_EXTRA_KEY = "MarkChaptersCompletedTestActivity.profile_id" - private const val SHOW_CONFIRMATION_NOTICE_EXTRA_KEY = - "MarkChaptersCompletedTestActivity.show_confirmation_notice" + /** Params key for MarkChaptersCompletedTestActivity. */ + private const val MARK_CHAPTERS_COMPLETED_TEST_ACTIVITY_PARAMS_KEY = + "MarkChaptersCompletedTestActivity.params" /** Returns an [Intent] for [MarkChaptersCompletedTestActivity]. */ fun createMarkChaptersCompletedTestIntent( @@ -46,8 +54,14 @@ class MarkChaptersCompletedTestActivity : InjectableSystemLocalizedAppCompatActi showConfirmationNotice: Boolean ): Intent { val intent = Intent(context, MarkChaptersCompletedTestActivity::class.java) - intent.putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) - intent.putExtra(SHOW_CONFIRMATION_NOTICE_EXTRA_KEY, showConfirmationNotice) + + val args = MarkChaptersCompletedTestActivityParams.newBuilder().apply { + this.internalProfileId = internalProfileId + this.showConfirmationNotice = showConfirmationNotice + } + .build() + + intent.putProtoExtra(MARK_CHAPTERS_COMPLETED_TEST_ACTIVITY_PARAMS_KEY, args) return intent } } 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 5c26e1408a4..e3373fc7df6 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 @@ -7,9 +7,12 @@ import android.view.MenuItem import org.oppia.android.R 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.ScreenName.MARK_STORIES_COMPLETED_ACTIVITY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Activity for Mark Stories Completed. */ @@ -26,7 +29,9 @@ class MarkStoriesCompletedActivity : InjectableAutoLocalizedAppCompatActivity() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - internalProfileId = intent.getIntExtra(PROFILE_ID_EXTRA_KEY, -1) + + val profileId = intent?.extractCurrentUserProfileId() + internalProfileId = profileId?.internalId ?: -1 markStoriesCompletedActivityPresenter.handleOnCreate(internalProfileId) title = resourceHandler.getStringInLocale(R.string.mark_stories_completed_activity_title) } @@ -40,13 +45,12 @@ class MarkStoriesCompletedActivity : InjectableAutoLocalizedAppCompatActivity() } companion object { - /** [String] key value for mapping to InternalProfileId in [Bundle]. */ - const val PROFILE_ID_EXTRA_KEY = "MarkStoriesCompletedActivity.profile_id" /** Returns an [Intent] to start this activity. */ fun createMarkStoriesCompletedIntent(context: Context, internalProfileId: Int): Intent { + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() return Intent(context, MarkStoriesCompletedActivity::class.java).apply { - putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) + decorateWithUserProfileId(profileId) decorateWithScreenName(MARK_STORIES_COMPLETED_ACTIVITY) } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragment.kt b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragment.kt index e87c481af15..460c6e1dd7b 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragment.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragment.kt @@ -7,6 +7,12 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.MarkStoriesCompletedFragmentStateBundle +import org.oppia.android.app.model.ProfileId +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Fragment to display all stories and provide functionality to mark them completed. */ @@ -15,17 +21,19 @@ class MarkStoriesCompletedFragment : InjectableFragment() { lateinit var markStoriesCompletedFragmentPresenter: MarkStoriesCompletedFragmentPresenter companion object { - internal const val PROFILE_ID_ARGUMENT_KEY = "MarkStoriesCompletedFragment.profile_id" - private const val STORY_ID_LIST_ARGUMENT_KEY = "MarkStoriesCompletedFragment.story_id_list" + const val MARK_STORIES_COMPLETED_FRAGMENT_STATE_KEY = "MarkStoriesCompletedFragment.state" + + internal const val PROFILE_ID_ARGUMENT_KEY = "MarkStoriesCompletedFragment.profile_id" /** Returns a new [MarkStoriesCompletedFragment]. */ fun newInstance(internalProfileId: Int): MarkStoriesCompletedFragment { - val markStoriesCompletedFragment = MarkStoriesCompletedFragment() - val args = Bundle() - args.putInt(PROFILE_ID_ARGUMENT_KEY, internalProfileId) - markStoriesCompletedFragment.arguments = args - return markStoriesCompletedFragment + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + return MarkStoriesCompletedFragment().apply { + arguments = Bundle().apply { + decorateWithUserProfileId(profileId) + } + } } } @@ -39,13 +47,20 @@ class MarkStoriesCompletedFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to MarkStoriesCompletedFragment" } - val internalProfileId = args - .getInt(PROFILE_ID_ARGUMENT_KEY, -1) + + val profileId = arguments.extractCurrentUserProfileId() + val internalProfileId = profileId.internalId + var selectedStoryIdList = ArrayList() if (savedInstanceState != null) { - selectedStoryIdList = savedInstanceState.getStringArrayList(STORY_ID_LIST_ARGUMENT_KEY)!! + + val stateArgs = savedInstanceState.getProto( + MARK_STORIES_COMPLETED_FRAGMENT_STATE_KEY, + MarkStoriesCompletedFragmentStateBundle.getDefaultInstance() + ) + selectedStoryIdList = stateArgs?.storyIdsList?.let { ArrayList(it) } ?: ArrayList() } return markStoriesCompletedFragmentPresenter.handleCreateView( inflater, @@ -57,9 +72,12 @@ class MarkStoriesCompletedFragment : InjectableFragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putStringArrayList( - STORY_ID_LIST_ARGUMENT_KEY, - markStoriesCompletedFragmentPresenter.selectedStoryIdList - ) + val args = MarkStoriesCompletedFragmentStateBundle.newBuilder().apply { + addAllStoryIds(markStoriesCompletedFragmentPresenter.selectedStoryIdList) + } + .build() + outState.apply { + putProto(MARK_STORIES_COMPLETED_FRAGMENT_STATE_KEY, args) + } } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/testing/MarkStoriesCompletedTestActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/testing/MarkStoriesCompletedTestActivity.kt index 2217450ebff..f128ade5049 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/testing/MarkStoriesCompletedTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/testing/MarkStoriesCompletedTestActivity.kt @@ -7,6 +7,9 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.devoptions.markstoriescompleted.MarkStoriesCompletedFragment +import org.oppia.android.app.model.ProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId /** Activity for testing [MarkStoriesCompletedFragment]. */ class MarkStoriesCompletedTestActivity : InjectableAutoLocalizedAppCompatActivity() { @@ -19,7 +22,10 @@ class MarkStoriesCompletedTestActivity : InjectableAutoLocalizedAppCompatActivit supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) setContentView(R.layout.mark_stories_completed_activity) - internalProfileId = intent.getIntExtra(PROFILE_ID_EXTRA_KEY, -1) + + val profileId = intent?.extractCurrentUserProfileId() + internalProfileId = profileId?.internalId ?: -1 + if (getMarkStoriesCompletedFragment() == null) { val markStoriesCompletedFragment = MarkStoriesCompletedFragment.newInstance(internalProfileId) supportFragmentManager.beginTransaction().add( @@ -35,13 +41,12 @@ class MarkStoriesCompletedTestActivity : InjectableAutoLocalizedAppCompatActivit } companion object { - /** [String] key value for mapping to InternalProfileId in [Bundle]. */ - const val PROFILE_ID_EXTRA_KEY = "MarkStoriesCompletedTestActivity.profile_id" /** Returns an [Intent] for [MarkStoriesCompletedTestActivity]. */ fun createMarkStoriesCompletedTestIntent(context: Context, internalProfileId: Int): Intent { + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() val intent = Intent(context, MarkStoriesCompletedTestActivity::class.java) - intent.putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) + intent.decorateWithUserProfileId(profileId) return intent } } 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 6801d2fcea0..a891de3a059 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 @@ -8,9 +8,12 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.devoptions.markstoriescompleted.testing.MarkStoriesCompletedTestActivity +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ScreenName.MARK_TOPICS_COMPLETED_ACTIVITY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Activity for Mark Topics Completed. */ @@ -27,7 +30,8 @@ class MarkTopicsCompletedActivity : InjectableAutoLocalizedAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - internalProfileId = intent.getIntExtra(PROFILE_ID_EXTRA_KEY, -1) + val profileId = intent?.extractCurrentUserProfileId() + internalProfileId = profileId?.internalId ?: -1 markTopicsCompletedActivityPresenter.handleOnCreate(internalProfileId) title = resourceHandler.getStringInLocale(R.string.mark_topics_completed_activity_title) } @@ -41,13 +45,11 @@ class MarkTopicsCompletedActivity : InjectableAutoLocalizedAppCompatActivity() { } companion object { - /** [String] key value for mapping to InternalProfileId in [Bundle]. */ - const val PROFILE_ID_EXTRA_KEY = "MarkTopicsCompletedActivity.profile_id" - /** Returns an [Intent] for [MarkStoriesCompletedTestActivity]. */ fun createMarkTopicsCompletedIntent(context: Context, internalProfileId: Int): Intent { + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() return Intent(context, MarkTopicsCompletedActivity::class.java).apply { - putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) + decorateWithUserProfileId(profileId) decorateWithScreenName(MARK_TOPICS_COMPLETED_ACTIVITY) } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragment.kt b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragment.kt index 39581587eb6..1b5f6dc5543 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragment.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragment.kt @@ -7,6 +7,12 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.MarkTopicsCompletedFragmentStateBundle +import org.oppia.android.app.model.ProfileId +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Fragment to display all topics and provide functionality to mark them completed. */ @@ -15,17 +21,19 @@ class MarkTopicsCompletedFragment : InjectableFragment() { lateinit var markTopicsCompletedFragmentPresenter: MarkTopicsCompletedFragmentPresenter companion object { - internal const val PROFILE_ID_ARGUMENT_KEY = "MarkTopicsCompletedFragment.profile_id" - private const val TOPIC_ID_LIST_ARGUMENT_KEY = "MarkTopicsCompletedFragment.topic_id_list" + /** State key for MarkTopicsCompletedFragment.. */ + const val MARK_TOPICS_COMPLETED_FRAGMENT_STATE_KEY = + "MarkTopicsCompletedFragment.state" /** Returns a new [MarkTopicsCompletedFragment]. */ fun newInstance(internalProfileId: Int): MarkTopicsCompletedFragment { - val markTopicsCompletedFragment = MarkTopicsCompletedFragment() - val args = Bundle() - args.putInt(PROFILE_ID_ARGUMENT_KEY, internalProfileId) - markTopicsCompletedFragment.arguments = args - return markTopicsCompletedFragment + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + return MarkTopicsCompletedFragment().apply { + arguments = Bundle().apply { + decorateWithUserProfileId(profileId) + } + } } } @@ -39,13 +47,19 @@ class MarkTopicsCompletedFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to MarkTopicsCompletedFragment" } - val internalProfileId = args - .getInt(PROFILE_ID_ARGUMENT_KEY, -1) + + val internalProfileId = arguments.extractCurrentUserProfileId().internalId + var selectedTopicIdList = ArrayList() if (savedInstanceState != null) { - selectedTopicIdList = savedInstanceState.getStringArrayList(TOPIC_ID_LIST_ARGUMENT_KEY)!! + + val stateArgs = savedInstanceState.getProto( + MARK_TOPICS_COMPLETED_FRAGMENT_STATE_KEY, + MarkTopicsCompletedFragmentStateBundle.getDefaultInstance() + ) + selectedTopicIdList = stateArgs?.topicIdsList?.let { ArrayList(it) } ?: ArrayList() } return markTopicsCompletedFragmentPresenter.handleCreateView( inflater, @@ -57,9 +71,11 @@ class MarkTopicsCompletedFragment : InjectableFragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putStringArrayList( - TOPIC_ID_LIST_ARGUMENT_KEY, - markTopicsCompletedFragmentPresenter.selectedTopicIdList - ) + val args = MarkTopicsCompletedFragmentStateBundle.newBuilder().apply { + addAllTopicIds(markTopicsCompletedFragmentPresenter.selectedTopicIdList) + }.build() + outState.apply { + putProto(MARK_TOPICS_COMPLETED_FRAGMENT_STATE_KEY, args) + } } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/testing/MarkTopicsCompletedTestActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/testing/MarkTopicsCompletedTestActivity.kt index a33ad677ace..5e6242271be 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/testing/MarkTopicsCompletedTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/testing/MarkTopicsCompletedTestActivity.kt @@ -7,6 +7,9 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.devoptions.marktopicscompleted.MarkTopicsCompletedFragment +import org.oppia.android.app.model.ProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId /** The activity for testing [MarkTopicsCompletedFragment]. */ class MarkTopicsCompletedTestActivity : InjectableAutoLocalizedAppCompatActivity() { @@ -19,7 +22,8 @@ class MarkTopicsCompletedTestActivity : InjectableAutoLocalizedAppCompatActivity supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) setContentView(R.layout.mark_topics_completed_activity) - internalProfileId = intent.getIntExtra(PROFILE_ID_EXTRA_KEY, -1) + val profileId = intent?.extractCurrentUserProfileId() + internalProfileId = profileId?.internalId ?: -1 if (getMarkTopicsCompletedFragment() == null) { val markTopicsCompletedFragment = MarkTopicsCompletedFragment.newInstance(internalProfileId) supportFragmentManager.beginTransaction().add( @@ -35,12 +39,12 @@ class MarkTopicsCompletedTestActivity : InjectableAutoLocalizedAppCompatActivity } companion object { - const val PROFILE_ID_EXTRA_KEY = "MarkTopicsCompletedTestActivity.profile_id" - /** Returns an [Intent] for [MarkTopicsCompletedTestActivity]. */ fun createMarkTopicsCompletedTestIntent(context: Context, internalProfileId: Int): Intent { - val intent = Intent(context, MarkTopicsCompletedTestActivity::class.java) - intent.putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + val intent = Intent(context, MarkTopicsCompletedTestActivity::class.java).apply { + decorateWithUserProfileId(profileId) + } return intent } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/testing/DeveloperOptionsTestActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/testing/DeveloperOptionsTestActivity.kt index 69ea6cb1889..7e1565c276d 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/testing/DeveloperOptionsTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/testing/DeveloperOptionsTestActivity.kt @@ -17,6 +17,9 @@ import org.oppia.android.app.devoptions.markchapterscompleted.MarkChaptersComple import org.oppia.android.app.devoptions.markstoriescompleted.MarkStoriesCompletedActivity import org.oppia.android.app.devoptions.marktopicscompleted.MarkTopicsCompletedActivity import org.oppia.android.app.devoptions.vieweventlogs.ViewEventLogsActivity +import org.oppia.android.app.model.ProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId /** Activity for testing [DeveloperOptionsFragment]. */ class DeveloperOptionsTestActivity : @@ -33,7 +36,10 @@ class DeveloperOptionsTestActivity : super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) setContentView(R.layout.developer_options_activity) - internalProfileId = intent.getIntExtra(PROFILE_ID_EXTRA_KEY, -1) + + val profileId = intent?.extractCurrentUserProfileId() + internalProfileId = profileId?.internalId ?: -1 + if (getDeveloperOptionsFragment() == null) { supportFragmentManager.beginTransaction().add( R.id.developer_options_fragment_placeholder, @@ -79,12 +85,11 @@ class DeveloperOptionsTestActivity : } companion object { - const val PROFILE_ID_EXTRA_KEY = "DeveloperOptionsTestActivity.profile_id" - /** Returns [Intent] for [DeveloperOptionsTestActivity]. */ fun createDeveloperOptionsTestIntent(context: Context, internalProfileId: Int): Intent { + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() val intent = Intent(context, DeveloperOptionsActivity::class.java) - intent.putExtra(PROFILE_ID_EXTRA_KEY, internalProfileId) + intent.decorateWithUserProfileId(profileId) return intent } } diff --git a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt index f76c4cf919e..a8fcd95253f 100644 --- a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt @@ -37,11 +37,10 @@ import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.topic.TopicController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import org.oppia.android.util.statusbar.StatusBarColor import javax.inject.Inject -const val NAVIGATION_PROFILE_ID_ARGUMENT_KEY = - "NavigationDrawerFragmentPresenter.navigation_profile_id" const val TAG_SWITCH_PROFILE_DIALOG = "SWITCH_PROFILE_DIALOG" /** The presenter for [NavigationDrawerFragment]. */ @@ -69,8 +68,8 @@ class NavigationDrawerFragmentPresenter @Inject constructor( fragment.setHasOptionsMenu(true) - internalProfileId = activity.intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) - profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + profileId = activity.intent.extractCurrentUserProfileId() + internalProfileId = profileId.internalId val headerBinding = NavHeaderNavigationDrawerBinding.inflate( @@ -105,7 +104,7 @@ class NavigationDrawerFragmentPresenter @Inject constructor( uncheckAllMenuItemsWhenAdministratorControlsOrDeveloperOptionsIsSelected() drawerLayout.closeDrawers() footerViewModel.isDeveloperOptionsSelected.set(true) - val intent = starter.createIntent(activity, internalProfileId) + val intent = starter.createIntent(activity, profileId) fragment.activity!!.startActivity(intent) if (previousMenuItemId == 0) fragment.activity!!.finish() else if (previousMenuItemId != null && @@ -143,7 +142,7 @@ class NavigationDrawerFragmentPresenter @Inject constructor( val intent = AdministratorControlsActivity.createAdministratorControlsActivityIntent( activity, - internalProfileId + profileId ) fragment.activity!!.startActivity(intent) if (previousMenuItemId == -1) fragment.activity!!.finish() @@ -233,13 +232,13 @@ class NavigationDrawerFragmentPresenter @Inject constructor( if (previousMenuItemId != menuItemId) { when (NavigationDrawerItem.valueFromNavId(menuItemId)) { NavigationDrawerItem.HOME -> { - val intent = HomeActivity.createHomeActivity(activity, internalProfileId) + val intent = HomeActivity.createHomeActivity(activity, profileId) fragment.activity!!.startActivity(intent) drawerLayout.closeDrawers() } NavigationDrawerItem.OPTIONS -> { val intent = OptionsActivity.createOptionsActivity( - activity, internalProfileId, + activity, profileId, /* isFromNavigationDrawer= */ true ) fragment.activity!!.startActivity(intent) @@ -250,7 +249,7 @@ class NavigationDrawerFragmentPresenter @Inject constructor( } NavigationDrawerItem.HELP -> { val intent = HelpActivity.createHelpActivityIntent( - activity, internalProfileId, + activity, profileId, /* isFromNavigationDrawer= */ true ) fragment.activity!!.startActivity(intent) diff --git a/app/src/main/java/org/oppia/android/app/help/HelpActivity.kt b/app/src/main/java/org/oppia/android/app/help/HelpActivity.kt index 8d1182d5105..bf559ef1d70 100644 --- a/app/src/main/java/org/oppia/android/app/help/HelpActivity.kt +++ b/app/src/main/java/org/oppia/android/app/help/HelpActivity.kt @@ -6,29 +6,26 @@ import android.os.Bundle import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.help.faq.FAQListActivity import org.oppia.android.app.help.faq.RouteToFAQSingleListener import org.oppia.android.app.help.faq.faqsingle.FAQSingleActivity import org.oppia.android.app.help.thirdparty.ThirdPartyDependencyListActivity -import org.oppia.android.app.model.PoliciesActivityParams +import org.oppia.android.app.model.HelpActivityParams +import org.oppia.android.app.model.HelpActivityStateBundle import org.oppia.android.app.model.PolicyPage +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ScreenName.HELP_ACTIVITY import org.oppia.android.app.policies.PoliciesActivity import org.oppia.android.app.policies.RouteToPoliciesListener import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.util.extensions.getProto -import org.oppia.android.util.extensions.getStringFromBundle +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId import javax.inject.Inject -const val HELP_OPTIONS_TITLE_SAVED_KEY = "HelpActivity.help_options_title" -const val SELECTED_FRAGMENT_SAVED_KEY = "HelpActivity.selected_fragment" -const val THIRD_PARTY_DEPENDENCY_INDEX_SAVED_KEY = - "HelpActivity.third_party_dependency_index" -const val LICENSE_INDEX_SAVED_KEY = "HelpActivity.license_index" const val FAQ_LIST_FRAGMENT_TAG = "FAQListFragment.tag" -const val POLICIES_ARGUMENT_PROTO = "PoliciesActivity.policy_page" const val POLICIES_FRAGMENT_TAG = "PoliciesFragment.tag" const val THIRD_PARTY_DEPENDENCY_LIST_FRAGMENT_TAG = "ThirdPartyDependencyListFragment.tag" const val LICENSE_LIST_FRAGMENT_TAG = "LicenseListFragment.tag" @@ -59,21 +56,26 @@ class HelpActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val isFromNavigationDrawer = intent.getBooleanExtra( - BOOL_IS_FROM_NAVIGATION_DRAWER_EXTRA_KEY, - /* defaultValue= */ false + val args = + intent.getProtoExtra(HELP_ACTIVITY_PARAMS_KEY, HelpActivityParams.getDefaultInstance()) + val isFromNavigationDrawer = args?.isFromNavigationDrawer ?: false + + val stateArgs = savedInstanceState?.getProto( + HELP_ACTIVITY_STATE_KEY, + HelpActivityStateBundle.getDefaultInstance() ) + selectedFragment = - savedInstanceState?.getStringFromBundle(SELECTED_FRAGMENT_SAVED_KEY) ?: FAQ_LIST_FRAGMENT_TAG + stateArgs?.selectedFragmentTag ?: FAQ_LIST_FRAGMENT_TAG val selectedDependencyIndex = - savedInstanceState?.getInt(THIRD_PARTY_DEPENDENCY_INDEX_SAVED_KEY) ?: 0 - val selectedLicenseIndex = savedInstanceState?.getInt(LICENSE_INDEX_SAVED_KEY) ?: 0 - selectedHelpOptionsTitle = savedInstanceState?.getStringFromBundle(HELP_OPTIONS_TITLE_SAVED_KEY) + stateArgs?.selectedDependencyIndex ?: 0 + val selectedLicenseIndex = stateArgs?.selectedLicenseIndex ?: 0 + + selectedHelpOptionsTitle = stateArgs?.helpOptionsTitle ?: resourceHandler.getStringInLocale(R.string.faq_activity_title) - val policiesActivityParams = savedInstanceState?.getProto( - POLICIES_ARGUMENT_PROTO, - PoliciesActivityParams.getDefaultInstance() - ) + + val policiesActivityParams = stateArgs?.policiesActivityParams + helpActivityPresenter.handleOnCreate( selectedHelpOptionsTitle, isFromNavigationDrawer, @@ -86,19 +88,28 @@ class HelpActivity : } companion object { - // TODO(#1655): Re-restrict access to fields in tests post-Gradle. - const val BOOL_IS_FROM_NAVIGATION_DRAWER_EXTRA_KEY = - "HelpActivity.bool_is_from_navigation_drawer" + /** Params key for HelpActivity. */ + const val HELP_ACTIVITY_PARAMS_KEY = + "HelpActivity.params" + + /** Arguments key for HelpActivity saved state. */ + const val HELP_ACTIVITY_STATE_KEY = + "HelpActivity.state" fun createHelpActivityIntent( context: Context, - profileId: Int?, + profileId: ProfileId?, isFromNavigationDrawer: Boolean ): Intent { + val args = HelpActivityParams.newBuilder().apply { + this.isFromNavigationDrawer = isFromNavigationDrawer + }.build() val intent = Intent(context, HelpActivity::class.java) - intent.putExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, profileId) - intent.putExtra(BOOL_IS_FROM_NAVIGATION_DRAWER_EXTRA_KEY, isFromNavigationDrawer) + intent.putProtoExtra(HELP_ACTIVITY_PARAMS_KEY, args) intent.decorateWithScreenName(HELP_ACTIVITY) + if (profileId != null) { + intent.decorateWithUserProfileId(profileId) + } return intent } } diff --git a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt index 29f2aa8123b..403012f7281 100644 --- a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt @@ -12,10 +12,12 @@ import androidx.fragment.app.Fragment import org.oppia.android.R import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.drawer.NavigationDrawerFragment +import org.oppia.android.app.help.HelpActivity.Companion.HELP_ACTIVITY_STATE_KEY import org.oppia.android.app.help.faq.FAQListFragment import org.oppia.android.app.help.thirdparty.LicenseListFragment import org.oppia.android.app.help.thirdparty.LicenseTextViewerFragment import org.oppia.android.app.help.thirdparty.ThirdPartyDependencyListFragment +import org.oppia.android.app.model.HelpActivityStateBundle import org.oppia.android.app.model.PoliciesActivityParams import org.oppia.android.app.model.PoliciesFragmentArguments import org.oppia.android.app.model.PolicyPage @@ -146,18 +148,22 @@ class HelpActivityPresenter @Inject constructor( /** Handles onSavedInstanceState() method for [HelpActivity]. */ fun handleOnSavedInstanceState(outState: Bundle) { val titleTextView = activity.findViewById(R.id.help_multipane_options_title_textview) - if (titleTextView != null) { - outState.putString(HELP_OPTIONS_TITLE_SAVED_KEY, titleTextView.text.toString()) - } - outState.putString(SELECTED_FRAGMENT_SAVED_KEY, selectedFragmentTag) - selectedDependencyIndex?.let { outState.putInt(THIRD_PARTY_DEPENDENCY_INDEX_SAVED_KEY, it) } - selectedLicenseIndex?.let { outState.putInt(LICENSE_INDEX_SAVED_KEY, it) } + val policiesActivityParams = PoliciesActivityParams .newBuilder() .setPolicyPage(internalPolicyPage) .build() - outState.putProto(POLICIES_ARGUMENT_PROTO, policiesActivityParams) + val args = HelpActivityStateBundle.newBuilder().apply { + if (titleTextView != null) { + this.helpOptionsTitle = titleTextView.text.toString() + this.selectedFragmentTag = this@HelpActivityPresenter.selectedFragmentTag + this.selectedDependencyIndex = this@HelpActivityPresenter.selectedDependencyIndex!! + this.selectedLicenseIndex = this@HelpActivityPresenter.selectedLicenseIndex!! + this.policiesActivityParams = policiesActivityParams + } + }.build() + outState.putProto(HELP_ACTIVITY_STATE_KEY, args) } private fun setUpToolbar() { @@ -342,7 +348,7 @@ class HelpActivityPresenter @Inject constructor( PolicyPage.TERMS_OF_SERVICE -> setMultipaneContainerTitle( resourceHandler.getStringInLocale(R.string.terms_of_service_title) ) - else -> { } + else -> {} } setMultipaneBackButtonVisibility(View.GONE) selectedFragmentTag = POLICIES_FRAGMENT_TAG diff --git a/app/src/main/java/org/oppia/android/app/help/HelpFragment.kt b/app/src/main/java/org/oppia/android/app/help/HelpFragment.kt index a468179fe45..3703b518f60 100644 --- a/app/src/main/java/org/oppia/android/app/help/HelpFragment.kt +++ b/app/src/main/java/org/oppia/android/app/help/HelpFragment.kt @@ -7,23 +7,26 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.HelpFragmentArguments +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject -private const val IS_MULTIPANE_KEY = "HelpFragment.bool_is_multipane" - /** Fragment that contains help in the app. */ class HelpFragment : InjectableFragment() { @Inject lateinit var helpFragmentPresenter: HelpFragmentPresenter companion object { + private const val HELP_FRAGMENT_ARGUMENTS_KEY = + "HelpFragment.arguments" + /** Returns instance of [HelpFragment]. */ fun newInstance(isMultipane: Boolean): HelpFragment { - val args = Bundle() - args.putBoolean(IS_MULTIPANE_KEY, isMultipane) - val fragment = HelpFragment() - fragment.arguments = args - return fragment + val args = HelpFragmentArguments.newBuilder().setIsMultipane(isMultipane).build() + return HelpFragment().apply { + arguments = Bundle().apply { putProto(HELP_FRAGMENT_ARGUMENTS_KEY, args) } + } } } @@ -37,10 +40,12 @@ class HelpFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = checkNotNull(arguments) { + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to HelpFragment" } - val isMultipane = args.getBoolean(IS_MULTIPANE_KEY) + val args = + arguments.getProto(HELP_FRAGMENT_ARGUMENTS_KEY, HelpFragmentArguments.getDefaultInstance()) + val isMultipane = args.isMultipane return helpFragmentPresenter.handleCreateView(inflater, container, isMultipane) } } diff --git a/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivity.kt b/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivity.kt index 9b6a10339a9..1f92f3b59f0 100644 --- a/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivity.kt +++ b/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivity.kt @@ -5,7 +5,10 @@ import android.content.Intent import android.os.Bundle import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity +import org.oppia.android.app.model.FAQSingleActivityParams import org.oppia.android.app.model.ScreenName.FAQ_SINGLE_ACTIVITY +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName import javax.inject.Inject @@ -17,11 +20,17 @@ class FAQSingleActivity : InjectableAutoLocalizedAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + (activityComponent as ActivityComponentImpl).inject(this) - val question = checkNotNull(intent.getStringExtra(FAQ_SINGLE_ACTIVITY_QUESTION)) { + val args = intent.getProtoExtra( + FAQ_SINGLE_ACTIVITY_PARAMS_KEY, + FAQSingleActivityParams.getDefaultInstance() + ) + + val question = checkNotNull(args?.question) { "Expected $FAQ_SINGLE_ACTIVITY_QUESTION to be in intent extras." } - val answer = checkNotNull(intent.getStringExtra(FAQ_SINGLE_ACTIVITY_ANSWER)) { + val answer = checkNotNull(args?.answer) { "Expected $FAQ_SINGLE_ACTIVITY_ANSWER to be in intent extras." } faqSingleActivityPresenter.handleOnCreate(question, answer) @@ -32,11 +41,19 @@ class FAQSingleActivity : InjectableAutoLocalizedAppCompatActivity() { const val FAQ_SINGLE_ACTIVITY_QUESTION = "FAQSingleActivity.question" const val FAQ_SINGLE_ACTIVITY_ANSWER = "FAQSingleActivity.answer" + /** Params key for FAQSingleActivity. */ + const val FAQ_SINGLE_ACTIVITY_PARAMS_KEY = "FAQSingleActivity.params" + fun createFAQSingleActivityIntent(context: Context, question: String, answer: String): Intent { - val intent = Intent(context, FAQSingleActivity::class.java) - intent.putExtra(FAQ_SINGLE_ACTIVITY_QUESTION, question) - intent.putExtra(FAQ_SINGLE_ACTIVITY_ANSWER, answer) - intent.decorateWithScreenName(FAQ_SINGLE_ACTIVITY) + + val intent = Intent(context, FAQSingleActivity::class.java).apply { + val args = FAQSingleActivityParams.newBuilder().apply { + this.question = question + this.answer = answer + }.build() + putProtoExtra(FAQ_SINGLE_ACTIVITY_PARAMS_KEY, args) + decorateWithScreenName(FAQ_SINGLE_ACTIVITY) + } return intent } } diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivity.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivity.kt index 84301c83952..b1f733ffa0b 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivity.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivity.kt @@ -5,7 +5,10 @@ import android.content.Intent import android.os.Bundle import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity +import org.oppia.android.app.model.LicenseListActivityParams import org.oppia.android.app.model.ScreenName.LICENSE_LIST_ACTIVITY +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName import javax.inject.Inject @@ -18,22 +21,29 @@ class LicenseListActivity : InjectableAutoLocalizedAppCompatActivity(), RouteToL override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val dependencyIndex = intent.getIntExtra(THIRD_PARTY_DEPENDENCY_INDEX, 0) + val args = intent.getProtoExtra( + LICENSE_LIST_ACTIVITY_PARAMS_KEY, + LicenseListActivityParams.getDefaultInstance() + ) + val dependencyIndex = args?.dependencyIndex ?: 0 licenseListActivityPresenter.handleOnCreate(dependencyIndex, false) } companion object { - private const val THIRD_PARTY_DEPENDENCY_INDEX = "LicenseListActivity.dependency_index" + /** Params key for LicenseListActivity. */ + private const val LICENSE_LIST_ACTIVITY_PARAMS_KEY = "LicenseListActivity.params" /** Returns [Intent] for [LicenseListActivity]. */ fun createLicenseListActivityIntent( context: Context, dependencyIndex: Int ): Intent { - val intent = Intent(context, LicenseListActivity::class.java) - intent.putExtra(THIRD_PARTY_DEPENDENCY_INDEX, dependencyIndex) - intent.decorateWithScreenName(LICENSE_LIST_ACTIVITY) - return intent + val args = + LicenseListActivityParams.newBuilder().setDependencyIndex(dependencyIndex).build() + return Intent(context, LicenseListActivity::class.java).apply { + putProtoExtra(LICENSE_LIST_ACTIVITY_PARAMS_KEY, args) + decorateWithScreenName(LICENSE_LIST_ACTIVITY) + } } } diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListFragment.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListFragment.kt index 6e45c57a7c4..ba95a6962fc 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListFragment.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListFragment.kt @@ -7,6 +7,9 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.LicenseListFragmentArguments +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject /** Fragment that contains list of licenses for a third-party dependency in the app. */ @@ -15,18 +18,21 @@ class LicenseListFragment : InjectableFragment() { lateinit var licenseListFragmentPresenter: LicenseListFragmentPresenter companion object { - private const val LICENSE_LIST_FRAGMENT_DEPENDENCY_INDEX = - "LicenseListFragment.dependency_index" - private const val IS_MULTIPANE_KEY = "LicenseListFragment.is_multipane" + /** Argument key for LicenseListFragment. */ + private const val LICENSE_LIST_FRAGMENT_ARGUMENTS_KEY = "LicenseListFragment.arguments" /** Returns an instance of [LicenseListFragment]. */ fun newInstance(dependencyIndex: Int, isMultipane: Boolean): LicenseListFragment { - val fragment = LicenseListFragment() - val args = Bundle() - args.putInt(LICENSE_LIST_FRAGMENT_DEPENDENCY_INDEX, dependencyIndex) - args.putBoolean(IS_MULTIPANE_KEY, isMultipane) - fragment.arguments = args - return fragment + val args = LicenseListFragmentArguments.newBuilder().apply { + this.dependencyIndex = dependencyIndex + this.isMultipane = isMultipane + }.build() + return LicenseListFragment().apply { + val bundle = Bundle().apply { + putProto(LICENSE_LIST_FRAGMENT_ARGUMENTS_KEY, args) + } + arguments = bundle + } } } @@ -40,11 +46,15 @@ class LicenseListFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = checkNotNull(arguments) { + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to LicenseListFragment" } - val dependencyIndex = args.getInt(LICENSE_LIST_FRAGMENT_DEPENDENCY_INDEX) - val isMultipane = args.getBoolean(IS_MULTIPANE_KEY) + val args = arguments.getProto( + LICENSE_LIST_FRAGMENT_ARGUMENTS_KEY, + LicenseListFragmentArguments.getDefaultInstance() + ) + val dependencyIndex = args.dependencyIndex + val isMultipane = args.isMultipane return licenseListFragmentPresenter.handleCreateView( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivity.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivity.kt index 4cde507fa3b..6c93b4ca4f9 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivity.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivity.kt @@ -5,7 +5,10 @@ import android.content.Intent import android.os.Bundle import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity +import org.oppia.android.app.model.LicenseTextViewerActivityParams import org.oppia.android.app.model.ScreenName.LICENSE_TEXT_VIEWER_ACTIVITY +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName import javax.inject.Inject @@ -18,16 +21,19 @@ class LicenseTextViewerActivity : InjectableAutoLocalizedAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val dependencyIndex = intent.getIntExtra(LICENSE_TEXT_VIEWER_ACTIVITY_DEP_INDEX, 0) - val licenseIndex = intent.getIntExtra(LICENSE_TEXT_VIEWER_ACTIVITY_LICENSE_INDEX, 0) + val args = intent.getProtoExtra( + LICENSE_TEXT_VIEWER_ACTIVITY_PARAMS_KEY, + LicenseTextViewerActivityParams.getDefaultInstance() + ) + val dependencyIndex = args?.dependencyIndex ?: 0 + val licenseIndex = args?.licenseIndex ?: 0 licenseTextViewerActivityPresenter.handleOnCreate(dependencyIndex, licenseIndex) } companion object { - private const val LICENSE_TEXT_VIEWER_ACTIVITY_DEP_INDEX = - "LicenseTextViewerActivity.dependency_index" - private const val LICENSE_TEXT_VIEWER_ACTIVITY_LICENSE_INDEX = - "LicenseTextViewerActivity.license_index" + /** Params key for LicenseTextViewerActivity. */ + private const val LICENSE_TEXT_VIEWER_ACTIVITY_PARAMS_KEY = + "LicenseTextViewerActivity.params" /** Returns [Intent] for [LicenseTextViewerActivity]. */ fun createLicenseTextViewerActivityIntent( @@ -35,11 +41,14 @@ class LicenseTextViewerActivity : InjectableAutoLocalizedAppCompatActivity() { dependencyIndex: Int, licenseIndex: Int ): Intent { - val intent = Intent(context, LicenseTextViewerActivity::class.java) - intent.putExtra(LICENSE_TEXT_VIEWER_ACTIVITY_DEP_INDEX, dependencyIndex) - intent.putExtra(LICENSE_TEXT_VIEWER_ACTIVITY_LICENSE_INDEX, licenseIndex) - intent.decorateWithScreenName(LICENSE_TEXT_VIEWER_ACTIVITY) - return intent + val args = LicenseTextViewerActivityParams.newBuilder().apply { + this.dependencyIndex = dependencyIndex + this.licenseIndex = licenseIndex + }.build() + return Intent(context, LicenseTextViewerActivity::class.java).apply { + putProtoExtra(LICENSE_TEXT_VIEWER_ACTIVITY_PARAMS_KEY, args) + decorateWithScreenName(LICENSE_TEXT_VIEWER_ACTIVITY) + } } } } diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerFragment.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerFragment.kt index 9abe45062b0..64d3585b5a5 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerFragment.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerFragment.kt @@ -7,6 +7,9 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.LicenseTextViewerFragmentArguments +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject /** Fragment that displays text of a copyright license of a third-party dependency. */ @@ -15,19 +18,22 @@ class LicenseTextViewerFragment : InjectableFragment() { lateinit var licenseTextViewerFragmentPresenter: LicenseTextViewerFragmentPresenter companion object { - private const val LICENSE_TEXT_VIEWER_FRAGMENT_DEPENDENCY_INDEX = - "LicenseTextViewerFragment.dependency_index" - private const val LICENSE_TEXT_VIEWER_FRAGMENT_LICENSE_INDEX = - "LicenseTextViewerFragment.license_index" + /** Argument key for LicenseTextViewerFragment. */ + private const val LICENSE_TEXT_VIEWER_FRAGMENT_ARGUMENTS_KEY = + "LicenseTextViewerFragment.arguments" /** Returns an instance of [LicenseTextViewerFragment]. */ fun newInstance(dependencyIndex: Int, licenseIndex: Int): LicenseTextViewerFragment { - val fragment = LicenseTextViewerFragment() - val args = Bundle() - args.putInt(LICENSE_TEXT_VIEWER_FRAGMENT_DEPENDENCY_INDEX, dependencyIndex) - args.putInt(LICENSE_TEXT_VIEWER_FRAGMENT_LICENSE_INDEX, licenseIndex) - fragment.arguments = args - return fragment + val args = LicenseTextViewerFragmentArguments.newBuilder().apply { + this.dependencyIndex = dependencyIndex + this.licenseIndex = licenseIndex + }.build() + return LicenseTextViewerFragment().apply { + val bundle = Bundle().apply { + putProto(LICENSE_TEXT_VIEWER_FRAGMENT_ARGUMENTS_KEY, args) + } + arguments = bundle + } } } @@ -41,10 +47,14 @@ class LicenseTextViewerFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to LicenseTextViewerFragment" } - val dependencyIndex = args.getInt(LICENSE_TEXT_VIEWER_FRAGMENT_DEPENDENCY_INDEX) - val licenseIndex = args.getInt(LICENSE_TEXT_VIEWER_FRAGMENT_LICENSE_INDEX) + val args = arguments.getProto( + LICENSE_TEXT_VIEWER_FRAGMENT_ARGUMENTS_KEY, + LicenseTextViewerFragmentArguments.getDefaultInstance() + ) + val dependencyIndex = args.dependencyIndex + val licenseIndex = args.licenseIndex return licenseTextViewerFragmentPresenter.handleCreateView( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListFragment.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListFragment.kt index 2e49f267984..3c84cf1d4a1 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListFragment.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListFragment.kt @@ -7,10 +7,11 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.ThirdPartyDependencyListFragmentArguments +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject -private const val IS_MULTIPANE_KEY = "ThirdPartyDependencyListFragment.is_multipane" - /** Fragment that contains third-party dependency list in the app. */ class ThirdPartyDependencyListFragment : InjectableFragment() { @@ -18,14 +19,20 @@ class ThirdPartyDependencyListFragment : InjectableFragment() { lateinit var thirdPartyDependencyListFragmentPresenter: ThirdPartyDependencyListFragmentPresenter companion object { + /** Arguments key for ThirdPartyDependencyListFragment. */ + private const val THIRD_PARTY_DEPENDENCY_LIST_FRAGMENT_ARGUMENTS_KEY = + "ThirdPartyDependencyListFragment.arguments" /** Returns an instance of [ThirdPartyDependencyListFragment]. */ fun newInstance(isMultipane: Boolean): ThirdPartyDependencyListFragment { - val args = Bundle() - args.putBoolean(IS_MULTIPANE_KEY, isMultipane) - val fragment = ThirdPartyDependencyListFragment() - fragment.arguments = args - return fragment + val args = + ThirdPartyDependencyListFragmentArguments.newBuilder().setIsMultipane(isMultipane).build() + + return ThirdPartyDependencyListFragment().apply { + arguments = Bundle().apply { + putProto(THIRD_PARTY_DEPENDENCY_LIST_FRAGMENT_ARGUMENTS_KEY, args) + } + } } } @@ -39,10 +46,14 @@ class ThirdPartyDependencyListFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - val args = checkNotNull(arguments) { + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to ThirdPartyDependencyListFragment" } - val isMultipane = args.getBoolean(IS_MULTIPANE_KEY, false) + val args = arguments.getProto( + THIRD_PARTY_DEPENDENCY_LIST_FRAGMENT_ARGUMENTS_KEY, + ThirdPartyDependencyListFragmentArguments.getDefaultInstance() + ) + val isMultipane = args?.isMultipane ?: false return thirdPartyDependencyListFragmentPresenter.handleCreateView( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt index ba337b31731..c36cafe9ecc 100644 --- a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt +++ b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt @@ -9,22 +9,15 @@ import org.oppia.android.R import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableDialogFragment import org.oppia.android.app.model.HelpIndex +import org.oppia.android.app.model.HintsAndSolutionDialogFragmentArguments +import org.oppia.android.app.model.HintsAndSolutionDialogFragmentStateBundle import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.State import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.util.extensions.getProto -import org.oppia.android.util.extensions.getStringFromBundle import org.oppia.android.util.extensions.putProto import javax.inject.Inject -private const val CURRENT_EXPANDED_ITEMS_LIST_SAVED_KEY = - "HintsAndSolutionDialogFragment.current_expanded_list_index" -private const val HINT_INDEX_SAVED_KEY = "HintsAndSolutionDialogFragment.hint_index" -private const val IS_HINT_REVEALED_SAVED_KEY = "HintsAndSolutionDialogFragment.is_hint_revealed" -private const val SOLUTION_INDEX_SAVED_KEY = "HintsAndSolutionDialogFragment.solution_index" -private const val IS_SOLUTION_REVEALED_SAVED_KEY = - "HintsAndSolutionDialogFragment.is_solution_revealed" - /* Fragment that displays a fullscreen dialog for Hints and Solutions. */ class HintsAndSolutionDialogFragment : InjectableDialogFragment(), @@ -43,14 +36,17 @@ class HintsAndSolutionDialogFragment : companion object { - internal const val ID_ARGUMENT_KEY = "HintsAndSolutionDialogFragment.id" - internal const val STATE_KEY = "HintsAndSolutionDialogFragment.state" - internal const val HELP_INDEX_KEY = "HintsAndSolutionDialogFragment.help_index" - internal const val WRITTEN_TRANSLATION_CONTEXT_KEY = - "HintsAndSolutionDialogFragment.written_translation_context" internal const val PROFILE_ID_KEY = "HintsAndSolutionDialogFragment.profile_id" + /** Arguments key for HintsAndSolutionDialogFragment. */ + const val HINT_AND_SOLUTION_DIALOG_FRAGMENT_ARGUMENTS_KEY = + "HintsAndSolutionDialogFragment.arguments" + + /** State key for HintsAndSolutionDialogFragment. */ + const val HINT_AND_SOLUTION_DIALOG_FRAGMENT_STATE_KEY = + "HintsAndSolutionDialogFragment.state" + /** * Creates a new instance of a DialogFragment to display hints and solution * @@ -69,12 +65,15 @@ class HintsAndSolutionDialogFragment : helpIndex: HelpIndex, writtenTranslationContext: WrittenTranslationContext ): HintsAndSolutionDialogFragment { + val args = HintsAndSolutionDialogFragmentArguments.newBuilder().apply { + this.idArgument = id + this.state = state + this.helpIndex = helpIndex + this.writtenTranslationContext = writtenTranslationContext + }.build() return HintsAndSolutionDialogFragment().apply { arguments = Bundle().apply { - putString(ID_ARGUMENT_KEY, id) - putProto(STATE_KEY, state) - putProto(HELP_INDEX_KEY, helpIndex) - putProto(WRITTEN_TRANSLATION_CONTEXT_KEY, writtenTranslationContext) + putProto(HINT_AND_SOLUTION_DIALOG_FRAGMENT_ARGUMENTS_KEY, args) } } } @@ -95,30 +94,42 @@ class HintsAndSolutionDialogFragment : container: ViewGroup?, savedInstanceState: Bundle? ): View { + if (savedInstanceState != null) { - expandedItemsList = - savedInstanceState.getIntegerArrayList(CURRENT_EXPANDED_ITEMS_LIST_SAVED_KEY) ?: ArrayList() - index = savedInstanceState.getInt(HINT_INDEX_SAVED_KEY, -1) + + val stateArgs = savedInstanceState.getProto( + HINT_AND_SOLUTION_DIALOG_FRAGMENT_STATE_KEY, + HintsAndSolutionDialogFragmentStateBundle.getDefaultInstance() + ) + expandedItemsList = stateArgs?.currentExpandedItemsList?.let { ArrayList(it) } ?: ArrayList() + + index = stateArgs?.hintIndex ?: -1 if (index == -1) index = null - isHintRevealed = savedInstanceState.getBoolean(IS_HINT_REVEALED_SAVED_KEY, false) - solutionIndex = savedInstanceState.getInt(SOLUTION_INDEX_SAVED_KEY, -1) + isHintRevealed = stateArgs?.isHintRevealed ?: false + solutionIndex = stateArgs?.solutionIndex ?: -1 if (solutionIndex == -1) solutionIndex = null - isSolutionRevealed = savedInstanceState.getBoolean(IS_SOLUTION_REVEALED_SAVED_KEY, false) + isSolutionRevealed = stateArgs?.isSolutionRevealed ?: false } - val args = + + val arguments = checkNotNull( arguments ) { "Expected arguments to be passed to HintsAndSolutionDialogFragment" } + val args = arguments.getProto( + HINT_AND_SOLUTION_DIALOG_FRAGMENT_ARGUMENTS_KEY, + HintsAndSolutionDialogFragmentArguments.getDefaultInstance() + ) + val id = checkNotNull( - args.getStringFromBundle(ID_ARGUMENT_KEY) + args.idArgument ) { "Expected id to be passed to HintsAndSolutionDialogFragment" } - val state = args.getProto(STATE_KEY, State.getDefaultInstance()) - val helpIndex = args.getProto(HELP_INDEX_KEY, HelpIndex.getDefaultInstance()) + val state = args.state ?: State.getDefaultInstance() + val helpIndex = args.helpIndex ?: HelpIndex.getDefaultInstance() val writtenTranslationContext = - args.getProto(WRITTEN_TRANSLATION_CONTEXT_KEY, WrittenTranslationContext.getDefaultInstance()) - val profileId = args.getProto(PROFILE_ID_KEY, ProfileId.getDefaultInstance()) + args.writtenTranslationContext ?: WrittenTranslationContext.getDefaultInstance() + val profileId = arguments.getProto(PROFILE_ID_KEY, ProfileId.getDefaultInstance()) return hintsAndSolutionDialogFragmentPresenter.handleCreateView( inflater, @@ -144,19 +155,19 @@ class HintsAndSolutionDialogFragment : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putIntegerArrayList(CURRENT_EXPANDED_ITEMS_LIST_SAVED_KEY, expandedItemsList) - if (index != null) { - outState.putInt(HINT_INDEX_SAVED_KEY, index!!) - } - if (isHintRevealed != null) { - outState.putBoolean(IS_HINT_REVEALED_SAVED_KEY, isHintRevealed!!) - } - if (solutionIndex != null) { - outState.putInt(SOLUTION_INDEX_SAVED_KEY, solutionIndex!!) - } - if (isSolutionRevealed != null) { - outState.putBoolean(IS_SOLUTION_REVEALED_SAVED_KEY, isSolutionRevealed!!) - } + + val args = HintsAndSolutionDialogFragmentStateBundle.newBuilder().apply { + this.addAllCurrentExpandedItems(expandedItemsList) + if (index != null) + this.hintIndex = index!! + if (this@HintsAndSolutionDialogFragment.isHintRevealed != null) + this.isHintRevealed = this@HintsAndSolutionDialogFragment.isHintRevealed!! + if (this@HintsAndSolutionDialogFragment.solutionIndex != null) + this.solutionIndex = this@HintsAndSolutionDialogFragment.solutionIndex!! + if (this@HintsAndSolutionDialogFragment.isSolutionRevealed != null) + this.isSolutionRevealed = this@HintsAndSolutionDialogFragment.isSolutionRevealed!! + }.build() + outState.putProto(HINT_AND_SOLUTION_DIALOG_FRAGMENT_STATE_KEY, args) } override fun onExpandListIconClicked(expandedItemsList: ArrayList) { diff --git a/app/src/main/java/org/oppia/android/app/home/HomeActivity.kt b/app/src/main/java/org/oppia/android/app/home/HomeActivity.kt index e6de1272867..8d6f828abd0 100644 --- a/app/src/main/java/org/oppia/android/app/home/HomeActivity.kt +++ b/app/src/main/java/org/oppia/android/app/home/HomeActivity.kt @@ -8,7 +8,6 @@ import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.activity.route.ActivityRouter import org.oppia.android.app.drawer.ExitProfileDialogFragment -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.drawer.TAG_SWITCH_PROFILE_DIALOG import org.oppia.android.app.model.DestinationScreen import org.oppia.android.app.model.ExitProfileDialogArguments @@ -20,6 +19,8 @@ import org.oppia.android.app.model.ScreenName.HOME_ACTIVITY import org.oppia.android.app.topic.TopicActivity import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** The central activity for all users entering the app. */ @@ -40,10 +41,14 @@ class HomeActivity : private var internalProfileId: Int = -1 companion object { - fun createHomeActivity(context: Context, profileId: Int?): Intent { + + fun createHomeActivity(context: Context, profileId: ProfileId?): Intent { + return Intent(context, HomeActivity::class.java).apply { - putExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, profileId) decorateWithScreenName(HOME_ACTIVITY) + if (profileId != null) { + decorateWithUserProfileId(profileId) + } } } } @@ -51,7 +56,8 @@ class HomeActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - internalProfileId = intent?.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1)!! + + internalProfileId = intent.extractCurrentUserProfileId().internalId homeActivityPresenter.handleOnCreate(internalProfileId) title = resourceHandler.getStringInLocale(R.string.home_activity_title) } diff --git a/app/src/main/java/org/oppia/android/app/home/HomeFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/home/HomeFragmentPresenter.kt index b62c266612d..64d53c68009 100644 --- a/app/src/main/java/org/oppia/android/app/home/HomeFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/HomeFragmentPresenter.kt @@ -8,7 +8,6 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager import org.oppia.android.R -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.home.promotedlist.ComingSoonTopicListViewModel import org.oppia.android.app.home.promotedlist.PromotedStoryListViewModel @@ -36,6 +35,7 @@ import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.parser.html.StoryHtmlParserEntityType import org.oppia.android.util.parser.html.TopicHtmlParserEntityType +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** The presenter for [HomeFragment]. */ @@ -64,7 +64,8 @@ class HomeFragmentPresenter @Inject constructor( // NB: Both the view model and lifecycle owner must be set in order to correctly bind LiveData elements to // data-bound view models. - internalProfileId = activity.intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + internalProfileId = activity.intent.extractCurrentUserProfileId().internalId + logHomeActivityEvent() val homeViewModel = HomeViewModel( diff --git a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragment.kt b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragment.kt index c7c23243ac0..f599f9150e5 100644 --- a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragment.kt +++ b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragment.kt @@ -7,12 +7,12 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.PromotedStory +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject -private const val RECENTLY_PLAYED_FRAGMENT_INTERNAL_PROFILE_ID_KEY = - "RecentlyPlayedFragment.internal_profile_id" - /** Fragment that contains all recently played stories. */ class RecentlyPlayedFragment : InjectableFragment(), PromotedStoryClickListener { companion object { @@ -20,11 +20,12 @@ class RecentlyPlayedFragment : InjectableFragment(), PromotedStoryClickListener /** Returns a new [RecentlyPlayedFragment] to display recently played stories. */ fun newInstance(internalProfileId: Int): RecentlyPlayedFragment { - val recentlyPlayedFragment = RecentlyPlayedFragment() - val args = Bundle() - args.putInt(RECENTLY_PLAYED_FRAGMENT_INTERNAL_PROFILE_ID_KEY, internalProfileId) - recentlyPlayedFragment.arguments = args - return recentlyPlayedFragment + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + return RecentlyPlayedFragment().apply { + arguments = Bundle().apply { + decorateWithUserProfileId(profileId) + } + } } } @@ -41,9 +42,12 @@ class RecentlyPlayedFragment : InjectableFragment(), PromotedStoryClickListener container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to RecentlyPlayedFragment" } - val internalProfileId = args.getInt(RECENTLY_PLAYED_FRAGMENT_INTERNAL_PROFILE_ID_KEY, -1) + val profileId = + arguments.extractCurrentUserProfileId() + + val internalProfileId = profileId.internalId return recentlyPlayedFragmentPresenter.handleCreateView(inflater, container, internalProfileId) } diff --git a/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivity.kt b/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivity.kt index 9e288a070f4..18b623ab7ec 100644 --- a/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivity.kt +++ b/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivity.kt @@ -5,10 +5,12 @@ import android.content.Intent import android.os.Bundle import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.home.HomeActivity +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ScreenName.MY_DOWNLOADS_ACTIVITY import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** The activity for displaying [MyDownloadsFragment]. */ @@ -21,24 +23,24 @@ class MyDownloadsActivity : InjectableAutoLocalizedAppCompatActivity() { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) myDownloadsActivityPresenter.handleOnCreate() - internalProfileId = intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + internalProfileId = intent?.extractCurrentUserProfileId()?.internalId ?: -1 } companion object { - fun createMyDownloadsActivityIntent(context: Context, profileId: Int?): Intent { + fun createMyDownloadsActivityIntent(context: Context, internalProfileId: Int?): Intent { + val profileId = internalProfileId?.let { ProfileId.newBuilder().setInternalId(it).build() } val intent = Intent(context, MyDownloadsActivity::class.java) - intent.putExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, profileId) + if (profileId != null) { + intent.decorateWithUserProfileId(profileId) + } intent.decorateWithScreenName(MY_DOWNLOADS_ACTIVITY) return intent } - - fun getIntentKey(): String { - return NAVIGATION_PROFILE_ID_ARGUMENT_KEY - } } override fun onBackPressed() { - val intent = HomeActivity.createHomeActivity(this, internalProfileId) + val profileid = ProfileId.newBuilder().setInternalId(internalProfileId).build() + val intent = HomeActivity.createHomeActivity(this, profileid) startActivity(intent) finish() } diff --git a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivity.kt b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivity.kt index 7f70f143426..92a96385275 100644 --- a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivity.kt +++ b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivity.kt @@ -5,8 +5,11 @@ import android.content.Intent 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.ScreenName.ONGOING_TOPIC_LIST_ACTIVITY import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Activity for ongoing topics. */ @@ -17,21 +20,17 @@ class OngoingTopicListActivity : InjectableAutoLocalizedAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val internalProfileId: Int = intent.getIntExtra( - ONGOING_TOPIC_LIST_ACTIVITY_PROFILE_ID_KEY, -1 - ) + val internalProfileId: Int = intent?.extractCurrentUserProfileId()?.internalId ?: -1 ongoingTopicListActivityPresenter.handleOnCreate(internalProfileId) } companion object { // TODO(#1655): Re-restrict access to fields in tests post-Gradle. - const val ONGOING_TOPIC_LIST_ACTIVITY_PROFILE_ID_KEY = - "OngoingTopicListActivity.profile_id" - /** Returns a new [Intent] to route to [OngoingTopicListActivity] for a specified profile ID. */ fun createOngoingTopicListActivityIntent(context: Context, internalProfileId: Int): Intent { + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() return Intent(context, OngoingTopicListActivity::class.java).apply { - putExtra(ONGOING_TOPIC_LIST_ACTIVITY_PROFILE_ID_KEY, internalProfileId) + decorateWithUserProfileId(profileId) decorateWithScreenName(ONGOING_TOPIC_LIST_ACTIVITY) } } diff --git a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragment.kt b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragment.kt index b06d1c1a27a..df42a578654 100644 --- a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragment.kt +++ b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragment.kt @@ -7,6 +7,9 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.ProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Fragment for displaying [OngoingTopicListActivity]. */ @@ -15,14 +18,13 @@ class OngoingTopicListFragment : InjectableFragment() { companion object { // TODO(#1655): Re-restrict access to fields in tests post-Gradle. const val ONGOING_TOPIC_LIST_FRAGMENT_TAG = "TAG_ONGOING_TOPIC_LIST_FRAGMENT" - internal const val ONGOING_TOPIC_LIST_FRAGMENT_PROFILE_ID_KEY = - "OngoingTopicListFragment.profile_id" /** Returns a new [OngoingTopicListFragment] to display corresponding to the specified profile ID. */ fun newInstance(internalProfileId: Int): OngoingTopicListFragment { val ongoingTopicListFragment = OngoingTopicListFragment() val args = Bundle() - args.putInt(ONGOING_TOPIC_LIST_FRAGMENT_PROFILE_ID_KEY, internalProfileId) + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() + args.decorateWithUserProfileId(profileId) ongoingTopicListFragment.arguments = args return ongoingTopicListFragment } @@ -43,9 +45,7 @@ class OngoingTopicListFragment : InjectableFragment() { ): View? { val args = checkNotNull(arguments) { "Expected arguments to be passed to OngoingTopicListFragment" } - val internalProfileId = args.getInt( - ONGOING_TOPIC_LIST_FRAGMENT_PROFILE_ID_KEY, -1 - ) + val internalProfileId = args.extractCurrentUserProfileId().internalId return ongoingTopicListFragmentPresenter.handleCreateView( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/options/AppLanguageActivity.kt b/app/src/main/java/org/oppia/android/app/options/AppLanguageActivity.kt index 6387577fbce..961113c101f 100644 --- a/app/src/main/java/org/oppia/android/app/options/AppLanguageActivity.kt +++ b/app/src/main/java/org/oppia/android/app/options/AppLanguageActivity.kt @@ -5,16 +5,18 @@ import android.content.Intent import android.os.Bundle import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.model.AppLanguageActivityParams import org.oppia.android.app.model.AppLanguageActivityStateBundle import org.oppia.android.app.model.OppiaLanguage +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ScreenName.APP_LANGUAGE_ACTIVITY import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.getProtoExtra import org.oppia.android.util.extensions.putProto import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** The activity to change the language of the app. */ @@ -26,7 +28,7 @@ class AppLanguageActivity : InjectableAutoLocalizedAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - profileId = intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + profileId = intent?.extractCurrentUserProfileId()?.internalId ?: -1 appLanguageActivityPresenter.handleOnCreate( savedInstanceState?.retrieveLanguageFromSavedState() ?: intent.retrieveLanguageFromParams(), profileId!! @@ -41,14 +43,15 @@ class AppLanguageActivity : InjectableAutoLocalizedAppCompatActivity() { fun createAppLanguageActivityIntent( context: Context, oppiaLanguage: OppiaLanguage, - profileId: Int? + internalProfileId: Int? ): Intent { + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId!!).build() return Intent(context, AppLanguageActivity::class.java).apply { val arguments = AppLanguageActivityParams.newBuilder().apply { this.oppiaLanguage = oppiaLanguage }.build() putProtoExtra(ACTIVITY_PARAMS_KEY, arguments) - putExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, profileId) + decorateWithUserProfileId(profileId) decorateWithScreenName(APP_LANGUAGE_ACTIVITY) } } diff --git a/app/src/main/java/org/oppia/android/app/options/AppLanguageFragment.kt b/app/src/main/java/org/oppia/android/app/options/AppLanguageFragment.kt index 34e25221b52..b946c93555b 100644 --- a/app/src/main/java/org/oppia/android/app/options/AppLanguageFragment.kt +++ b/app/src/main/java/org/oppia/android/app/options/AppLanguageFragment.kt @@ -5,14 +5,16 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment import org.oppia.android.app.model.AppLanguageFragmentArguments import org.oppia.android.app.model.AppLanguageFragmentStateBundle import org.oppia.android.app.model.OppiaLanguage +import org.oppia.android.app.model.ProfileId import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** The fragment to change the language of the app. */ @@ -27,14 +29,15 @@ class AppLanguageFragment : InjectableFragment(), AppLanguageRadioButtonListener private const val FRAGMENT_SAVED_STATE_KEY = "AppLanguageFragment.saved_state" /** Returns a new [AppLanguageFragment] instance. */ - fun newInstance(oppiaLanguage: OppiaLanguage, profileId: Int): AppLanguageFragment { + fun newInstance(oppiaLanguage: OppiaLanguage, internalProfileId: Int): AppLanguageFragment { + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() return AppLanguageFragment().apply { arguments = Bundle().apply { val args = AppLanguageFragmentArguments.newBuilder().apply { this.oppiaLanguage = oppiaLanguage }.build() putProto(FRAGMENT_ARGUMENTS_KEY, args) - putInt(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, profileId) + decorateWithUserProfileId(profileId) } } } @@ -67,7 +70,7 @@ class AppLanguageFragment : InjectableFragment(), AppLanguageRadioButtonListener savedInstanceState?.retrieveLanguageFromSavedState() ?: arguments?.retrieveLanguageFromArguments() ) { "Expected arguments to be passed to AppLanguageFragment" } - profileId = arguments?.getInt(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + profileId = arguments?.extractCurrentUserProfileId()?.internalId ?: -1 return appLanguageFragmentPresenter.handleOnCreateView( inflater, diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsActivity.kt b/app/src/main/java/org/oppia/android/app/options/OptionsActivity.kt index ccbcea40b7b..ff8d49a7ede 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsActivity.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsActivity.kt @@ -7,25 +7,31 @@ import android.widget.TextView import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.model.AudioLanguage import org.oppia.android.app.model.AudioLanguageActivityResultBundle import org.oppia.android.app.model.OppiaLanguage +import org.oppia.android.app.model.OptionsActivityParams +import org.oppia.android.app.model.OptionsActivityStateBundle +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.model.ReadingTextSizeActivityResultBundle import org.oppia.android.app.model.ScreenName.OPTIONS_ACTIVITY import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.getProtoExtra -import org.oppia.android.util.extensions.getStringFromBundle +import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject -private const val SELECTED_OPTIONS_TITLE_SAVED_KEY = "OptionsActivity.selected_options_title" -private const val SELECTED_FRAGMENT_SAVED_KEY = "OptionsActivity.selected_fragment" /** [String] key for mapping to [ReadingTextSizeFragment]. */ const val READING_TEXT_SIZE_FRAGMENT = "READING_TEXT_SIZE_FRAGMENT" + /** [String] key for mapping to [AppLanguageFragment]. */ const val APP_LANGUAGE_FRAGMENT = "APP_LANGUAGE_FRAGMENT" + /** [String] key for mapping to [AudioLanguageFragment]. */ const val AUDIO_LANGUAGE_FRAGMENT = "AUDIO_LANGUAGE_FRAGMENT" @@ -51,20 +57,27 @@ class OptionsActivity : companion object { // TODO(#1655): Re-restrict access to fields in tests post-Gradle. - /** [Boolean] indicating whether user is navigating from Drawer. */ - const val BOOL_IS_FROM_NAVIGATION_DRAWER_EXTRA_KEY = - "OptionsActivity.bool_is_from_navigation_drawer_extra_key" + /** Params key for OptionsActivity. */ + const val OPTIONS_ACTIVITY_PARAMS_KEY = "OptionsActivity.params" + + /** Saved state key for OptionsActivity. */ + const val OPTIONS_ACTIVITY_STATE_KEY = "OptionsActivity.state" /** Returns an [Intent] to start this activity. */ fun createOptionsActivity( context: Context, - profileId: Int?, + profileId: ProfileId?, isFromNavigationDrawer: Boolean ): Intent { + val args = + OptionsActivityParams.newBuilder().setIsFromNavigationDrawer(isFromNavigationDrawer) + .build() return Intent(context, OptionsActivity::class.java).apply { - putExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, profileId) - putExtra(BOOL_IS_FROM_NAVIGATION_DRAWER_EXTRA_KEY, isFromNavigationDrawer) + putProtoExtra(OPTIONS_ACTIVITY_PARAMS_KEY, args) decorateWithScreenName(OPTIONS_ACTIVITY) + if (profileId != null) { + decorateWithUserProfileId(profileId) + } } } } @@ -72,22 +85,28 @@ class OptionsActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) - val isFromNavigationDrawer = intent.getBooleanExtra( - BOOL_IS_FROM_NAVIGATION_DRAWER_EXTRA_KEY, - /* defaultValue= */ false + val args = intent.getProtoExtra( + OPTIONS_ACTIVITY_PARAMS_KEY, + OptionsActivityParams.getDefaultInstance() ) - profileId = intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + val isFromNavigationDrawer = args?.isFromNavigationDrawer ?: false + profileId = intent.extractCurrentUserProfileId().internalId if (savedInstanceState != null) { isFirstOpen = false } + val stateArgs = + savedInstanceState?.getProto( + OPTIONS_ACTIVITY_STATE_KEY, + OptionsActivityStateBundle.getDefaultInstance() + ) + selectedFragment = if (savedInstanceState == null) { READING_TEXT_SIZE_FRAGMENT } else { - @Suppress("DEPRECATION") // TODO(#5405): Ensure the correct type is being retrieved. - savedInstanceState.get(SELECTED_FRAGMENT_SAVED_KEY) as String + stateArgs?.selectedFragment as String } val extraOptionsTitle = - savedInstanceState?.getStringFromBundle(SELECTED_OPTIONS_TITLE_SAVED_KEY) + stateArgs?.selectedOptionsTitle optionActivityPresenter.handleOnCreate( isFromNavigationDrawer, extraOptionsTitle, @@ -171,9 +190,12 @@ class OptionsActivity : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) val titleTextView = findViewById(R.id.options_activity_selected_options_title) - if (titleTextView != null) { - outState.putString(SELECTED_OPTIONS_TITLE_SAVED_KEY, titleTextView.text.toString()) - } - outState.putString(SELECTED_FRAGMENT_SAVED_KEY, selectedFragment) + val args = OptionsActivityStateBundle.newBuilder().apply { + if (titleTextView != null) { + selectedOptionsTitle = titleTextView.text.toString() + } + selectedFragment = this@OptionsActivity.selectedFragment + }.build() + outState.putProto(OPTIONS_ACTIVITY_STATE_KEY, args) } } diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt index 6193d3a337e..ccdff3ba113 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt @@ -122,7 +122,7 @@ class OptionsActivityPresenter @Inject constructor( */ fun loadAppLanguageFragment(appLanguage: OppiaLanguage) { val appLanguageFragment = - AppLanguageFragment.newInstance(appLanguage, profileId = this.profileId!!) + AppLanguageFragment.newInstance(appLanguage, this.profileId!!) activity.supportFragmentManager .beginTransaction() .replace(R.id.multipane_options_container, appLanguageFragment) diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsFragment.kt b/app/src/main/java/org/oppia/android/app/options/OptionsFragment.kt index bc188d4219b..43a91064be6 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsFragment.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsFragment.kt @@ -9,24 +9,26 @@ import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment import org.oppia.android.app.model.AudioLanguage import org.oppia.android.app.model.OppiaLanguage +import org.oppia.android.app.model.OptionsFragmentArguments import org.oppia.android.app.model.ReadingTextSize -import org.oppia.android.util.extensions.getStringFromBundle +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject /** OnActivity result key to access [ReadingTextSize] result. */ const val MESSAGE_READING_TEXT_SIZE_RESULTS_KEY = "OptionsFragment.message_reading_text_size" -/** OnActivity result key to access [OppiaLanguage] result. */ -const val MESSAGE_APP_LANGUAGE_ARGUMENT_KEY = "OptionsFragment.message_app_language" + /** OnActivity result key to access [AudioLanguage] result. */ const val MESSAGE_AUDIO_LANGUAGE_RESULTS_KEY = "OptionsFragment.message_audio_language" + /** Request code for [ReadingTextSize]. */ const val REQUEST_CODE_TEXT_SIZE = 1 + /** Request code for [AudioLanguage]. */ const val REQUEST_CODE_AUDIO_LANGUAGE = 3 -private const val IS_MULTIPANE_EXTRA = "IS_MULTIPANE_EXTRA" -private const val IS_FIRST_OPEN_EXTRA = "IS_FIRST_OPEN_EXTRA" -private const val SELECTED_FRAGMENT_EXTRA = "SELECTED_FRAGMENT_EXTRA" +/** Arguments key for OptionsFragment. */ +const val OPTIONS_FRAGMENT_ARGUMENTS_KEY = "OptionsFragment.arguments" /** Fragment that contains an introduction to the app. */ class OptionsFragment : InjectableFragment() { @@ -40,13 +42,17 @@ class OptionsFragment : InjectableFragment() { isFirstOpen: Boolean, selectedFragment: String ): OptionsFragment { - val args = Bundle() - args.putBoolean(IS_MULTIPANE_EXTRA, isMultipane) - args.putBoolean(IS_FIRST_OPEN_EXTRA, isFirstOpen) - args.putString(SELECTED_FRAGMENT_EXTRA, selectedFragment) - val fragment = OptionsFragment() - fragment.arguments = args - return fragment + + val args = OptionsFragmentArguments.newBuilder().apply { + this.isMultipane = isMultipane + this.isFirstOpen = isFirstOpen + this.selectedFragment = selectedFragment + }.build() + return OptionsFragment().apply { + arguments = Bundle().apply { + putProto(OPTIONS_FRAGMENT_ARGUMENTS_KEY, args) + } + } } } @@ -60,11 +66,16 @@ class OptionsFragment : InjectableFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val args = + val arguments = checkNotNull(arguments) { "Expected arguments to be passed to OptionsFragment" } - val isMultipane = args.getBoolean(IS_MULTIPANE_EXTRA) - val isFirstOpen = args.getBoolean(IS_FIRST_OPEN_EXTRA) - val selectedFragment = checkNotNull(args.getStringFromBundle(SELECTED_FRAGMENT_EXTRA)) + val args = arguments.getProto( + OPTIONS_FRAGMENT_ARGUMENTS_KEY, + OptionsFragmentArguments.getDefaultInstance() + ) + + val isMultipane = args.isMultipane + val isFirstOpen = args.isFirstOpen + val selectedFragment = checkNotNull(args.selectedFragment) return optionsFragmentPresenter.handleCreateView( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/options/OptionsFragmentPresenter.kt index 462b67c8b13..b13cf402a8e 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsFragmentPresenter.kt @@ -6,7 +6,6 @@ import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView -import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.AppLanguageSelection import org.oppia.android.app.model.AudioLanguage @@ -23,6 +22,7 @@ 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.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import java.security.InvalidParameterException import javax.inject.Inject @@ -70,7 +70,7 @@ class OptionsFragmentPresenter @Inject constructor( /* attachToRoot= */ false ) - internalProfileId = activity.intent.getIntExtra(NAVIGATION_PROFILE_ID_ARGUMENT_KEY, -1) + internalProfileId = activity.intent?.extractCurrentUserProfileId()?.internalId ?: -1 profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() optionControlsViewModel.setProfileId(profileId) @@ -184,8 +184,10 @@ class OptionsFragmentPresenter @Inject constructor( private enum class ViewType { /** Represents view type for displaying [ReadingTextSize]. */ VIEW_TYPE_READING_TEXT_SIZE, + /** Represents view type for displaying [OppiaLanguage]. */ VIEW_TYPE_APP_LANGUAGE, + /** Represents view type for displaying [AudioLanguage]. */ VIEW_TYPE_AUDIO_LANGUAGE } diff --git a/app/src/main/java/org/oppia/android/app/player/audio/AudioFragment.kt b/app/src/main/java/org/oppia/android/app/player/audio/AudioFragment.kt index 0fe678cbb5a..a4fc2738f49 100755 --- a/app/src/main/java/org/oppia/android/app/player/audio/AudioFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/audio/AudioFragment.kt @@ -7,7 +7,10 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.State +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId +import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject /** Fragment that controls audio for a content-card. */ @@ -25,10 +28,12 @@ class AudioFragment : * @param profileId used by AudioFragment to get Audio Language. * @return a new instance of [AudioFragment]. */ - fun newInstance(profileId: Int): AudioFragment { + fun newInstance(internalProfileId: Int): AudioFragment { + + val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() val audioFragment = AudioFragment() val args = Bundle() - args.putInt(AUDIO_FRAGMENT_PROFILE_ID_ARGUMENT_KEY, profileId) + args.decorateWithUserProfileId(profileId) audioFragment.arguments = args return audioFragment } @@ -46,7 +51,7 @@ class AudioFragment : ): View? { super.onCreateView(inflater, container, savedInstanceState) val internalProfileId = - arguments!!.getInt(AUDIO_FRAGMENT_PROFILE_ID_ARGUMENT_KEY, /* defaultValue= */ -1) + arguments?.extractCurrentUserProfileId()?.internalId ?: -1 return audioFragmentPresenter.handleCreateView(inflater, container, internalProfileId) } diff --git a/app/src/main/java/org/oppia/android/app/player/audio/LanguageDialogFragment.kt b/app/src/main/java/org/oppia/android/app/player/audio/LanguageDialogFragment.kt index 8480ce2a454..178c93c57cb 100644 --- a/app/src/main/java/org/oppia/android/app/player/audio/LanguageDialogFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/audio/LanguageDialogFragment.kt @@ -9,22 +9,29 @@ import org.oppia.android.R import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableDialogFragment import org.oppia.android.app.model.AudioLanguage +import org.oppia.android.app.model.LanguageDialogFragmentArguments import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import org.oppia.android.util.locale.OppiaLocale import javax.inject.Inject import kotlin.collections.ArrayList -private const val LANGUAGE_LIST_ARGUMENT_KEY = "LanguageDialogFragment.language_list" -private const val SELECTED_INDEX_ARGUMENT_KEY = "LanguageDialogFragment.selected_index" - /** * DialogFragment that controls language selection in audio and written translations. */ class LanguageDialogFragment : InjectableDialogFragment() { - @Inject lateinit var appLanguageResourceHandler: AppLanguageResourceHandler - @Inject lateinit var machineLocale: OppiaLocale.MachineLocale + @Inject + lateinit var appLanguageResourceHandler: AppLanguageResourceHandler + + @Inject + lateinit var machineLocale: OppiaLocale.MachineLocale companion object { + + /** Arguments key for LanguageDialogFragment. */ + const val LANGUAGE_DIALOG_FRAGMENT_ARGUMENTS_KEY = "LanguageDialogFragment.arguments" + /** * This function is responsible for displaying content in DialogFragment. * @@ -37,12 +44,16 @@ class LanguageDialogFragment : InjectableDialogFragment() { currentLanguageCode: String ): LanguageDialogFragment { val selectedIndex = languageArrayList.indexOf(currentLanguageCode) - val languageDialogFragment = LanguageDialogFragment() - val args = Bundle() - args.putStringArrayList(LANGUAGE_LIST_ARGUMENT_KEY, languageArrayList) - args.putInt(SELECTED_INDEX_ARGUMENT_KEY, selectedIndex) - languageDialogFragment.arguments = args - return languageDialogFragment + + val args = LanguageDialogFragmentArguments.newBuilder().apply { + this.addAllLanguages(languageArrayList) + this.selectedIndex = selectedIndex + }.build() + return LanguageDialogFragment().apply { + arguments = Bundle().apply { + putProto(LANGUAGE_DIALOG_FRAGMENT_ARGUMENTS_KEY, args) + } + } } } @@ -52,12 +63,18 @@ class LanguageDialogFragment : InjectableDialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val args = checkNotNull(arguments) { "Expected arguments to be pass to LanguageDialogFragment" } + val arguments = + checkNotNull(arguments) { "Expected arguments to be pass to LanguageDialogFragment" } - var selectedIndex = args.getInt(SELECTED_INDEX_ARGUMENT_KEY, 0) + val args = arguments.getProto( + LANGUAGE_DIALOG_FRAGMENT_ARGUMENTS_KEY, + LanguageDialogFragmentArguments.getDefaultInstance() + ) + var selectedIndex = args?.selectedIndex ?: 0 val languageCodeArrayList: ArrayList = checkNotNull( - args.getStringArrayList(LANGUAGE_LIST_ARGUMENT_KEY) + args?.languagesList?.let { ArrayList(it) } ) + val languageNameArrayList = ArrayList() for (languageCode in languageCodeArrayList) { 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 54cde19b341..e940dabb48c 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 @@ -211,7 +211,7 @@ class ExplorationActivityPresenter @Inject constructor( R.id.action_options -> { val intent = OptionsActivity.createOptionsActivity( activity, - profileId.internalId, + profileId, /* isFromNavigationDrawer= */ false ) fontScaleConfigurationUtil.adjustFontScale(activity, ReadingTextSize.MEDIUM_TEXT_SIZE) @@ -221,7 +221,7 @@ class ExplorationActivityPresenter @Inject constructor( R.id.action_help -> { val intent = HelpActivity.createHelpActivityIntent( activity, - profileId.internalId, + profileId, /* isFromNavigationDrawer= */false ) fontScaleConfigurationUtil.adjustFontScale(activity, ReadingTextSize.MEDIUM_TEXT_SIZE) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt index e55b81ef35d..ca7c1d55565 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup 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.StateFragmentArguments import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler @@ -19,7 +20,8 @@ import org.oppia.android.app.player.state.listener.PreviousResponsesHeaderClickL import org.oppia.android.app.player.state.listener.ReturnToTopicNavigationButtonListener import org.oppia.android.app.player.state.listener.ShowHintAvailabilityListener import org.oppia.android.app.player.state.listener.SubmitNavigationButtonListener -import org.oppia.android.util.extensions.getStringFromBundle +import org.oppia.android.util.extensions.getProto +import org.oppia.android.util.extensions.putProto import javax.inject.Inject /** Fragment that represents the current state of an exploration. */ @@ -36,6 +38,10 @@ class StateFragment : PreviousResponsesHeaderClickListener, ShowHintAvailabilityListener { companion object { + + /** Arguments key for StateFragment. */ + const val STATE_FRAGMENT_ARGUMENTS_KEY = "StateFragment.arguments" + /** * Creates a new instance of a StateFragment. * @param internalProfileId used by StateFragment to mark progress. @@ -50,14 +56,18 @@ class StateFragment : storyId: String, explorationId: String ): StateFragment { - val stateFragment = StateFragment() - val args = Bundle() - args.putInt(STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY, internalProfileId) - args.putString(STATE_FRAGMENT_TOPIC_ID_ARGUMENT_KEY, topicId) - args.putString(STATE_FRAGMENT_STORY_ID_ARGUMENT_KEY, storyId) - args.putString(STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY, explorationId) - stateFragment.arguments = args - return stateFragment + + val args = StateFragmentArguments.newBuilder().apply { + this.internalProfileId = internalProfileId + this.topicId = topicId + this.storyId = storyId + this.explorationId = explorationId + }.build() + return StateFragment().apply { + arguments = Bundle().apply { + putProto(STATE_FRAGMENT_ARGUMENTS_KEY, args) + } + } } } @@ -74,11 +84,13 @@ class StateFragment : container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val internalProfileId = arguments!!.getInt(STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY, -1) - val topicId = arguments!!.getStringFromBundle(STATE_FRAGMENT_TOPIC_ID_ARGUMENT_KEY)!! - val storyId = arguments!!.getStringFromBundle(STATE_FRAGMENT_STORY_ID_ARGUMENT_KEY)!! + val args = + arguments?.getProto(STATE_FRAGMENT_ARGUMENTS_KEY, StateFragmentArguments.getDefaultInstance()) + val internalProfileId = args?.internalProfileId ?: -1 + val topicId = args?.topicId!! + val storyId = args.storyId!! val explorationId = - arguments!!.getStringFromBundle(STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY)!! + args.explorationId!! return stateFragmentPresenter.handleCreateView( inflater, container, diff --git a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt index 032849bd9d4..e8117516284 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt @@ -12,6 +12,7 @@ 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.State +import org.oppia.android.app.model.StateFragmentTestActivityParams import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.audio.AudioButtonListener import org.oppia.android.app.player.exploration.HintsAndSolutionExplorationManagerListener @@ -19,19 +20,10 @@ import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_DIALOG 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.StopStatePlayingSessionWithSavedProgressListener +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra import javax.inject.Inject -internal const val TEST_ACTIVITY_PROFILE_ID_EXTRA_KEY = - "StateFragmentTestActivity.test_activity_profile_id" -internal const val TEST_ACTIVITY_TOPIC_ID_EXTRA_KEY = - "StateFragmentTestActivity.test_activity_topic_id" -internal const val TEST_ACTIVITY_STORY_ID_EXTRA_KEY = - "StateFragmentTestActivity.test_activity_story_id" -internal const val TEST_ACTIVITY_EXPLORATION_ID_EXTRA_KEY = - "StateFragmentTestActivity.test_activity_exploration_id" -internal const val TEST_ACTIVITY_SHOULD_SAVE_PARTIAL_PROGRESS_EXTRA_KEY = - "StateFragmentTestActivity.test_activity_should_save_partial_progress" - /** Test Activity used for testing StateFragment. */ class StateFragmentTestActivity : InjectableAutoLocalizedAppCompatActivity(), @@ -52,8 +44,13 @@ class StateFragmentTestActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (activityComponent as ActivityComponentImpl).inject(this) + val args = intent.getProtoExtra( + STATE_FRAGMENT_TEST_ACTIVITY_PARAMS_KEY, + StateFragmentTestActivityParams.getDefaultInstance() + ) + profileId = ProfileId.newBuilder().apply { - internalId = intent.getIntExtra(TEST_ACTIVITY_PROFILE_ID_EXTRA_KEY, -1) + internalId = args?.internalProfileId ?: -1 }.build() stateFragmentTestActivityPresenter.handleOnCreate() } @@ -67,6 +64,10 @@ class StateFragmentTestActivity : override fun onEditorAction(actionCode: Int) {} companion object { + + /** Params key for StateFragmentTestActivity. */ + const val STATE_FRAGMENT_TEST_ACTIVITY_PARAMS_KEY = "StateFragmentTestActivity.params" + fun createTestActivityIntent( context: Context, profileId: Int, @@ -75,15 +76,15 @@ class StateFragmentTestActivity : explorationId: String, shouldSavePartialProgress: Boolean ): Intent { + val args = StateFragmentTestActivityParams.newBuilder().apply { + this.internalProfileId = profileId + this.topicId = topicId + this.storyId = storyId + this.explorationId = explorationId + this.shouldSavePartialProgress = shouldSavePartialProgress + }.build() val intent = Intent(context, StateFragmentTestActivity::class.java) - intent.putExtra(TEST_ACTIVITY_PROFILE_ID_EXTRA_KEY, profileId) - intent.putExtra(TEST_ACTIVITY_TOPIC_ID_EXTRA_KEY, topicId) - intent.putExtra(TEST_ACTIVITY_STORY_ID_EXTRA_KEY, storyId) - intent.putExtra(TEST_ACTIVITY_EXPLORATION_ID_EXTRA_KEY, explorationId) - intent.putExtra( - TEST_ACTIVITY_SHOULD_SAVE_PARTIAL_PROGRESS_EXTRA_KEY, - shouldSavePartialProgress - ) + intent.putProtoExtra(STATE_FRAGMENT_TEST_ACTIVITY_PARAMS_KEY, args) return intent } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt index dd1dbc96a80..3c52bb6e777 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt @@ -6,9 +6,11 @@ import androidx.databinding.DataBindingUtil import org.oppia.android.R import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.StateFragmentTestActivityParams import org.oppia.android.app.player.exploration.HintsAndSolutionExplorationManagerFragment import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_EXPLORATION_MANAGER import org.oppia.android.app.player.state.StateFragment +import org.oppia.android.app.player.state.testing.StateFragmentTestActivity.Companion.STATE_FRAGMENT_TEST_ACTIVITY_PARAMS_KEY import org.oppia.android.databinding.StateFragmentTestActivityBinding import org.oppia.android.domain.exploration.ExplorationDataController import org.oppia.android.domain.oppialogger.OppiaLogger @@ -17,6 +19,7 @@ import org.oppia.android.domain.topic.TEST_STORY_ID_0 import org.oppia.android.domain.topic.TEST_TOPIC_ID_0 import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.extensions.getProtoExtra import javax.inject.Inject private const val TEST_ACTIVITY_TAG = "TestActivity" @@ -46,16 +49,19 @@ class StateFragmentTestActivityPresenter @Inject constructor( viewModel = stateFragmentTestViewModel } - profileId = activity.intent.getIntExtra(TEST_ACTIVITY_PROFILE_ID_EXTRA_KEY, 1) + val args = activity.intent.getProtoExtra( + STATE_FRAGMENT_TEST_ACTIVITY_PARAMS_KEY, + StateFragmentTestActivityParams.getDefaultInstance() + ) + profileId = args?.internalProfileId ?: 1 topicId = - activity.intent.getStringExtra(TEST_ACTIVITY_TOPIC_ID_EXTRA_KEY) ?: TEST_TOPIC_ID_0 + args?.topicId ?: TEST_TOPIC_ID_0 storyId = - activity.intent.getStringExtra(TEST_ACTIVITY_STORY_ID_EXTRA_KEY) ?: TEST_STORY_ID_0 + args?.storyId ?: TEST_STORY_ID_0 explorationId = - activity.intent.getStringExtra(TEST_ACTIVITY_EXPLORATION_ID_EXTRA_KEY) + args?.explorationId ?: TEST_EXPLORATION_ID_2 - shouldSavePartialProgress = - activity.intent.getBooleanExtra(TEST_ACTIVITY_SHOULD_SAVE_PARTIAL_PROGRESS_EXTRA_KEY, false) + shouldSavePartialProgress = args?.shouldSavePartialProgress ?: false activity.findViewById