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