diff --git a/.editorconfig b/.editorconfig
index 16f61bea844..474690550a8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,4 +1,4 @@
[*.{kt,kts}]
# possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely)
indent_size=2
-kotlin_imports_layout=idea
+
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 66d46e74f0c..2ae2a26048e 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -5,8 +5,6 @@
-
-
@@ -161,4 +159,4 @@
-
+
\ No newline at end of file
diff --git a/app/.idea/.gitignore b/app/.idea/.gitignore
new file mode 100644
index 00000000000..26d33521af1
--- /dev/null
+++ b/app/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/app/.idea/caches/deviceStreaming.xml b/app/.idea/caches/deviceStreaming.xml
new file mode 100644
index 00000000000..406736c9fa0
--- /dev/null
+++ b/app/.idea/caches/deviceStreaming.xml
@@ -0,0 +1,340 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/gradle.xml b/app/.idea/gradle.xml
new file mode 100644
index 00000000000..89935b50bf3
--- /dev/null
+++ b/app/.idea/gradle.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/migrations.xml b/app/.idea/migrations.xml
new file mode 100644
index 00000000000..f8051a6f973
--- /dev/null
+++ b/app/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/misc.xml b/app/.idea/misc.xml
new file mode 100644
index 00000000000..3040d03eac7
--- /dev/null
+++ b/app/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/vcs.xml b/app/.idea/vcs.xml
new file mode 100644
index 00000000000..6c0b8635858
--- /dev/null
+++ b/app/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/local.properties b/app/local.properties
new file mode 100644
index 00000000000..47cd5740b0e
--- /dev/null
+++ b/app/local.properties
@@ -0,0 +1,8 @@
+## This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Sun Dec 29 14:30:24 IST 2024
+sdk.dir=D\:\\Android
diff --git a/app/src/main/java/org/oppia/android/app/activity/ActivityComponent.kt b/app/src/main/java/org/oppia/android/app/activity/ActivityComponent.kt
index 402161f88ea..c957525c1b9 100644
--- a/app/src/main/java/org/oppia/android/app/activity/ActivityComponent.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/ActivityComponent.kt
@@ -8,4 +8,6 @@ import org.oppia.android.app.utility.datetime.DateTimeUtil
*
* Instances of this subcomponent should be created using [ActivityComponentFactory].
*/
-interface ActivityComponent : AppLanguageActivityInjector, DateTimeUtil.Injector
+interface ActivityComponent :
+ AppLanguageActivityInjector,
+ DateTimeUtil.Injector
diff --git a/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt b/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt
index bf9968544d9..97e9b924944 100644
--- a/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt
@@ -107,12 +107,14 @@ import org.oppia.android.app.walkthrough.WalkthroughActivity
@Subcomponent(
modules = [
ActivityModule::class, FragmentComponentBuilderModule::class,
- ActivityIntentFactoriesModule::class
- ]
+ ActivityIntentFactoriesModule::class,
+ ],
)
@ActivityScope
interface ActivityComponentImpl :
- ActivityComponent, FragmentComponentBuilderInjector, TestActivity.Injector {
+ ActivityComponent,
+ FragmentComponentBuilderInjector,
+ TestActivity.Injector {
@Subcomponent.Builder
interface Builder {
@BindsInstance
@@ -122,108 +124,190 @@ interface ActivityComponentImpl :
}
fun inject(addProfileActivity: AddProfileActivity)
+
fun inject(adminAuthActivity: AdminAuthActivity)
+
fun inject(administratorControlsActivity: AdministratorControlsActivity)
+
fun inject(administratorControlsFragmentTestActivity: AdministratorControlsFragmentTestActivity)
+
fun inject(adminPinActivity: AdminPinActivity)
- fun inject(
- appCompatCheckBoxBindingAdaptersTestActivity:
- AppCompatCheckBoxBindingAdaptersTestActivity
- )
+
+ fun inject(appCompatCheckBoxBindingAdaptersTestActivity: AppCompatCheckBoxBindingAdaptersTestActivity)
+
fun inject(appLanguageActivity: AppLanguageActivity)
+
fun inject(appVersionActivity: AppVersionActivity)
+
fun inject(audioFragmentTestActivity: AudioFragmentTestActivity)
+
fun inject(audioLanguageActivity: AudioLanguageActivity)
+
fun inject(circularProgressAdaptersTestActivity: CircularProgressIndicatorAdaptersTestActivity)
+
fun inject(completedStoryListActivity: CompletedStoryListActivity)
+
fun inject(conceptCardFragmentTestActivity: ConceptCardFragmentTestActivity)
+
fun inject(developerOptionsActivity: DeveloperOptionsActivity)
+
fun inject(developerOptionsTestActivity: DeveloperOptionsTestActivity)
+
fun inject(dragDropTestActivity: DragDropTestActivity)
+
fun inject(drawableBindingAdaptersTestActivity: DrawableBindingAdaptersTestActivity)
+
fun inject(explorationActivity: ExplorationActivity)
+
fun inject(explorationInjectionActivity: ExplorationInjectionActivity)
+
fun inject(explorationTestActivity: ExplorationTestActivity)
+
fun inject(faqListActivity: FAQListActivity)
+
fun inject(faqSingleActivity: FAQSingleActivity)
+
fun inject(forceNetworkTypeActivity: ForceNetworkTypeActivity)
+
fun inject(forceNetworkTypeTestActivity: ForceNetworkTypeTestActivity)
+
fun inject(fractionInputInteractionViewTestActivity: FractionInputInteractionViewTestActivity)
+
fun inject(helpActivity: HelpActivity)
+
fun inject(homeActivity: HomeActivity)
+
fun inject(homeFragmentTestActivity: HomeFragmentTestActivity)
+
fun inject(homeTestActivity: HomeTestActivity)
+
fun inject(htmlParserTestActivity: HtmlParserTestActivity)
+
fun inject(imageRegionSelectionTestActivity: ImageRegionSelectionTestActivity)
+
fun inject(imageViewBindingAdaptersTestActivity: ImageViewBindingAdaptersTestActivity)
+
fun inject(inputInteractionViewTestActivity: InputInteractionViewTestActivity)
+
fun inject(textInputInteractionViewTestActivity: TextInputInteractionViewTestActivity)
+
fun inject(mathExpressionInteractionsViewTestActivity: MathExpressionInteractionsViewTestActivity)
+
fun inject(ratioInputInteractionViewTestActivity: RatioInputInteractionViewTestActivity)
+
fun inject(licenseListActivity: LicenseListActivity)
+
fun inject(licenseTextViewerActivity: LicenseTextViewerActivity)
+
fun inject(listItemLeadingMarginSpanTestActivity: ListItemLeadingMarginSpanTestActivity)
+
fun inject(markChaptersCompletedActivity: MarkChaptersCompletedActivity)
+
fun inject(markChaptersCompletedTestActivity: MarkChaptersCompletedTestActivity)
+
fun inject(markStoriesCompletedActivity: MarkStoriesCompletedActivity)
+
fun inject(markStoriesCompletedTestActivity: MarkStoriesCompletedTestActivity)
+
fun inject(markTopicsCompletedActivity: MarkTopicsCompletedActivity)
+
fun inject(marginBindableAdaptersTestActivity: MarginBindingAdaptersTestActivity)
+
fun inject(markTopicsCompletedTestActivity: MarkTopicsCompletedTestActivity)
+
fun inject(mathExpressionParserActivity: MathExpressionParserActivity)
+
fun inject(myDownloadsActivity: MyDownloadsActivity)
+
fun inject(navigationDrawerTestActivity: NavigationDrawerTestActivity)
+
fun inject(onboardingActivity: OnboardingActivity)
+
fun inject(ongoingTopicListActivity: OngoingTopicListActivity)
+
fun inject(optionActivity: OptionsActivity)
+
fun inject(pinPasswordActivity: PinPasswordActivity)
+
fun inject(policiesActivity: PoliciesActivity)
+
fun inject(policiesFragmentTestActivity: PoliciesFragmentTestActivity)
+
fun inject(profileAndDeviceIdActivity: ProfileAndDeviceIdActivity)
+
fun inject(profileChooserActivity: ProfileChooserActivity)
+
fun inject(profileChooserFragmentTestActivity: ProfileChooserFragmentTestActivity)
+
fun inject(profileEditActivity: ProfileEditActivity)
+
fun inject(profileEditFragmentTestActivity: ProfileEditFragmentTestActivity)
+
fun inject(profileListActivity: ProfileListActivity)
+
fun inject(profilePictureActivity: ProfilePictureActivity)
+
fun inject(profileProgressActivity: ProfileProgressActivity)
+
fun inject(profileRenameActivity: ProfileRenameActivity)
+
fun inject(profileResetPinActivity: ProfileResetPinActivity)
+
fun inject(questionPlayerActivity: QuestionPlayerActivity)
+
fun inject(readingTextSizeActivity: ReadingTextSizeActivity)
+
fun inject(recentlyPlayedActivity: RecentlyPlayedActivity)
+
fun inject(resumeLessonActivity: ResumeLessonActivity)
+
fun inject(revisionCardActivity: RevisionCardActivity)
+
fun inject(splashActivity: SplashActivity)
+
fun inject(splashTestActivity: SplashTestActivity)
- fun inject(
- stateAssemblerMarginBindingAdaptersTestActivity:
- StateAssemblerMarginBindingAdaptersTestActivity
- )
- fun inject(
- stateAssemblerPaddingBindingAdaptersTestActivity:
- StateAssemblerPaddingBindingAdaptersTestActivity
- )
+ fun inject(stateAssemblerMarginBindingAdaptersTestActivity: StateAssemblerMarginBindingAdaptersTestActivity)
+
+ fun inject(stateAssemblerPaddingBindingAdaptersTestActivity: StateAssemblerPaddingBindingAdaptersTestActivity)
fun inject(spotlightFragmentTestActivity: SpotlightFragmentTestActivity)
+
fun inject(stateFragmentTestActivity: StateFragmentTestActivity)
+
fun inject(storyActivity: StoryActivity)
+
fun inject(testFontScaleConfigurationUtilActivity: TestFontScaleConfigurationUtilActivity)
+
fun inject(textViewBindingAdaptersTestActivity: TextViewBindingAdaptersTestActivity)
+
fun inject(thirdPartyDependencyListActivity: ThirdPartyDependencyListActivity)
+
fun inject(topicActivity: TopicActivity)
+
fun inject(topicRevisionTestActivity: TopicRevisionTestActivity)
+
fun inject(topicTestActivity: TopicTestActivity)
+
fun inject(topicTestActivityForStory: TopicTestActivityForStory)
+
fun inject(viewBindingAdaptersTestActivity: ViewBindingAdaptersTestActivity)
+
fun inject(viewEventLogsActivity: ViewEventLogsActivity)
+
fun inject(viewEventLogsTestActivity: ViewEventLogsTestActivity)
+
fun inject(walkthroughActivity: WalkthroughActivity)
+
fun inject(surveyActivity: SurveyActivity)
+
fun inject(colorBindingAdaptersTestActivity: ColorBindingAdaptersTestActivity)
+
fun inject(classroomListActivity: ClassroomListActivity)
+
fun inject(onboardingProfileTypeActivity: OnboardingProfileTypeActivity)
+
fun inject(createProfileActivity: CreateProfileActivity)
+
fun inject(introActivity: IntroActivity)
}
diff --git a/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactories.kt b/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactories.kt
index 078032aabcf..c1fa48d09de 100644
--- a/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactories.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactories.kt
@@ -20,7 +20,11 @@ interface ActivityIntentFactories {
* Returns a new [Intent] to start the topic activity for the specified profile, classroom
* and topic.
*/
- fun createIntent(profileId: ProfileId, classroomId: String, topicId: String): Intent
+ fun createIntent(
+ profileId: ProfileId,
+ classroomId: String,
+ topicId: String,
+ ): Intent
/**
* Returns a new [Intent] to start the topic activity for the specified profile, classroom,
@@ -31,7 +35,7 @@ interface ActivityIntentFactories {
profileId: ProfileId,
classroomId: String,
topicId: String,
- storyId: String
+ storyId: String,
): Intent
}
diff --git a/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactoriesModule.kt b/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactoriesModule.kt
index 8ee9bb4df64..3d666cf33a6 100644
--- a/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactoriesModule.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/ActivityIntentFactoriesModule.kt
@@ -12,11 +12,11 @@ import org.oppia.android.app.topic.TopicActivity
interface ActivityIntentFactoriesModule {
@Binds
fun provideTopicActivityIntentFactory(
- impl: TopicActivity.TopicActivityIntentFactoryImpl
+ impl: TopicActivity.TopicActivityIntentFactoryImpl,
): ActivityIntentFactories.TopicActivityIntentFactory
@Binds
fun provideRecentlyPlayedActivityIntentFactory(
- impl: RecentlyPlayedActivity.RecentlyPlayedActivityIntentFactoryImpl
+ impl: RecentlyPlayedActivity.RecentlyPlayedActivityIntentFactoryImpl,
): ActivityIntentFactories.RecentlyPlayedActivityIntentFactory
}
diff --git a/app/src/main/java/org/oppia/android/app/activity/InjectableAppCompatActivity.kt b/app/src/main/java/org/oppia/android/app/activity/InjectableAppCompatActivity.kt
index 2658f3e3c33..c68be42a26f 100644
--- a/app/src/main/java/org/oppia/android/app/activity/InjectableAppCompatActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/InjectableAppCompatActivity.kt
@@ -12,12 +12,15 @@ import org.oppia.android.app.fragment.FragmentComponentFactory
import org.oppia.android.app.translation.AppLanguageActivityInjector
import org.oppia.android.app.translation.AppLanguageActivityInjectorProvider
import org.oppia.android.app.translation.AppLanguageWatcherMixin
+
/**
* An [AppCompatActivity] that facilitates field injection to child activities and constituent
* fragments that extend [org.oppia.android.app.fragment.InjectableFragment].
*/
abstract class InjectableAppCompatActivity :
- AppCompatActivity(), FragmentComponentFactory, AppLanguageActivityInjectorProvider {
+ AppCompatActivity(),
+ FragmentComponentFactory,
+ AppLanguageActivityInjectorProvider {
/**
* The [ActivityComponent] corresponding to this activity. This cannot be used before
* [attachBaseContext] is called, and can be used to inject lateinit fields in child activities
@@ -26,9 +29,10 @@ abstract class InjectableAppCompatActivity :
lateinit var activityComponent: ActivityComponent
override fun attachBaseContext(newBase: Context?) {
- val applicationContext = checkNotNull(newBase?.applicationContext) {
- "Expected attached Context to have an application context defined."
- }
+ val applicationContext =
+ checkNotNull(newBase?.applicationContext) {
+ "Expected attached Context to have an application context defined."
+ }
onInitializeActivityComponent(applicationContext)
val newConfiguration = onInitializeLocalization(newBase)
super.attachBaseContext(newBase?.createConfigurationContext(newConfiguration))
@@ -39,14 +43,21 @@ abstract class InjectableAppCompatActivity :
super.onCreate(savedInstanceState)
}
- override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
+ override fun onCreate(
+ savedInstanceState: Bundle?,
+ persistentState: PersistableBundle?,
+ ) {
ensureLayoutDirection()
super.onCreate(savedInstanceState, persistentState)
}
override fun createFragmentComponent(fragment: Fragment): FragmentComponent {
val builderInjector = activityComponent as FragmentComponentBuilderInjector
- return builderInjector.getFragmentComponentBuilderProvider().get().setFragment(fragment).build()
+ return builderInjector
+ .getFragmentComponentBuilderProvider()
+ .get()
+ .setFragment(fragment)
+ .build()
}
override fun getAppLanguageActivityInjector(): AppLanguageActivityInjector = activityComponent
@@ -56,9 +67,7 @@ abstract class InjectableAppCompatActivity :
activityComponent = componentFactory.createActivityComponent(this)
}
- private fun onInitializeLocalization(
- newBase: Context?
- ): Configuration {
+ private fun onInitializeLocalization(newBase: Context?): Configuration {
// Given how DataProviders work (i.e. by resolving data races using eventual consistency), it's
// possible to miss some updates in really unlikely situations. No additional work will be done
// to prevent these data races unless they're actually hit by users. It shouldn't, in practice,
diff --git a/app/src/main/java/org/oppia/android/app/activity/InjectableAutoLocalizedAppCompatActivity.kt b/app/src/main/java/org/oppia/android/app/activity/InjectableAutoLocalizedAppCompatActivity.kt
index 38ddbf13cf0..f0448497f0b 100644
--- a/app/src/main/java/org/oppia/android/app/activity/InjectableAutoLocalizedAppCompatActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/InjectableAutoLocalizedAppCompatActivity.kt
@@ -11,7 +11,6 @@ import org.oppia.android.app.translation.AppLanguageWatcherMixin
* user's selected app language.
*/
abstract class InjectableAutoLocalizedAppCompatActivity : InjectableAppCompatActivity() {
-
override fun initializeMixin(appLanguageWatcherMixin: AppLanguageWatcherMixin) {
appLanguageWatcherMixin.initialize(shouldOnlyUseSystemLanguage = false)
}
diff --git a/app/src/main/java/org/oppia/android/app/activity/InjectableSystemLocalizedAppCompatActivity.kt b/app/src/main/java/org/oppia/android/app/activity/InjectableSystemLocalizedAppCompatActivity.kt
index 2bdb8a62f85..aacdd7866e3 100644
--- a/app/src/main/java/org/oppia/android/app/activity/InjectableSystemLocalizedAppCompatActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/InjectableSystemLocalizedAppCompatActivity.kt
@@ -11,7 +11,6 @@ import org.oppia.android.app.translation.AppLanguageWatcherMixin
* device default language.
*/
abstract class InjectableSystemLocalizedAppCompatActivity : InjectableAppCompatActivity() {
-
override fun initializeMixin(appLanguageWatcherMixin: AppLanguageWatcherMixin) {
appLanguageWatcherMixin.initialize(shouldOnlyUseSystemLanguage = true)
}
diff --git a/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouter.kt b/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouter.kt
index 7e4c020e4af..fbd0ee580f8 100644
--- a/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouter.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouter.kt
@@ -10,22 +10,25 @@ import javax.inject.Inject
* A central router that can navigate the user to a specific activity based on a provided
* [DestinationScreen].
*/
-class ActivityRouter @Inject constructor(
- private val activity: AppCompatActivity,
- private val destinationRoutes: Map,
- private val consoleLogger: ConsoleLogger
-) {
- /** Opens the activity corresponding to the specified [destinationScreen]. */
- fun routeToScreen(destinationScreen: DestinationScreen) {
- val routeIntent = destinationRoutes[destinationScreen.destinationScreenCase]
- ?.createIntent(
- activity,
- destinationScreen
- )
- if (routeIntent != null) {
- activity.startActivity(routeIntent)
- } else {
- consoleLogger.w("ActivityRouter", "Destination screen case is not identified.")
+class ActivityRouter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val destinationRoutes: Map,
+ private val consoleLogger: ConsoleLogger,
+ ) {
+ /** Opens the activity corresponding to the specified [destinationScreen]. */
+ fun routeToScreen(destinationScreen: DestinationScreen) {
+ val routeIntent =
+ destinationRoutes[destinationScreen.destinationScreenCase]
+ ?.createIntent(
+ activity,
+ destinationScreen,
+ )
+ if (routeIntent != null) {
+ activity.startActivity(routeIntent)
+ } else {
+ consoleLogger.w("ActivityRouter", "Destination screen case is not identified.")
+ }
}
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouterModule.kt b/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouterModule.kt
index 856cbc37cdd..d17bd6bb3af 100644
--- a/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouterModule.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/route/ActivityRouterModule.kt
@@ -14,17 +14,15 @@ class ActivityRouterModule {
@Provides
@IntoMap
@RouteKey(DestinationScreen.DestinationScreenCase.RECENTLY_PLAYED_ACTIVITY_PARAMS)
- fun provideRecentlyPlayedActivityRoute(): Route {
- return object : Route {
+ fun provideRecentlyPlayedActivityRoute(): Route =
+ object : Route {
override fun createIntent(
context: Context,
- destinationScreen: DestinationScreen
- ): Intent {
- return RecentlyPlayedActivity.createRecentlyPlayedActivityIntent(
+ destinationScreen: DestinationScreen,
+ ): Intent =
+ RecentlyPlayedActivity.createRecentlyPlayedActivityIntent(
context,
- destinationScreen.recentlyPlayedActivityParams
+ destinationScreen.recentlyPlayedActivityParams,
)
- }
}
- }
}
diff --git a/app/src/main/java/org/oppia/android/app/activity/route/Route.kt b/app/src/main/java/org/oppia/android/app/activity/route/Route.kt
index 5b186a09b74..acef9fe2ec5 100644
--- a/app/src/main/java/org/oppia/android/app/activity/route/Route.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/route/Route.kt
@@ -15,6 +15,6 @@ interface Route {
*/
fun createIntent(
context: Context,
- destinationScreen: DestinationScreen
+ destinationScreen: DestinationScreen,
): Intent
}
diff --git a/app/src/main/java/org/oppia/android/app/activity/route/RouteKey.kt b/app/src/main/java/org/oppia/android/app/activity/route/RouteKey.kt
index 12895e1f1e6..5446a5bc278 100644
--- a/app/src/main/java/org/oppia/android/app/activity/route/RouteKey.kt
+++ b/app/src/main/java/org/oppia/android/app/activity/route/RouteKey.kt
@@ -5,4 +5,6 @@ import org.oppia.android.app.model.DestinationScreen
/** Specifies [DestinationScreenCase] which can be used to pass in activity Route. */
@MapKey
-annotation class RouteKey(val value: DestinationScreen.DestinationScreenCase)
+annotation class RouteKey(
+ val value: DestinationScreen.DestinationScreenCase,
+)
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 64beda78f53..5995eedaeef 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
@@ -60,28 +60,30 @@ class AdministratorControlsActivity :
super.onCreate(savedInstanceState)
(activityComponent as ActivityComponentImpl).inject(this)
- val args = savedInstanceState?.getProto(
- ADMINISTRATOR_CONTROLS_ACTIVITY_STATE_KEY,
- AdministratorControlActivityStateBundle.getDefaultInstance()
- )
+ val args =
+ savedInstanceState?.getProto(
+ ADMINISTRATOR_CONTROLS_ACTIVITY_STATE_KEY,
+ AdministratorControlActivityStateBundle.getDefaultInstance(),
+ )
val extraControlsTitle = args?.selectedControlsTitle
isProfileDeletionDialogVisible =
args?.isProfileDeletionDialogVisible ?: false
- lastLoadedFragment = if (savedInstanceState != null) {
- 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
- }
+ lastLoadedFragment =
+ if (savedInstanceState != null) {
+ 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 = args?.selectedProfileId ?: -1
administratorControlsActivityPresenter.handleOnCreate(
extraControlsTitle,
lastLoadedFragment,
selectedProfileId,
- isProfileDeletionDialogVisible
+ isProfileDeletionDialogVisible,
)
title = resourceHandler.getStringInLocale(R.string.administrator_controls)
@@ -91,7 +93,7 @@ class AdministratorControlsActivity :
override fun handleOnBackPressed() {
this@AdministratorControlsActivity.handleBackPress()
}
- }
+ },
)
}
@@ -107,7 +109,10 @@ class AdministratorControlsActivity :
startActivity(ProfileAndDeviceIdActivity.createIntent(this))
}
- override fun loadProfileEdit(profileId: Int, profileName: String) {
+ override fun loadProfileEdit(
+ profileId: Int,
+ profileName: String,
+ ) {
lastLoadedFragment = PROFILE_EDIT_FRAGMENT
administratorControlsActivityPresenter.loadProfileEdit(profileId, profileName)
}
@@ -118,10 +123,11 @@ class AdministratorControlsActivity :
}
companion object {
-
/** Returns an [Intent] to start this activity. */
- fun createAdministratorControlsActivityIntent(context: Context, profileId: ProfileId?): Intent {
-
+ fun createAdministratorControlsActivityIntent(
+ context: Context,
+ profileId: ProfileId?,
+ ): Intent {
val intent = Intent(context, AdministratorControlsActivity::class.java)
intent.decorateWithScreenName(ADMINISTRATOR_CONTROLS_ACTIVITY)
if (profileId != null) {
@@ -134,7 +140,7 @@ class AdministratorControlsActivity :
private fun handleBackPress() {
val fragment =
supportFragmentManager.findFragmentById(
- R.id.administrator_controls_fragment_multipane_placeholder
+ R.id.administrator_controls_fragment_multipane_placeholder,
)
/*
* If the current fragment is ProfileListFragment then the activity should end on back press.
@@ -152,7 +158,7 @@ class AdministratorControlsActivity :
lastLoadedFragment = PROFILE_LIST_FRAGMENT
administratorControlsActivityPresenter
.setExtraControlsTitle(
- resourceHandler.getStringInLocale(R.string.administrator_controls_edit_profiles)
+ resourceHandler.getStringInLocale(R.string.administrator_controls_edit_profiles),
)
administratorControlsActivityPresenter.loadProfileList()
}
@@ -161,7 +167,7 @@ class AdministratorControlsActivity :
lastLoadedFragment = APP_VERSION_FRAGMENT
administratorControlsActivityPresenter
.setExtraControlsTitle(
- resourceHandler.getStringInLocale(R.string.administrator_controls_app_version)
+ resourceHandler.getStringInLocale(R.string.administrator_controls_app_version),
)
administratorControlsActivityPresenter.loadAppVersion()
}
@@ -169,13 +175,14 @@ class AdministratorControlsActivity :
override fun loadLearnerAnalyticsData() {
lastLoadedFragment = PROFILE_AND_DEVICE_ID_FRAGMENT
administratorControlsActivityPresenter.setExtraControlsTitle(
- resourceHandler.getStringInLocale(R.string.profile_and_device_id_activity_title)
+ resourceHandler.getStringInLocale(R.string.profile_and_device_id_activity_title),
)
administratorControlsActivityPresenter.loadLearnerAnalyticsData()
}
override fun showLogoutDialog() {
- LogoutDialogFragment.newInstance()
+ LogoutDialogFragment
+ .newInstance()
.showNow(supportFragmentManager, LogoutDialogFragment.TAG_LOGOUT_DIALOG_FRAGMENT)
}
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 4802fc28ce9..c36cd442f42 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
@@ -20,198 +20,221 @@ import javax.inject.Inject
/** The presenter for [AdministratorControlsActivity]. */
@ActivityScope
-class AdministratorControlsActivityPresenter @Inject constructor(
- private val activity: AppCompatActivity,
- private val resourceHandler: AppLanguageResourceHandler
-) {
- private lateinit var navigationDrawerFragment: NavigationDrawerFragment
- private var isMultipane = false
- private lateinit var binding: AdministratorControlsActivityBinding
-
- private lateinit var lastLoadedFragment: String
- private var selectedProfileId: Int = -1
- private lateinit var extraControlsTitle: String
- private var isProfileDeletionDialogVisible: Boolean = false
-
- /** Initializes the [AdministratorControlsActivity] and sets the navigation drawer. */
- fun handleOnCreate(
- extraControlsTitle: String?,
- lastLoadedFragment: String,
- selectedProfileId: Int,
- isProfileDeletionDialogVisible: Boolean
+class AdministratorControlsActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val resourceHandler: AppLanguageResourceHandler,
) {
- binding = DataBindingUtil.setContentView(
- activity,
- R.layout.administrator_controls_activity
- )
- setUpNavigationDrawer()
- this.lastLoadedFragment = lastLoadedFragment
- this.selectedProfileId = selectedProfileId
- this.isProfileDeletionDialogVisible = isProfileDeletionDialogVisible
- binding.extraControlsTitle?.apply {
- text = extraControlsTitle
- }
- isMultipane = binding.administratorControlsFragmentMultipanePlaceholder != null
- val previousFragment = getAdministratorControlsFragment()
- if (previousFragment != null) {
- activity.supportFragmentManager.beginTransaction().remove(previousFragment).commitNow()
- }
- activity.supportFragmentManager.beginTransaction().add(
- R.id.administrator_controls_fragment_placeholder,
- AdministratorControlsFragment.newInstance(isMultipane)
- ).commitNow()
- if (isMultipane) {
- val adminControlsActivity = activity as AdministratorControlsActivity
- when (lastLoadedFragment) {
- PROFILE_LIST_FRAGMENT -> activity.loadProfileList()
- APP_VERSION_FRAGMENT -> activity.loadAppVersion()
- PROFILE_EDIT_FRAGMENT -> selectedProfileId.let { profileId ->
- if (extraControlsTitle != null) {
- activity.loadProfileEdit(profileId = profileId, profileName = extraControlsTitle)
- if (isProfileDeletionDialogVisible && profileId != 0) {
- val fragment = activity.supportFragmentManager.findFragmentById(
- R.id.administrator_controls_fragment_multipane_placeholder
- )
- (fragment as LoadProfileEditDeletionDialogListener)
- .loadProfileEditDeletionDialog(profileId)
- this.isProfileDeletionDialogVisible = false
+ private lateinit var navigationDrawerFragment: NavigationDrawerFragment
+ private var isMultipane = false
+ private lateinit var binding: AdministratorControlsActivityBinding
+
+ private lateinit var lastLoadedFragment: String
+ private var selectedProfileId: Int = -1
+ private lateinit var extraControlsTitle: String
+ private var isProfileDeletionDialogVisible: Boolean = false
+
+ /** Initializes the [AdministratorControlsActivity] and sets the navigation drawer. */
+ fun handleOnCreate(
+ extraControlsTitle: String?,
+ lastLoadedFragment: String,
+ selectedProfileId: Int,
+ isProfileDeletionDialogVisible: Boolean,
+ ) {
+ binding =
+ DataBindingUtil.setContentView(
+ activity,
+ R.layout.administrator_controls_activity,
+ )
+ setUpNavigationDrawer()
+ this.lastLoadedFragment = lastLoadedFragment
+ this.selectedProfileId = selectedProfileId
+ this.isProfileDeletionDialogVisible = isProfileDeletionDialogVisible
+ binding.extraControlsTitle?.apply {
+ text = extraControlsTitle
+ }
+ isMultipane = binding.administratorControlsFragmentMultipanePlaceholder != null
+ val previousFragment = getAdministratorControlsFragment()
+ if (previousFragment != null) {
+ activity.supportFragmentManager
+ .beginTransaction()
+ .remove(previousFragment)
+ .commitNow()
+ }
+ activity.supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.administrator_controls_fragment_placeholder,
+ AdministratorControlsFragment.newInstance(isMultipane),
+ ).commitNow()
+ if (isMultipane) {
+ val adminControlsActivity = activity as AdministratorControlsActivity
+ when (lastLoadedFragment) {
+ PROFILE_LIST_FRAGMENT -> activity.loadProfileList()
+ APP_VERSION_FRAGMENT -> activity.loadAppVersion()
+ PROFILE_EDIT_FRAGMENT ->
+ selectedProfileId.let { profileId ->
+ if (extraControlsTitle != null) {
+ activity.loadProfileEdit(profileId = profileId, profileName = extraControlsTitle)
+ if (isProfileDeletionDialogVisible && profileId != 0) {
+ val fragment =
+ activity.supportFragmentManager.findFragmentById(
+ R.id.administrator_controls_fragment_multipane_placeholder,
+ )
+ (fragment as LoadProfileEditDeletionDialogListener)
+ .loadProfileEditDeletionDialog(profileId)
+ this.isProfileDeletionDialogVisible = false
+ }
+ }
}
- }
+ PROFILE_AND_DEVICE_ID_FRAGMENT -> adminControlsActivity.loadLearnerAnalyticsData()
}
- PROFILE_AND_DEVICE_ID_FRAGMENT -> adminControlsActivity.loadLearnerAnalyticsData()
+ setBackButtonClickListener()
}
- setBackButtonClickListener()
}
- }
-
- /** Sets up the navigation drawer. */
- private fun setUpNavigationDrawer() {
- val toolbar = binding.administratorControlsActivityToolbar
- activity.setSupportActionBar(toolbar)
- activity.supportActionBar!!.setDisplayShowHomeEnabled(true)
- navigationDrawerFragment = activity
- .supportFragmentManager
- .findFragmentById(
- R.id.administrator_controls_activity_fragment_navigation_drawer
- ) as NavigationDrawerFragment
- navigationDrawerFragment.setUpDrawer(
- binding.administratorControlsActivityDrawerLayout,
- toolbar, /* menuItemId= */ 0
- )
- }
- /** Returns [AdministratorControlsFragment] instance. */
- private fun getAdministratorControlsFragment(): AdministratorControlsFragment? {
- return activity
- .supportFragmentManager
- .findFragmentById(
- R.id.administrator_controls_fragment_placeholder
- ) as AdministratorControlsFragment?
- }
+ /** Sets up the navigation drawer. */
+ private fun setUpNavigationDrawer() {
+ val toolbar = binding.administratorControlsActivityToolbar
+ activity.setSupportActionBar(toolbar)
+ activity.supportActionBar!!.setDisplayShowHomeEnabled(true)
+ navigationDrawerFragment =
+ activity
+ .supportFragmentManager
+ .findFragmentById(
+ R.id.administrator_controls_activity_fragment_navigation_drawer,
+ ) as NavigationDrawerFragment
+ navigationDrawerFragment.setUpDrawer(
+ binding.administratorControlsActivityDrawerLayout,
+ toolbar, // menuItemId=
+ 0,
+ )
+ }
- /** Loads the profile list fragment as the [AdministratorControlsActivity] is started in multipane tablet mode. */
- fun loadProfileList() {
- lastLoadedFragment = PROFILE_LIST_FRAGMENT
- getAdministratorControlsFragment()!!.setSelectedFragment(lastLoadedFragment)
- setMultipaneBackButtonVisibility(View.GONE)
- activity.supportFragmentManager.beginTransaction().replace(
- R.id.administrator_controls_fragment_multipane_placeholder,
- ProfileListFragment.newInstance(isMultipane)
- ).commitNow()
- }
+ /** Returns [AdministratorControlsFragment] instance. */
+ private fun getAdministratorControlsFragment(): AdministratorControlsFragment? =
+ activity
+ .supportFragmentManager
+ .findFragmentById(
+ R.id.administrator_controls_fragment_placeholder,
+ ) as AdministratorControlsFragment?
+
+ /** Loads the profile list fragment as the [AdministratorControlsActivity] is started in multipane tablet mode. */
+ fun loadProfileList() {
+ lastLoadedFragment = PROFILE_LIST_FRAGMENT
+ getAdministratorControlsFragment()!!.setSelectedFragment(lastLoadedFragment)
+ setMultipaneBackButtonVisibility(View.GONE)
+ activity.supportFragmentManager
+ .beginTransaction()
+ .replace(
+ R.id.administrator_controls_fragment_multipane_placeholder,
+ ProfileListFragment.newInstance(isMultipane),
+ ).commitNow()
+ }
- /** Loads the [AppVersionFragment] in the multipane tablet mode. */
- fun loadAppVersion() {
- lastLoadedFragment = APP_VERSION_FRAGMENT
- getAdministratorControlsFragment()!!.setSelectedFragment(lastLoadedFragment)
- setMultipaneBackButtonVisibility(View.GONE)
- activity.supportFragmentManager.beginTransaction().replace(
- R.id.administrator_controls_fragment_multipane_placeholder,
- AppVersionFragment()
- ).commitNow()
- }
+ /** Loads the [AppVersionFragment] in the multipane tablet mode. */
+ fun loadAppVersion() {
+ lastLoadedFragment = APP_VERSION_FRAGMENT
+ getAdministratorControlsFragment()!!.setSelectedFragment(lastLoadedFragment)
+ setMultipaneBackButtonVisibility(View.GONE)
+ activity.supportFragmentManager
+ .beginTransaction()
+ .replace(
+ R.id.administrator_controls_fragment_multipane_placeholder,
+ AppVersionFragment(),
+ ).commitNow()
+ }
- /** Loads the [ProfileAndDeviceIdFragment] in the multipane tablet mode. */
- fun loadLearnerAnalyticsData() {
- lastLoadedFragment = PROFILE_AND_DEVICE_ID_FRAGMENT
- getAdministratorControlsFragment()!!.setSelectedFragment(lastLoadedFragment)
- activity.supportFragmentManager.beginTransaction().replace(
- R.id.administrator_controls_fragment_multipane_placeholder,
- ProfileAndDeviceIdFragment()
- ).commitNow()
- }
+ /** Loads the [ProfileAndDeviceIdFragment] in the multipane tablet mode. */
+ fun loadLearnerAnalyticsData() {
+ lastLoadedFragment = PROFILE_AND_DEVICE_ID_FRAGMENT
+ getAdministratorControlsFragment()!!.setSelectedFragment(lastLoadedFragment)
+ activity.supportFragmentManager
+ .beginTransaction()
+ .replace(
+ R.id.administrator_controls_fragment_multipane_placeholder,
+ ProfileAndDeviceIdFragment(),
+ ).commitNow()
+ }
- /** Loads the [ProfileEditFragment] when the user clicks on a profile in tablet multipane mode. */
- fun loadProfileEdit(profileId: Int, profileName: String) {
- lastLoadedFragment = PROFILE_EDIT_FRAGMENT
- selectedProfileId = profileId
- extraControlsTitle = profileName
- setExtraControlsTitle(extraControlsTitle)
- setMultipaneBackButtonVisibility(View.VISIBLE)
- val fragment = ProfileEditFragment.newInstance(profileId, isMultipane)
- activity.supportFragmentManager.beginTransaction().replace(
- R.id.administrator_controls_fragment_multipane_placeholder,
- fragment
- ).commitNow()
- }
+ /** Loads the [ProfileEditFragment] when the user clicks on a profile in tablet multipane mode. */
+ fun loadProfileEdit(
+ profileId: Int,
+ profileName: String,
+ ) {
+ lastLoadedFragment = PROFILE_EDIT_FRAGMENT
+ selectedProfileId = profileId
+ extraControlsTitle = profileName
+ setExtraControlsTitle(extraControlsTitle)
+ setMultipaneBackButtonVisibility(View.VISIBLE)
+ val fragment = ProfileEditFragment.newInstance(profileId, isMultipane)
+ activity.supportFragmentManager
+ .beginTransaction()
+ .replace(
+ R.id.administrator_controls_fragment_multipane_placeholder,
+ fragment,
+ ).commitNow()
+ }
- /** Sets whether the user has clicked on the profile deletion [ProfileEditDeletionDialogFragment] in tablet mode. */
- fun loadProfileDeletionDialog(profileDeletionDialogVisible: Boolean) {
- isProfileDeletionDialogVisible = profileDeletionDialogVisible
- }
+ /** Sets whether the user has clicked on the profile deletion [ProfileEditDeletionDialogFragment] in tablet mode. */
+ fun loadProfileDeletionDialog(profileDeletionDialogVisible: Boolean) {
+ isProfileDeletionDialogVisible = profileDeletionDialogVisible
+ }
- private fun setMultipaneBackButtonVisibility(visibility: Int) {
- binding.administratorControlsMultipaneOptionsBackButton?.visibility = visibility
- }
+ private fun setMultipaneBackButtonVisibility(visibility: Int) {
+ binding.administratorControlsMultipaneOptionsBackButton?.visibility = visibility
+ }
- /** Handles the back button according to the back stack of fragments. */
- fun handleOnBackPressed() {
- if (isMultipane) {
- setPreviousFragmentOnBackButtonClick()
+ /** Handles the back button according to the back stack of fragments. */
+ fun handleOnBackPressed() {
+ if (isMultipane) {
+ setPreviousFragmentOnBackButtonClick()
+ }
}
- }
- private fun setBackButtonClickListener() {
- binding.administratorControlsMultipaneOptionsBackButton!!.setOnClickListener {
- setPreviousFragmentOnBackButtonClick()
+ private fun setBackButtonClickListener() {
+ binding.administratorControlsMultipaneOptionsBackButton!!.setOnClickListener {
+ setPreviousFragmentOnBackButtonClick()
+ }
}
- }
- private fun setPreviousFragmentOnBackButtonClick() {
- val currentFragment =
- activity.supportFragmentManager.findFragmentById(
- R.id.administrator_controls_fragment_multipane_placeholder
- )
- when (currentFragment) {
- is ProfileEditFragment -> {
- setExtraControlsTitle(
- resourceHandler.getStringInLocale(R.string.administrator_controls_edit_profiles)
+ private fun setPreviousFragmentOnBackButtonClick() {
+ val currentFragment =
+ activity.supportFragmentManager.findFragmentById(
+ R.id.administrator_controls_fragment_multipane_placeholder,
)
- loadProfileList()
+ when (currentFragment) {
+ is ProfileEditFragment -> {
+ setExtraControlsTitle(
+ resourceHandler.getStringInLocale(R.string.administrator_controls_edit_profiles),
+ )
+ loadProfileList()
+ }
}
}
- }
- /** Sets the title of the extra controls in multipane tablet mode. */
- fun setExtraControlsTitle(title: String) {
- binding.extraControlsTitle?.text = title
- }
+ /** Sets the title of the extra controls in multipane tablet mode. */
+ fun setExtraControlsTitle(title: String) {
+ binding.extraControlsTitle?.text = title
+ }
- /** Saves the state of the views on configuration changes. */
- fun handleOnSaveInstanceState(outState: Bundle) {
- val titleTextView = binding.extraControlsTitle
- 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)
+ /** Saves the state of the views on configuration changes. */
+ fun handleOnSaveInstanceState(outState: Bundle) {
+ val titleTextView = binding.extraControlsTitle
+ 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 095316c008a..d400bfca561 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
@@ -21,15 +21,15 @@ class AdministratorControlsFragment : InjectableFragment() {
lateinit var administratorControlsFragmentPresenter: AdministratorControlsFragmentPresenter
companion object {
-
/** Creates a new instance of [AdministratorControlsFragment]. */
fun newInstance(isMultipane: Boolean): AdministratorControlsFragment {
val args =
AdministratorControlsFragmentArguments.newBuilder().setIsMultipane(isMultipane).build()
return AdministratorControlsFragment().apply {
- arguments = Bundle().apply {
- putProto(ADMINISTRATOR_CONTROLS_FRAGMENT_ARGUMENTS_KEY, args)
- }
+ arguments =
+ Bundle().apply {
+ putProto(ADMINISTRATOR_CONTROLS_FRAGMENT_ARGUMENTS_KEY, args)
+ }
}
}
}
@@ -42,16 +42,17 @@ class AdministratorControlsFragment : InjectableFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
+ savedInstanceState: Bundle?,
): View? {
val arguments =
checkNotNull(arguments) {
"Expected arguments to be passed to AdministratorControlsFragment"
}
- val args = arguments.getProto(
- ADMINISTRATOR_CONTROLS_FRAGMENT_ARGUMENTS_KEY,
- AdministratorControlsFragmentArguments.getDefaultInstance()
- )
+ 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 843b1dbc2fe..42e44a5ce8b 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
@@ -29,56 +29,58 @@ import javax.inject.Inject
/** The presenter for [AdministratorControlsFragment]. */
@FragmentScope
-class AdministratorControlsFragmentPresenter @Inject constructor(
- private val activity: AppCompatActivity,
- private val fragment: Fragment,
- private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory
-) {
- private lateinit var binding: AdministratorControlsFragmentBinding
- private lateinit var linearLayoutManager: LinearLayoutManager
- private var internalProfileId: Int = -1
- private lateinit var profileId: ProfileId
-
+class AdministratorControlsFragmentPresenter
@Inject
- lateinit var administratorControlsViewModel: AdministratorControlsViewModel
-
- /** Initializes and creates the views for the [AdministratorControlsFragment]. */
- fun handleCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- isMultipane: Boolean
- ): View? {
- binding =
- AdministratorControlsFragmentBinding
- .inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
-
- internalProfileId = activity.intent.extractCurrentUserProfileId().internalId
- profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build()
- administratorControlsViewModel.setProfileId(profileId)
-
- linearLayoutManager = LinearLayoutManager(activity.applicationContext)
-
- binding.administratorControlsList.apply {
- layoutManager = linearLayoutManager
- adapter = createRecyclerViewAdapter(isMultipane)
- }
-
- binding.apply {
- this.viewModel = administratorControlsViewModel
- this.lifecycleOwner = fragment
+ constructor(
+ private val activity: AppCompatActivity,
+ private val fragment: Fragment,
+ private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory,
+ ) {
+ private lateinit var binding: AdministratorControlsFragmentBinding
+ private lateinit var linearLayoutManager: LinearLayoutManager
+ private var internalProfileId: Int = -1
+ private lateinit var profileId: ProfileId
+
+ @Inject
+ lateinit var administratorControlsViewModel: AdministratorControlsViewModel
+
+ /** Initializes and creates the views for the [AdministratorControlsFragment]. */
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ isMultipane: Boolean,
+ ): View? {
+ binding =
+ AdministratorControlsFragmentBinding
+ .inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
+
+ internalProfileId = activity.intent.extractCurrentUserProfileId().internalId
+ profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build()
+ administratorControlsViewModel.setProfileId(profileId)
+
+ linearLayoutManager = LinearLayoutManager(activity.applicationContext)
+
+ binding.administratorControlsList.apply {
+ layoutManager = linearLayoutManager
+ adapter = createRecyclerViewAdapter(isMultipane)
+ }
+
+ binding.apply {
+ this.viewModel = administratorControlsViewModel
+ this.lifecycleOwner = fragment
+ }
+
+ return binding.root
}
- return binding.root
- }
-
- /** Returns the recycler view adapter for the controls panel in administrator controls fragment. */
- private fun createRecyclerViewAdapter(isMultipane: Boolean):
- BindableAdapter {
- return multiTypeBuilderFactory
+ /** Returns the recycler view adapter for the controls panel in administrator controls fragment. */
+ private fun createRecyclerViewAdapter(isMultipane: Boolean): BindableAdapter =
+ multiTypeBuilderFactory
.create { viewModel ->
viewModel.isMultipane.set(isMultipane)
when (viewModel) {
@@ -108,105 +110,96 @@ class AdministratorControlsFragmentPresenter @Inject constructor(
}
else -> throw IllegalArgumentException("Encountered unexpected view model: $viewModel")
}
- }
- .registerViewDataBinder(
+ }.registerViewDataBinder(
viewType = ViewType.VIEW_TYPE_GENERAL,
inflateDataBinding = AdministratorControlsGeneralViewBinding::inflate,
setViewModel = AdministratorControlsGeneralViewBinding::setViewModel,
- transformViewModel = { it as AdministratorControlsGeneralViewModel }
- )
- .registerViewDataBinder(
+ transformViewModel = { it as AdministratorControlsGeneralViewModel },
+ ).registerViewDataBinder(
viewType = ViewType.VIEW_TYPE_PROFILE,
inflateDataBinding = AdministratorControlsProfileViewBinding::inflate,
setViewModel = this::bindProfileList,
- transformViewModel = { it as AdministratorControlsProfileViewModel }
- )
- .registerViewDataBinder(
+ transformViewModel = { it as AdministratorControlsProfileViewModel },
+ ).registerViewDataBinder(
viewType = ViewType.VIEW_TYPE_LEARNER_ANALYTICS,
inflateDataBinding = AdministratorControlsLearnerAnalyticsViewBinding::inflate,
setViewModel = this::bindLearnerAnalytics,
- transformViewModel = { it as AdministratorControlsProfileAndDeviceIdViewModel }
- )
- .registerViewDataBinder(
+ transformViewModel = { it as AdministratorControlsProfileAndDeviceIdViewModel },
+ ).registerViewDataBinder(
viewType = ViewType.VIEW_TYPE_DOWNLOAD_PERMISSIONS,
inflateDataBinding = AdministratorControlsDownloadPermissionsViewBinding::inflate,
setViewModel = AdministratorControlsDownloadPermissionsViewBinding::setViewModel,
- transformViewModel = { it as AdministratorControlsDownloadPermissionsViewModel }
- )
- .registerViewDataBinder(
+ transformViewModel = { it as AdministratorControlsDownloadPermissionsViewModel },
+ ).registerViewDataBinder(
viewType = ViewType.VIEW_TYPE_APP_INFORMATION,
inflateDataBinding = AdministratorControlsAppInformationViewBinding::inflate,
setViewModel = this::bindAppVersion,
- transformViewModel = { it as AdministratorControlsAppInformationViewModel }
- )
- .registerViewDataBinder(
+ transformViewModel = { it as AdministratorControlsAppInformationViewModel },
+ ).registerViewDataBinder(
viewType = ViewType.VIEW_TYPE_ACCOUNT_ACTIONS,
inflateDataBinding = AdministratorControlsAccountActionsViewBinding::inflate,
setViewModel = AdministratorControlsAccountActionsViewBinding::setViewModel,
- transformViewModel = { it as AdministratorControlsAccountActionsViewModel }
- )
- .build()
+ transformViewModel = { it as AdministratorControlsAccountActionsViewModel },
+ ).build()
+
+ /** Binds the profile list to the view. */
+ private fun bindProfileList(
+ binding: AdministratorControlsProfileViewBinding,
+ model: AdministratorControlsProfileViewModel,
+ ) {
+ binding.commonViewModel = administratorControlsViewModel
+ binding.viewModel = model
}
- /** Binds the profile list to the view. */
- private fun bindProfileList(
- binding: AdministratorControlsProfileViewBinding,
- model: AdministratorControlsProfileViewModel
- ) {
- binding.commonViewModel = administratorControlsViewModel
- binding.viewModel = model
- }
-
- /** Binds the app version to the view. */
- private fun bindAppVersion(
- binding: AdministratorControlsAppInformationViewBinding,
- model: AdministratorControlsAppInformationViewModel
- ) {
- binding.commonViewModel = administratorControlsViewModel
- binding.viewModel = model
- }
-
- private fun bindLearnerAnalytics(
- binding: AdministratorControlsLearnerAnalyticsViewBinding,
- model: AdministratorControlsProfileAndDeviceIdViewModel
- ) {
- binding.commonViewModel = administratorControlsViewModel
- binding.viewModel = model
- }
+ /** Binds the app version to the view. */
+ private fun bindAppVersion(
+ binding: AdministratorControlsAppInformationViewBinding,
+ model: AdministratorControlsAppInformationViewModel,
+ ) {
+ binding.commonViewModel = administratorControlsViewModel
+ binding.viewModel = model
+ }
- /** Sets the selected fragment Argument as the selected fragment in the view model. */
- fun setSelectedFragment(selectedFragment: String) {
- administratorControlsViewModel.selectedFragmentIndex.set(
- getSelectedFragmentIndex(selectedFragment)
- )
- }
+ private fun bindLearnerAnalytics(
+ binding: AdministratorControlsLearnerAnalyticsViewBinding,
+ model: AdministratorControlsProfileAndDeviceIdViewModel,
+ ) {
+ binding.commonViewModel = administratorControlsViewModel
+ binding.viewModel = model
+ }
- private fun getSelectedFragmentIndex(selectedFragment: String): Int {
- return when (selectedFragment) {
- PROFILE_LIST_FRAGMENT -> 1
- PROFILE_AND_DEVICE_ID_FRAGMENT -> 2
- APP_VERSION_FRAGMENT -> 4
- else -> throw InvalidParameterException("Not a valid fragment in getSelectedFragmentIndex.")
+ /** Sets the selected fragment Argument as the selected fragment in the view model. */
+ fun setSelectedFragment(selectedFragment: String) {
+ administratorControlsViewModel.selectedFragmentIndex.set(
+ getSelectedFragmentIndex(selectedFragment),
+ )
}
- }
- private enum class ViewType {
- /** Represents [View] for the general section. */
- VIEW_TYPE_GENERAL,
+ private fun getSelectedFragmentIndex(selectedFragment: String): Int =
+ when (selectedFragment) {
+ PROFILE_LIST_FRAGMENT -> 1
+ PROFILE_AND_DEVICE_ID_FRAGMENT -> 2
+ APP_VERSION_FRAGMENT -> 4
+ else -> throw InvalidParameterException("Not a valid fragment in getSelectedFragmentIndex.")
+ }
- /** Represents [View] for the profile section. */
- VIEW_TYPE_PROFILE,
+ private enum class ViewType {
+ /** Represents [View] for the general section. */
+ VIEW_TYPE_GENERAL,
- /** Represents [View] for the download permissions section. */
- VIEW_TYPE_DOWNLOAD_PERMISSIONS,
+ /** Represents [View] for the profile section. */
+ VIEW_TYPE_PROFILE,
- /** Represents [View] for the app information section. */
- VIEW_TYPE_APP_INFORMATION,
+ /** Represents [View] for the download permissions section. */
+ VIEW_TYPE_DOWNLOAD_PERMISSIONS,
- /** Represents [View] for the account actions section. */
- VIEW_TYPE_ACCOUNT_ACTIONS,
+ /** Represents [View] for the app information section. */
+ VIEW_TYPE_APP_INFORMATION,
- /** Represents [View] for the learner analytics section. */
- VIEW_TYPE_LEARNER_ANALYTICS
+ /** 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/administratorcontrols/AdministratorControlsViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsViewModel.kt
index 8f6779dfeb8..13b095a1f4c 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsViewModel.kt
@@ -28,98 +28,95 @@ import javax.inject.Inject
/** [ViewModel] for [AdministratorControlsFragment]. */
@FragmentScope
-class AdministratorControlsViewModel @Inject constructor(
- private val activity: AppCompatActivity,
- private val fragment: Fragment,
- private val oppiaLogger: OppiaLogger,
- private val profileManagementController: ProfileManagementController,
- @EnableEditAccountsOptionsUi
- private val enableEditAccountsOptionsUi: PlatformParameterValue,
- @EnableLearnerStudyAnalytics
- private val enableLearnerStudyAnalytics: PlatformParameterValue,
- @EnableDownloadsSupport private val enableDownloadsSupport: PlatformParameterValue
-) {
- private val routeToProfileListListener = activity as RouteToProfileListListener
- private val loadProfileListListener = activity as LoadProfileListListener
- private val showLogoutDialogListener = activity as ShowLogoutDialogListener
- private lateinit var userProfileId: ProfileId
+class AdministratorControlsViewModel
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val fragment: Fragment,
+ private val oppiaLogger: OppiaLogger,
+ private val profileManagementController: ProfileManagementController,
+ @EnableEditAccountsOptionsUi
+ private val enableEditAccountsOptionsUi: PlatformParameterValue,
+ @EnableLearnerStudyAnalytics
+ private val enableLearnerStudyAnalytics: PlatformParameterValue,
+ @EnableDownloadsSupport private val enableDownloadsSupport: PlatformParameterValue,
+ ) {
+ private val routeToProfileListListener = activity as RouteToProfileListListener
+ private val loadProfileListListener = activity as LoadProfileListListener
+ private val showLogoutDialogListener = activity as ShowLogoutDialogListener
+ private lateinit var userProfileId: ProfileId
- /** Sets the index for the currently selected fragment. */
- val selectedFragmentIndex = ObservableField(1)
+ /** Sets the index for the currently selected fragment. */
+ val selectedFragmentIndex = ObservableField(1)
- private val deviceSettingsLiveData: LiveData by lazy {
- Transformations.map(
- profileManagementController.getDeviceSettings().toLiveData(),
- ::processGetDeviceSettingsResult
- )
- }
+ private val deviceSettingsLiveData: LiveData by lazy {
+ Transformations.map(
+ profileManagementController.getDeviceSettings().toLiveData(),
+ ::processGetDeviceSettingsResult,
+ )
+ }
- /** This temporarily stores the list of the controls in the [AdministratorControlsFragment]. */
- val administratorControlsLiveData: LiveData> by lazy {
- Transformations.map(deviceSettingsLiveData, ::processAdministratorControlsList)
- }
+ /** This temporarily stores the list of the controls in the [AdministratorControlsFragment]. */
+ val administratorControlsLiveData: LiveData> by lazy {
+ Transformations.map(deviceSettingsLiveData, ::processAdministratorControlsList)
+ }
- private fun processGetDeviceSettingsResult(
- deviceSettingsResult: AsyncResult
- ): DeviceSettings {
- return when (deviceSettingsResult) {
- is AsyncResult.Failure -> {
- oppiaLogger.e(
- "AdministratorControlsFragment",
- "Failed to retrieve profile",
- deviceSettingsResult.error
- )
- DeviceSettings.getDefaultInstance()
+ private fun processGetDeviceSettingsResult(deviceSettingsResult: AsyncResult): DeviceSettings =
+ when (deviceSettingsResult) {
+ is AsyncResult.Failure -> {
+ oppiaLogger.e(
+ "AdministratorControlsFragment",
+ "Failed to retrieve profile",
+ deviceSettingsResult.error,
+ )
+ DeviceSettings.getDefaultInstance()
+ }
+ is AsyncResult.Pending -> DeviceSettings.getDefaultInstance()
+ is AsyncResult.Success -> deviceSettingsResult.value
}
- is AsyncResult.Pending -> DeviceSettings.getDefaultInstance()
- is AsyncResult.Success -> deviceSettingsResult.value
- }
- }
- private fun processAdministratorControlsList(
- deviceSettings: DeviceSettings
- ): List {
- val itemViewModelList = mutableListOf()
+ private fun processAdministratorControlsList(deviceSettings: DeviceSettings): List {
+ val itemViewModelList = mutableListOf()
- if (enableEditAccountsOptionsUi.value) {
- itemViewModelList.add(AdministratorControlsGeneralViewModel())
- }
+ if (enableEditAccountsOptionsUi.value) {
+ itemViewModelList.add(AdministratorControlsGeneralViewModel())
+ }
- itemViewModelList.add(
- AdministratorControlsProfileViewModel(
- routeToProfileListListener,
- loadProfileListListener
+ itemViewModelList.add(
+ AdministratorControlsProfileViewModel(
+ routeToProfileListListener,
+ loadProfileListListener,
+ ),
)
- )
- // TODO(#4345): Add tests to verify this behavior both for the study flag being on & off.
- if (enableLearnerStudyAnalytics.value) {
- itemViewModelList.add(AdministratorControlsProfileAndDeviceIdViewModel(activity))
- }
+ // TODO(#4345): Add tests to verify this behavior both for the study flag being on & off.
+ if (enableLearnerStudyAnalytics.value) {
+ itemViewModelList.add(AdministratorControlsProfileAndDeviceIdViewModel(activity))
+ }
- if (enableDownloadsSupport.value) {
- itemViewModelList.add(
- AdministratorControlsDownloadPermissionsViewModel(
- fragment,
- oppiaLogger,
- profileManagementController,
- userProfileId,
- deviceSettings
+ if (enableDownloadsSupport.value) {
+ itemViewModelList.add(
+ AdministratorControlsDownloadPermissionsViewModel(
+ fragment,
+ oppiaLogger,
+ profileManagementController,
+ userProfileId,
+ deviceSettings,
+ ),
)
- )
- }
+ }
- itemViewModelList.add(AdministratorControlsAppInformationViewModel(activity))
- itemViewModelList.add(
- AdministratorControlsAccountActionsViewModel(
- showLogoutDialogListener
+ itemViewModelList.add(AdministratorControlsAppInformationViewModel(activity))
+ itemViewModelList.add(
+ AdministratorControlsAccountActionsViewModel(
+ showLogoutDialogListener,
+ ),
)
- )
- return itemViewModelList
- }
+ return itemViewModelList
+ }
- /** Sets the user profile id. */
- fun setProfileId(profileId: ProfileId) {
- userProfileId = profileId
+ /** Sets the user profile id. */
+ fun setProfileId(profileId: ProfileId) {
+ userProfileId = profileId
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/LoadProfileEditListener.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/LoadProfileEditListener.kt
index 0a8157990e7..25398f620ab 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/LoadProfileEditListener.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/LoadProfileEditListener.kt
@@ -3,5 +3,8 @@ package org.oppia.android.app.administratorcontrols
/** Listener for when an activity should load [ProfileEditFragment]. */
interface LoadProfileEditListener {
/** Inflates [ProfileEditFragment] as part of a tablet mode a multipane fragment. */
- fun loadProfileEdit(profileId: Int, profileName: String)
+ fun loadProfileEdit(
+ profileId: Int,
+ profileName: String,
+ )
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/LogoutDialogFragment.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/LogoutDialogFragment.kt
index f53e3244ee3..a826387c2f7 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/LogoutDialogFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/LogoutDialogFragment.kt
@@ -12,15 +12,12 @@ import org.oppia.android.app.profile.ProfileChooserActivity
/** [DialogFragment] that gives option to either cancel or log out from current profile. */
class LogoutDialogFragment : InjectableDialogFragment() {
-
companion object {
/** [String] key to access [LogoutDialogFragment]. */
const val TAG_LOGOUT_DIALOG_FRAGMENT = "TAG_LOGOUT_DIALOG_FRAGMENT"
/** Returns a new [LogoutDialogFragment] instance. */
- fun newInstance(): LogoutDialogFragment {
- return LogoutDialogFragment()
- }
+ fun newInstance(): LogoutDialogFragment = LogoutDialogFragment()
}
override fun onAttach(context: Context) {
@@ -28,15 +25,14 @@ class LogoutDialogFragment : InjectableDialogFragment() {
(fragmentComponent as FragmentComponentImpl).inject(this)
}
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- return AlertDialog.Builder(requireContext(), R.style.OppiaAlertDialogTheme)
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
+ AlertDialog
+ .Builder(requireContext(), R.style.OppiaAlertDialogTheme)
.setMessage(R.string.log_out_dialog_message)
.setNegativeButton(R.string.log_out_dialog_cancel_button) { dialog, _ ->
dialog.dismiss()
- }
- .setPositiveButton(R.string.log_out_dialog_okay_button) { _, _ ->
+ }.setPositiveButton(R.string.log_out_dialog_okay_button) { _, _ ->
val intent = ProfileChooserActivity.createProfileChooserActivity(activity!!)
startActivity(intent)
}.create()
- }
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/ShowLogoutDialogListener.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/ShowLogoutDialogListener.kt
index 3e37c1ea2b6..7e6de879ad0 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/ShowLogoutDialogListener.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/ShowLogoutDialogListener.kt
@@ -2,7 +2,6 @@ package org.oppia.android.app.administratorcontrols
/** Listener for when the logout dialog should be shown. */
interface ShowLogoutDialogListener {
-
/** Shows the logout dialog. This cannot be called at times when there's an ongoing fragment transaction. */
fun showLogoutDialog()
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt
index bdcb90e20aa..694fb3d2221 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt
@@ -4,7 +4,7 @@ import org.oppia.android.app.administratorcontrols.ShowLogoutDialogListener
/** [ViewModel] for the recycler view in [AdministratorControlsFragment]. */
class AdministratorControlsAccountActionsViewModel(
- private val showLogoutDialogListener: ShowLogoutDialogListener
+ private val showLogoutDialogListener: ShowLogoutDialogListener,
) : AdministratorControlsItemViewModel() {
/** Called when user clicks logout on [AdministratorControlsActivity]. */
fun onLogOutClicked() {
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAppInformationViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAppInformationViewModel.kt
index fc94c665256..bd73613cf42 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAppInformationViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAppInformationViewModel.kt
@@ -6,9 +6,8 @@ import org.oppia.android.app.administratorcontrols.RouteToAppVersionListener
/** [ViewModel] for the recycler view in [AdministratorControlsFragment]. */
class AdministratorControlsAppInformationViewModel(
- activity: AppCompatActivity
+ activity: AppCompatActivity,
) : AdministratorControlsItemViewModel() {
-
private val routeToAppVersionListener = activity as RouteToAppVersionListener
private val loadAppVersionListener = activity as LoadAppVersionListener
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsDownloadPermissionsViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsDownloadPermissionsViewModel.kt
index c6a91549f82..866fdbbf555 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsDownloadPermissionsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsDownloadPermissionsViewModel.kt
@@ -16,23 +16,25 @@ class AdministratorControlsDownloadPermissionsViewModel(
private val oppiaLogger: OppiaLogger,
private val profileManagementController: ProfileManagementController,
private val userProfileId: ProfileId,
- deviceSettings: DeviceSettings
+ deviceSettings: DeviceSettings,
) : AdministratorControlsItemViewModel() {
/**
* [Boolean] observable value showing if topic downloads and updates should happen only on Wifi.
*/
val isTopicWifiUpdatePermission =
ObservableField(deviceSettings.allowDownloadAndUpdateOnlyOnWifi)
+
/** [Boolean] observable value showing if topic updates should happen automatically. */
val isTopicAutoUpdatePermission =
ObservableField(deviceSettings.automaticallyUpdateTopics)
/** Called when topic wifi update permission changes. */
fun onTopicWifiUpdatePermissionChanged() {
- profileManagementController.updateWifiPermissionDeviceSettings(
- userProfileId,
- !isTopicWifiUpdatePermission.get()!!
- ).toLiveData()
+ profileManagementController
+ .updateWifiPermissionDeviceSettings(
+ userProfileId,
+ !isTopicWifiUpdatePermission.get()!!,
+ ).toLiveData()
.observe(
fragment,
Observer {
@@ -40,29 +42,31 @@ class AdministratorControlsDownloadPermissionsViewModel(
oppiaLogger.e(
"AdministratorControlsFragment",
"Failed to update topic update on wifi permission",
- it.error
+ it.error,
)
}
- }
+ },
)
}
/** Called when topic auto update permission changes. */
fun onTopicAutoUpdatePermissionChanged() {
- profileManagementController.updateTopicAutomaticallyPermissionDeviceSettings(
- userProfileId,
- !isTopicAutoUpdatePermission.get()!!
- ).toLiveData().observe(
- fragment,
- Observer {
- if (it is AsyncResult.Failure) {
- oppiaLogger.e(
- "AdministratorControlsFragment",
- "Failed to update topic auto update permission",
- it.error
- )
- }
- }
- )
+ profileManagementController
+ .updateTopicAutomaticallyPermissionDeviceSettings(
+ userProfileId,
+ !isTopicAutoUpdatePermission.get()!!,
+ ).toLiveData()
+ .observe(
+ fragment,
+ Observer {
+ if (it is AsyncResult.Failure) {
+ oppiaLogger.e(
+ "AdministratorControlsFragment",
+ "Failed to update topic auto update permission",
+ it.error,
+ )
+ }
+ },
+ )
}
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsItemViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsItemViewModel.kt
index add6384478b..1290d887775 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsItemViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsItemViewModel.kt
@@ -10,6 +10,7 @@ import org.oppia.android.app.viewmodel.ObservableViewModel
abstract class AdministratorControlsItemViewModel : ObservableViewModel() {
/** [Boolean] observable value showing if [View] is multipane. */
val isMultipane = ObservableField(false)
+
/** [Int] representing the index of items bound in [AdministratorControlsActivity]. */
val itemIndex = ObservableField()
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileAndDeviceIdViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileAndDeviceIdViewModel.kt
index 450175e8c49..888f17806e3 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileAndDeviceIdViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileAndDeviceIdViewModel.kt
@@ -9,9 +9,8 @@ import org.oppia.android.app.administratorcontrols.RouteToLearnerAnalyticsListen
* analytics screen.
*/
class AdministratorControlsProfileAndDeviceIdViewModel(
- activity: AppCompatActivity
+ activity: AppCompatActivity,
) : AdministratorControlsItemViewModel() {
-
private val routeToLearnerAnalyticsListener = activity as RouteToLearnerAnalyticsListener
private val loadLearnerAnalyticsListener = activity as LoadLearnerAnalyticsListener
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileViewModel.kt
index b1715b0f078..aa574b61cae 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileViewModel.kt
@@ -6,9 +6,8 @@ import org.oppia.android.app.administratorcontrols.RouteToProfileListListener
/** [ViewModel] for the recycler view in [AdministratorControlsFragment]. */
class AdministratorControlsProfileViewModel(
private val routeToProfileListListener: RouteToProfileListListener,
- private val loadProfileListListener: LoadProfileListListener
+ private val loadProfileListListener: LoadProfileListListener,
) : AdministratorControlsItemViewModel() {
-
/** Called when a user clicks on EditProfiles in [AdministratorControlsActivity]. */
fun onEditProfilesClicked() {
if (isMultipane.get()!!) {
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt
index f78d770df1d..381f51c17a4 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt
@@ -27,7 +27,7 @@ class AppVersionActivity : InjectableAutoLocalizedAppCompatActivity() {
override fun handleOnBackPressed() {
finish()
}
- }
+ },
)
}
@@ -40,10 +40,9 @@ class AppVersionActivity : InjectableAutoLocalizedAppCompatActivity() {
companion object {
/** Returns an [Intent] to start this activity. */
- fun createAppVersionActivityIntent(context: Context): Intent {
- return Intent(context, AppVersionActivity::class.java).apply {
+ fun createAppVersionActivityIntent(context: Context): Intent =
+ Intent(context, AppVersionActivity::class.java).apply {
decorateWithScreenName(APP_VERSION_ACTIVITY)
}
- }
}
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt
index ffdd6a2cde4..35833450430 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt
@@ -8,29 +8,33 @@ import javax.inject.Inject
/** The presenter for [AppVersionActivity]. */
@ActivityScope
-class AppVersionActivityPresenter @Inject constructor(private val activity: AppCompatActivity) {
-
- /** Initializes the [AppVersionActivity] views and binds [AppVersionFragment]. */
- fun handleOnCreate() {
- activity.setContentView(R.layout.app_version_activity)
- setToolbar()
- if (getAppVersionFragment() == null) {
- activity.supportFragmentManager.beginTransaction().add(
- R.id.app_version_fragment_placeholder,
- AppVersionFragment()
- ).commitNow()
+class AppVersionActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ ) {
+ /** Initializes the [AppVersionActivity] views and binds [AppVersionFragment]. */
+ fun handleOnCreate() {
+ activity.setContentView(R.layout.app_version_activity)
+ setToolbar()
+ if (getAppVersionFragment() == null) {
+ activity.supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.app_version_fragment_placeholder,
+ AppVersionFragment(),
+ ).commitNow()
+ }
}
- }
- private fun setToolbar() {
- val appVersionToolbar: Toolbar = activity.findViewById(R.id.app_version_toolbar) as Toolbar
- activity.setSupportActionBar(appVersionToolbar)
- activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
- }
+ private fun setToolbar() {
+ val appVersionToolbar: Toolbar = activity.findViewById(R.id.app_version_toolbar) as Toolbar
+ activity.setSupportActionBar(appVersionToolbar)
+ activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
- private fun getAppVersionFragment(): AppVersionFragment? {
- return activity
- .supportFragmentManager
- .findFragmentById(R.id.app_version_fragment_placeholder) as AppVersionFragment?
+ private fun getAppVersionFragment(): AppVersionFragment? =
+ activity
+ .supportFragmentManager
+ .findFragmentById(R.id.app_version_fragment_placeholder) as AppVersionFragment?
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragment.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragment.kt
index f2903a2a696..95b823978ea 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragment.kt
@@ -22,8 +22,6 @@ class AppVersionFragment : InjectableFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return appVersionFragmentPresenter.handleCreateView(inflater, container)
- }
+ savedInstanceState: Bundle?,
+ ): View? = appVersionFragmentPresenter.handleCreateView(inflater, container)
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragmentPresenter.kt
index 125841f8f9f..952e9849d58 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionFragmentPresenter.kt
@@ -10,23 +10,30 @@ import javax.inject.Inject
/** The presenter for [AppVersionFragment]. */
@FragmentScope
-class AppVersionFragmentPresenter @Inject constructor(
- private val fragment: Fragment,
- private val appVersionViewModel: AppVersionViewModel
-) {
- private lateinit var binding: AppVersionFragmentBinding
+class AppVersionFragmentPresenter
+ @Inject
+ constructor(
+ private val fragment: Fragment,
+ private val appVersionViewModel: AppVersionViewModel,
+ ) {
+ private lateinit var binding: AppVersionFragmentBinding
- /** Initializes and creates the views for the [AppVersionFragment]. */
- fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
- binding = AppVersionFragmentBinding.inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
- binding.let {
- it.lifecycleOwner = fragment
- it.viewModel = appVersionViewModel
+ /** Initializes and creates the views for the [AppVersionFragment]. */
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ ): View? {
+ binding =
+ AppVersionFragmentBinding.inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
+ binding.let {
+ it.lifecycleOwner = fragment
+ it.viewModel = appVersionViewModel
+ }
+ return binding.root
}
- return binding.root
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt
index b9d77464349..ec78c8c27e0 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt
@@ -12,24 +12,24 @@ import javax.inject.Inject
/** [ViewModel] for [AppVersionFragment]. */
@FragmentScope
-class AppVersionViewModel @Inject constructor(
- private val resourceHandler: AppLanguageResourceHandler,
- context: Context
-) : ObservableViewModel() {
+class AppVersionViewModel
+ @Inject
+ constructor(
+ private val resourceHandler: AppLanguageResourceHandler,
+ context: Context,
+ ) : ObservableViewModel() {
+ private val versionName: String = context.getVersionName()
+ private val lastUpdateDateTime = context.getLastUpdateTime()
- private val versionName: String = context.getVersionName()
- private val lastUpdateDateTime = context.getLastUpdateTime()
+ /** Returns a localized, human-readable app version name. */
+ fun computeVersionNameText(): String = resourceHandler.getStringInLocaleWithWrapping(R.string.app_version_name, versionName)
- /** Returns a localized, human-readable app version name. */
- fun computeVersionNameText(): String =
- resourceHandler.getStringInLocaleWithWrapping(R.string.app_version_name, versionName)
+ /** Returns a localized, human-readable lastUpdateDateTime. */
+ fun computeLastUpdatedDateText(): String =
+ resourceHandler.getStringInLocaleWithWrapping(
+ R.string.app_last_update_date,
+ getDateTime(lastUpdateDateTime),
+ )
- /** Returns a localized, human-readable lastUpdateDateTime. */
- fun computeLastUpdatedDateText(): String =
- resourceHandler.getStringInLocaleWithWrapping(
- R.string.app_last_update_date, getDateTime(lastUpdateDateTime)
- )
-
- private fun getDateTime(lastUpdateTime: Long): String =
- resourceHandler.computeDateString(lastUpdateTime)
-}
+ private fun getDateTime(lastUpdateTime: Long): String = resourceHandler.computeDateString(lastUpdateTime)
+ }
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ControlButtonsViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ControlButtonsViewModel.kt
index 7bfc41a7ffe..e19974081ab 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ControlButtonsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ControlButtonsViewModel.kt
@@ -35,7 +35,7 @@ class ControlButtonsViewModel private constructor(
private val analyticsController: AnalyticsController,
private val syncStatusManager: SyncStatusManager,
private val machineLocale: OppiaLocale.MachineLocale,
- private val viewModels: List
+ private val viewModels: List,
) : ProfileListItemViewModel(ProfileListViewModel.ProfileListItemViewType.SHARE_IDS) {
private var monitoredUploadProgress: LiveData =
MutableLiveData(ForceSyncProgress())
@@ -56,49 +56,52 @@ class ControlButtonsViewModel private constructor(
// Reference: https://developer.android.com/guide/components/intents-common#Email from
// https://stackoverflow.com/a/15022153/3689782.
val logs = retrieveEventLogs(viewModels.filterIsInstance())
- val sharedText = viewModels.mapNotNull { viewModel ->
- when (viewModel) {
- is DeviceIdItemViewModel -> listOf("Oppia app installation ID: ${viewModel.deviceId.value}")
- is ProfileLearnerIdItemViewModel -> {
- val profile = viewModel.profile
- val stats = viewModel.profileSpecificEventsUploadStats.value
- val learnerStats = stats?.learnerStats
- val uncategorizedStats = stats?.uncategorizedStats
- listOfNotNull(
- "- Profile name: ${profile.name}, learner ID: ${profile.learnerId}",
- learnerStats?.awaitingUploadEventCountText?.let { " - Uploading learner events: $it" },
- learnerStats?.uploadedEventCountText?.let { " - Uploaded learner events: $it" },
- uncategorizedStats?.awaitingUploadEventCountText?.let {
- " - Uploading uncategorized events: $it"
- },
- uncategorizedStats?.uploadedEventCountText?.let {
- " - Uploaded uncategorized events: $it"
+ val sharedText =
+ viewModels
+ .mapNotNull { viewModel ->
+ when (viewModel) {
+ is DeviceIdItemViewModel -> listOf("Oppia app installation ID: ${viewModel.deviceId.value}")
+ is ProfileLearnerIdItemViewModel -> {
+ val profile = viewModel.profile
+ val stats = viewModel.profileSpecificEventsUploadStats.value
+ val learnerStats = stats?.learnerStats
+ val uncategorizedStats = stats?.uncategorizedStats
+ listOfNotNull(
+ "- Profile name: ${profile.name}, learner ID: ${profile.learnerId}",
+ learnerStats?.awaitingUploadEventCountText?.let { " - Uploading learner events: $it" },
+ learnerStats?.uploadedEventCountText?.let { " - Uploaded learner events: $it" },
+ uncategorizedStats?.awaitingUploadEventCountText?.let {
+ " - Uploading uncategorized events: $it"
+ },
+ uncategorizedStats?.uploadedEventCountText?.let {
+ " - Uploaded uncategorized events: $it"
+ },
+ )
}
- )
- }
- is SyncStatusItemViewModel -> {
- val halfLineCount = BASE64_LINE_WRAP_LIMIT / 2
- val logsStr = logs?.toCompressedBase64()
- listOf(
- "Current sync status: ${viewModel.syncStatus.value}.",
- "Event log encoding integrity checks:",
- "- First $halfLineCount chars of encoded string: ${logsStr?.take(halfLineCount)}",
- "- Last $halfLineCount chars of encoded string: ${logsStr?.takeLast(halfLineCount)}",
- "- SHA-1 hash (unwrapped event string): ${logsStr?.computeSha1Hash(machineLocale)}",
- "- Total event string length (unwrapped): ${logsStr?.length}",
- "Encoded event logs:"
- ) + (logsStr?.chunked(BASE64_LINE_WRAP_LIMIT) ?: listOf("Missing"))
- }
- else -> null
- }
- }.flatten().joinToString(separator = "\n")
+ is SyncStatusItemViewModel -> {
+ val halfLineCount = BASE64_LINE_WRAP_LIMIT / 2
+ val logsStr = logs?.toCompressedBase64()
+ listOf(
+ "Current sync status: ${viewModel.syncStatus.value}.",
+ "Event log encoding integrity checks:",
+ "- First $halfLineCount chars of encoded string: ${logsStr?.take(halfLineCount)}",
+ "- Last $halfLineCount chars of encoded string: ${logsStr?.takeLast(halfLineCount)}",
+ "- SHA-1 hash (unwrapped event string): ${logsStr?.computeSha1Hash(machineLocale)}",
+ "- Total event string length (unwrapped): ${logsStr?.length}",
+ "Encoded event logs:",
+ ) + (logsStr?.chunked(BASE64_LINE_WRAP_LIMIT) ?: listOf("Missing"))
+ }
+ else -> null
+ }
+ }.flatten()
+ .joinToString(separator = "\n")
try {
activity.startActivity(
Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(Intent.EXTRA_TEXT, sharedText)
- }
+ },
)
} catch (e: ActivityNotFoundException) {
oppiaLogger.e("ControlButtonsViewModel", "No activity found to receive shared IDs.", e)
@@ -117,7 +120,8 @@ class ControlButtonsViewModel private constructor(
fun onUploadLogsNowButtonClicked() {
monitoredUploadProgress =
Transformations.map(
- analyticsController.uploadEventLogs().toLiveData(), this::processUploadedEventLogs
+ analyticsController.uploadEventLogs().toLiveData(),
+ this::processUploadedEventLogs,
)
notifyChange() // Recompute bindings since the live data instance has changed.
}
@@ -133,53 +137,62 @@ class ControlButtonsViewModel private constructor(
* implementation to work around this case by assuming that either value missing means no
* determination can be made whether logs can start being uploaded.
*/
- fun canStartUploadingLogs(isCurrentlyUploading: Boolean?, syncStatus: SyncStatus?): Boolean =
- isCurrentlyUploading == false && syncStatus == SyncStatus.WAITING_TO_START_UPLOADING
+ fun canStartUploadingLogs(
+ isCurrentlyUploading: Boolean?,
+ syncStatus: SyncStatus?,
+ ): Boolean = isCurrentlyUploading == false && syncStatus == SyncStatus.WAITING_TO_START_UPLOADING
private fun retrieveEventLogs(viewModels: List): OppiaEventLogs? =
- viewModels.firstOrNull()?.oppiaEventLogs?.value?.let { processEventLogs(it) }
+ viewModels
+ .firstOrNull()
+ ?.oppiaEventLogs
+ ?.value
+ ?.let { processEventLogs(it) }
- private fun processUploadedEventLogs(result: AsyncResult>): ForceSyncProgress {
- return when (result) {
+ private fun processUploadedEventLogs(result: AsyncResult>): ForceSyncProgress =
+ when (result) {
is AsyncResult.Pending -> ForceSyncProgress(eventsUploaded = 0, totalEventsToUpload = 0)
is AsyncResult.Success -> {
val (currentUploadedEventCount, totalEventCount) = result.value
ForceSyncProgress(currentUploadedEventCount, totalEventCount)
}
- is AsyncResult.Failure -> ForceSyncProgress().also {
- oppiaLogger.e(
- "ControlButtonsViewModel", "Encountered failure while uploading events.", result.error
- )
- }
+ is AsyncResult.Failure ->
+ ForceSyncProgress().also {
+ oppiaLogger.e(
+ "ControlButtonsViewModel",
+ "Encountered failure while uploading events.",
+ result.error,
+ )
+ }
}
- }
- private fun processSyncStatus(result: AsyncResult): SyncStatus {
- return when (result) {
+ private fun processSyncStatus(result: AsyncResult): SyncStatus =
+ when (result) {
is AsyncResult.Pending -> SyncStatus.INITIAL_UNKNOWN
is AsyncResult.Success -> result.value
- is AsyncResult.Failure -> SyncStatus.INITIAL_UNKNOWN.also {
- oppiaLogger.e(
- "ControlButtonsViewModel",
- "Encountered failure while retrieving sync status.",
- result.error
- )
- }
+ is AsyncResult.Failure ->
+ SyncStatus.INITIAL_UNKNOWN.also {
+ oppiaLogger.e(
+ "ControlButtonsViewModel",
+ "Encountered failure while retrieving sync status.",
+ result.error,
+ )
+ }
}
- }
- private fun processEventLogs(result: AsyncResult): OppiaEventLogs? {
- return when (result) {
+ private fun processEventLogs(result: AsyncResult): OppiaEventLogs? =
+ when (result) {
is AsyncResult.Pending -> null
is AsyncResult.Success -> result.value
is AsyncResult.Failure -> {
oppiaLogger.e(
- "ControlButtonsViewModel", "Encountered failure while on-disk event logs.", result.error
+ "ControlButtonsViewModel",
+ "Encountered failure while on-disk event logs.",
+ result.error,
)
null
}
}
- }
/**
* Progress representation structure that provides the UI with an indication of how much of an
@@ -195,7 +208,7 @@ class ControlButtonsViewModel private constructor(
*/
data class ForceSyncProgress(
val eventsUploaded: Int = 0,
- val totalEventsToUpload: Int = DEFAULT_UNKNOWN_EVENTS_TO_UPLOAD_COUNT
+ val totalEventsToUpload: Int = DEFAULT_UNKNOWN_EVENTS_TO_UPLOAD_COUNT,
) {
/** Returns whether there are events that can be uploaded. */
fun hasEventsToUpload(): Boolean = totalEventsToUpload != DEFAULT_UNKNOWN_EVENTS_TO_UPLOAD_COUNT
@@ -205,20 +218,26 @@ class ControlButtonsViewModel private constructor(
}
/** Factory for creating new [ControlButtonsViewModel]s. */
- class Factory @Inject constructor(
- private val oppiaLogger: OppiaLogger,
- private val activity: AppCompatActivity,
- private val analyticsController: AnalyticsController,
- private val syncStatusManager: SyncStatusManager,
- private val machineLocale: OppiaLocale.MachineLocale
- ) {
- /** Returns a new [ControlButtonsViewModel]. */
- fun create(viewModels: List): ControlButtonsViewModel {
- return ControlButtonsViewModel(
- oppiaLogger, activity, analyticsController, syncStatusManager, machineLocale, viewModels
- )
+ class Factory
+ @Inject
+ constructor(
+ private val oppiaLogger: OppiaLogger,
+ private val activity: AppCompatActivity,
+ private val analyticsController: AnalyticsController,
+ private val syncStatusManager: SyncStatusManager,
+ private val machineLocale: OppiaLocale.MachineLocale,
+ ) {
+ /** Returns a new [ControlButtonsViewModel]. */
+ fun create(viewModels: List): ControlButtonsViewModel =
+ ControlButtonsViewModel(
+ oppiaLogger,
+ activity,
+ analyticsController,
+ syncStatusManager,
+ machineLocale,
+ viewModels,
+ )
}
- }
private companion object {
private const val BASE64_LINE_WRAP_LIMIT = 80
@@ -226,9 +245,11 @@ class ControlButtonsViewModel private constructor(
// Copied from ProtoStringEncoder (which isn't available in production code).
private fun M.toCompressedBase64(): String {
- val compressedMessage = ByteArrayOutputStream().also { byteOutputStream ->
- GZIPOutputStream(byteOutputStream).use(::writeTo)
- }.toByteArray()
+ val compressedMessage =
+ ByteArrayOutputStream()
+ .also { byteOutputStream ->
+ GZIPOutputStream(byteOutputStream).use(::writeTo)
+ }.toByteArray()
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Base64.getEncoder().encodeToString(compressedMessage)
} else {
@@ -236,12 +257,12 @@ class ControlButtonsViewModel private constructor(
}
}
- private fun String.computeSha1Hash(machineLocale: OppiaLocale.MachineLocale): String {
- return machineLocale.run {
- MessageDigest.getInstance("SHA-1")
+ private fun String.computeSha1Hash(machineLocale: OppiaLocale.MachineLocale): String =
+ machineLocale.run {
+ MessageDigest
+ .getInstance("SHA-1")
.digest(this@computeSha1Hash.toByteArray())
.joinToString("") { "%02x".formatForMachines(it) }
}
- }
}
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/CopyIdMaterialButtonView.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/CopyIdMaterialButtonView.kt
index 227c345c131..bfc5398f22b 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/CopyIdMaterialButtonView.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/CopyIdMaterialButtonView.kt
@@ -12,13 +12,15 @@ import com.google.android.material.button.MaterialButton
* Note that this view is currently only used for learner analytics admin page buttons to copy
* various IDs to the device clipboard, and is intended only for that purpose.
*/
-class CopyIdMaterialButtonView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : MaterialButton(context, attrs, defStyleAttr) {
- init {
- // Ensure the tint mode is properly set (since it can't be set in XML).
- backgroundTintMode = PorterDuff.Mode.DARKEN
+class CopyIdMaterialButtonView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ ) : MaterialButton(context, attrs, defStyleAttr) {
+ init {
+ // Ensure the tint mode is properly set (since it can't be set in XML).
+ backgroundTintMode = PorterDuff.Mode.DARKEN
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/DeviceIdItemViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/DeviceIdItemViewModel.kt
index 372be3cfffc..f5e069f125b 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/DeviceIdItemViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/DeviceIdItemViewModel.kt
@@ -23,12 +23,13 @@ class DeviceIdItemViewModel private constructor(
private val resourceHandler: AppLanguageResourceHandler,
private val loggingIdentifierController: LoggingIdentifierController,
private val oppiaLogger: OppiaLogger,
- private val fragment: Fragment
+ private val fragment: Fragment,
) : ProfileListItemViewModel(ProfileListViewModel.ProfileListItemViewType.DEVICE_ID) {
/** The device installation ID associated with analytics. */
val deviceId: LiveData by lazy {
Transformations.map(
- loggingIdentifierController.getInstallationId().toLiveData(), this::processDeviceId
+ loggingIdentifierController.getInstallationId().toLiveData(),
+ this::processDeviceId,
)
}
@@ -38,61 +39,70 @@ class DeviceIdItemViewModel private constructor(
}
/** Returns a human-readable label to represent the specified [deviceId]. */
- fun computeDeviceIdLabel(deviceId: String?): String {
- return if (deviceId != null) {
+ fun computeDeviceIdLabel(deviceId: String?): String =
+ if (deviceId != null) {
resourceHandler.getStringInLocaleWithWrapping(
- R.string.learner_analytics_device_id_label, deviceId
+ R.string.learner_analytics_device_id_label,
+ deviceId,
)
} else {
resourceHandler.getStringInLocale(R.string.learner_analytics_error_retrieving_device_id_error)
}
- }
/** Copies the specified device ID (if non-null) to the user's clipboard. */
fun copyDeviceId(deviceId: String?) {
// Only copy the device ID if it's available.
deviceId?.let {
val appName = resourceHandler.getStringInLocale(R.string.app_name)
- clipboardController.setCurrentClip(
- resourceHandler.getStringInLocaleWithWrapping(
- R.string.learner_analytics_device_id_clipboard_label_description, appName
- ),
- deviceId
- ).toLiveData().observe(fragment) {
- if (it !is AsyncResult.Success) {
- oppiaLogger.w(
- "ProfileLearnerIdItemViewModel",
- "Encountered unexpected non-successful result when copying to clipboard: $it"
- )
+ clipboardController
+ .setCurrentClip(
+ resourceHandler.getStringInLocaleWithWrapping(
+ R.string.learner_analytics_device_id_clipboard_label_description,
+ appName,
+ ),
+ deviceId,
+ ).toLiveData()
+ .observe(fragment) {
+ if (it !is AsyncResult.Success) {
+ oppiaLogger.w(
+ "ProfileLearnerIdItemViewModel",
+ "Encountered unexpected non-successful result when copying to clipboard: $it",
+ )
+ }
}
- }
}
}
private fun processDeviceId(result: AsyncResult) = (result as? AsyncResult.Success)?.value
- private fun processCurrentClip(result: AsyncResult): String? {
- return if (result is AsyncResult.Success) {
+ private fun processCurrentClip(result: AsyncResult): String? =
+ if (result is AsyncResult.Success) {
when (val clip = result.value) {
is CurrentClip.SetWithAppText -> clip.text
CurrentClip.SetWithOtherContent, CurrentClip.Unknown -> null
}
- } else null
- }
+ } else {
+ null
+ }
/** Factory for creating new [DeviceIdItemViewModel]s. */
- class Factory @Inject constructor(
- private val clipboardController: ClipboardController,
- private val resourceHandler: AppLanguageResourceHandler,
- private val loggingIdentifierController: LoggingIdentifierController,
- private val oppiaLogger: OppiaLogger,
- private val fragment: Fragment
- ) {
- /** Returns a new [DeviceIdItemViewModel]. */
- fun create(): DeviceIdItemViewModel {
- return DeviceIdItemViewModel(
- clipboardController, resourceHandler, loggingIdentifierController, oppiaLogger, fragment
- )
+ class Factory
+ @Inject
+ constructor(
+ private val clipboardController: ClipboardController,
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val loggingIdentifierController: LoggingIdentifierController,
+ private val oppiaLogger: OppiaLogger,
+ private val fragment: Fragment,
+ ) {
+ /** Returns a new [DeviceIdItemViewModel]. */
+ fun create(): DeviceIdItemViewModel =
+ DeviceIdItemViewModel(
+ clipboardController,
+ resourceHandler,
+ loggingIdentifierController,
+ oppiaLogger,
+ fragment,
+ )
}
- }
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt
index 45a27fac5b8..dac59583369 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt
@@ -33,7 +33,7 @@ class ProfileAndDeviceIdActivity : InjectableAutoLocalizedAppCompatActivity() {
override fun handleOnBackPressed() {
finish()
}
- }
+ },
)
}
@@ -46,10 +46,9 @@ class ProfileAndDeviceIdActivity : InjectableAutoLocalizedAppCompatActivity() {
companion object {
/** Returns an [Intent] to launch [ProfileAndDeviceIdActivity]. */
- fun createIntent(context: Context): Intent {
- return Intent(context, ProfileAndDeviceIdActivity::class.java).apply {
+ fun createIntent(context: Context): Intent =
+ Intent(context, ProfileAndDeviceIdActivity::class.java).apply {
decorateWithScreenName(PROFILE_AND_DEVICE_ID_ACTIVITY)
}
- }
}
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt
index da6eee404bf..bc06ce5208c 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt
@@ -6,30 +6,34 @@ import org.oppia.android.R
import javax.inject.Inject
/** The presenter for arranging the UI of [ProfileAndDeviceIdActivity]. */
-class ProfileAndDeviceIdActivityPresenter @Inject constructor(
- private val activity: AppCompatActivity
-) {
- /** Handles [ProfileAndDeviceIdActivity]'s creation flow. */
- fun handleOnCreate() {
- activity.setContentView(R.layout.profile_and_device_id_activity)
- setToolbar()
- if (getProfileAndDeviceIdFragment() == null) {
- activity.supportFragmentManager.beginTransaction().add(
- R.id.profile_and_device_id_fragment_placeholder,
- ProfileAndDeviceIdFragment()
- ).commitNow()
+class ProfileAndDeviceIdActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ ) {
+ /** Handles [ProfileAndDeviceIdActivity]'s creation flow. */
+ fun handleOnCreate() {
+ activity.setContentView(R.layout.profile_and_device_id_activity)
+ setToolbar()
+ if (getProfileAndDeviceIdFragment() == null) {
+ activity.supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.profile_and_device_id_fragment_placeholder,
+ ProfileAndDeviceIdFragment(),
+ ).commitNow()
+ }
}
- }
- private fun setToolbar() {
- val appVersionToolbar = activity.findViewById(R.id.profile_and_device_id_toolbar) as Toolbar
- activity.setSupportActionBar(appVersionToolbar)
- activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
- }
+ private fun setToolbar() {
+ val appVersionToolbar = activity.findViewById(R.id.profile_and_device_id_toolbar) as Toolbar
+ activity.setSupportActionBar(appVersionToolbar)
+ activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
- private fun getProfileAndDeviceIdFragment(): ProfileAndDeviceIdFragment? {
- val fragManager = activity.supportFragmentManager
- val fragment = fragManager.findFragmentById(R.id.profile_and_device_id_fragment_placeholder)
- return fragment as? ProfileAndDeviceIdFragment
+ private fun getProfileAndDeviceIdFragment(): ProfileAndDeviceIdFragment? {
+ val fragManager = activity.supportFragmentManager
+ val fragment = fragManager.findFragmentById(R.id.profile_and_device_id_fragment_placeholder)
+ return fragment as? ProfileAndDeviceIdFragment
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragment.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragment.kt
index 6bc0ad70c56..db7ad70eaf3 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragment.kt
@@ -26,8 +26,6 @@ class ProfileAndDeviceIdFragment : InjectableFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return profileAndDeviceIdFragmentPresenter.handleCreateView(inflater, container)
- }
+ savedInstanceState: Bundle?,
+ ): View = profileAndDeviceIdFragmentPresenter.handleCreateView(inflater, container)
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt
index 07dc3cd210e..eae3175fbd9 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentPresenter.kt
@@ -15,61 +15,69 @@ import org.oppia.android.databinding.ProfileListSyncStatusItemBinding
import javax.inject.Inject
/** Presenter for arranging [ProfileAndDeviceIdFragment]'s UI. */
-class ProfileAndDeviceIdFragmentPresenter @Inject constructor(
- private val fragment: Fragment,
- private val profileListViewModelFactory: ProfileListViewModel.Factory,
- private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory
-) {
+class ProfileAndDeviceIdFragmentPresenter
+ @Inject
+ constructor(
+ private val fragment: Fragment,
+ private val profileListViewModelFactory: ProfileListViewModel.Factory,
+ private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory,
+ ) {
+ private lateinit var binding: ProfileAndDeviceIdFragmentBinding
- private lateinit var binding: ProfileAndDeviceIdFragmentBinding
-
- /** Handles [ProfileAndDeviceIdFragment]'s creation flow. */
- fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View {
- binding = ProfileAndDeviceIdFragmentBinding.inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
- binding.apply {
- lifecycleOwner = fragment
- this.viewModel = profileListViewModelFactory.create()
- }
- binding.profileAndDeviceIdRecyclerView.apply {
- adapter = createRecyclerViewAdapter()
+ /** Handles [ProfileAndDeviceIdFragment]'s creation flow. */
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ ): View {
+ binding =
+ ProfileAndDeviceIdFragmentBinding.inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
+ binding.apply {
+ lifecycleOwner = fragment
+ this.viewModel = profileListViewModelFactory.create()
+ }
+ binding.profileAndDeviceIdRecyclerView.apply {
+ adapter = createRecyclerViewAdapter()
+ }
+ return binding.root
}
- return binding.root
- }
- private fun createRecyclerViewAdapter(): BindableAdapter {
- return multiTypeBuilderFactory.create { viewModel ->
- when (viewModel) {
- is DeviceIdItemViewModel -> ProfileListItemViewType.DEVICE_ID
- is ProfileLearnerIdItemViewModel -> ProfileListItemViewType.LEARNER_ID
- is SyncStatusItemViewModel -> ProfileListItemViewType.SYNC_STATUS
- is ControlButtonsViewModel -> ProfileListItemViewType.SHARE_IDS
- else -> error("Encountered unexpected view model: $viewModel")
- }
- }.registerViewDataBinder(
- viewType = ProfileListItemViewType.DEVICE_ID,
- inflateDataBinding = ProfileListDeviceIdItemBinding::inflate,
- setViewModel = ProfileListDeviceIdItemBinding::setViewModel,
- transformViewModel = { it as DeviceIdItemViewModel }
- ).registerViewDataBinder(
- viewType = ProfileListItemViewType.LEARNER_ID,
- inflateDataBinding = ProfileListLearnerIdItemBinding::inflate,
- setViewModel = ProfileListLearnerIdItemBinding::setViewModel,
- transformViewModel = { it as ProfileLearnerIdItemViewModel }
- ).registerViewDataBinder(
- viewType = ProfileListItemViewType.SYNC_STATUS,
- inflateDataBinding = ProfileListSyncStatusItemBinding::inflate,
- setViewModel = ProfileListSyncStatusItemBinding::setViewModel,
- transformViewModel = { it as SyncStatusItemViewModel }
- ).registerViewDataBinder(
- viewType = ProfileListItemViewType.SHARE_IDS,
- inflateDataBinding = ProfileListControlButtonsBinding::inflate,
- setViewModel = ProfileListControlButtonsBinding::setViewModel,
- transformViewModel = { it as ControlButtonsViewModel }
- ).build()
+ private fun createRecyclerViewAdapter(): BindableAdapter =
+ multiTypeBuilderFactory
+ .create<
+ ProfileListItemViewModel,
+ ProfileListItemViewType,
+ > { viewModel ->
+ when (viewModel) {
+ is DeviceIdItemViewModel -> ProfileListItemViewType.DEVICE_ID
+ is ProfileLearnerIdItemViewModel -> ProfileListItemViewType.LEARNER_ID
+ is SyncStatusItemViewModel -> ProfileListItemViewType.SYNC_STATUS
+ is ControlButtonsViewModel -> ProfileListItemViewType.SHARE_IDS
+ else -> error("Encountered unexpected view model: $viewModel")
+ }
+ }.registerViewDataBinder(
+ viewType = ProfileListItemViewType.DEVICE_ID,
+ inflateDataBinding = ProfileListDeviceIdItemBinding::inflate,
+ setViewModel = ProfileListDeviceIdItemBinding::setViewModel,
+ transformViewModel = { it as DeviceIdItemViewModel },
+ ).registerViewDataBinder(
+ viewType = ProfileListItemViewType.LEARNER_ID,
+ inflateDataBinding = ProfileListLearnerIdItemBinding::inflate,
+ setViewModel = ProfileListLearnerIdItemBinding::setViewModel,
+ transformViewModel = { it as ProfileLearnerIdItemViewModel },
+ ).registerViewDataBinder(
+ viewType = ProfileListItemViewType.SYNC_STATUS,
+ inflateDataBinding = ProfileListSyncStatusItemBinding::inflate,
+ setViewModel = ProfileListSyncStatusItemBinding::setViewModel,
+ transformViewModel = { it as SyncStatusItemViewModel },
+ ).registerViewDataBinder(
+ viewType = ProfileListItemViewType.SHARE_IDS,
+ inflateDataBinding = ProfileListControlButtonsBinding::inflate,
+ setViewModel = ProfileListControlButtonsBinding::setViewModel,
+ transformViewModel = { it as ControlButtonsViewModel },
+ ).build()
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileLearnerIdItemViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileLearnerIdItemViewModel.kt
index d5fe581ba87..1437a3cfcba 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileLearnerIdItemViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileLearnerIdItemViewModel.kt
@@ -30,7 +30,7 @@ class ProfileLearnerIdItemViewModel private constructor(
private val resourceHandler: AppLanguageResourceHandler,
private val analyticsController: AnalyticsController,
private val oppiaLogger: OppiaLogger,
- private val fragment: Fragment
+ private val fragment: Fragment,
) : ProfileListItemViewModel(ProfileListViewModel.ProfileListItemViewType.LEARNER_ID) {
/** The current ID copied to the user's clipboard, or ``null`` if there isn't one. */
val currentCopiedId: LiveData by lazy {
@@ -54,40 +54,43 @@ class ProfileLearnerIdItemViewModel private constructor(
* Copies the analytics learner ID associated with this model's [profile] to the user's clipboard.
*/
fun copyLearnerId() {
- clipboardController.setCurrentClip(
- resourceHandler.getStringInLocaleWithWrapping(
- R.string.learner_analytics_learner_id_clipboard_label_description, profile.name
- ),
- profile.learnerId
- ).toLiveData().observe(fragment) {
- if (it !is AsyncResult.Success) {
- oppiaLogger.w(
- "ProfileLearnerIdItemViewModel",
- "Encountered unexpected non-successful result when copying to clipboard: $it"
- )
+ clipboardController
+ .setCurrentClip(
+ resourceHandler.getStringInLocaleWithWrapping(
+ R.string.learner_analytics_learner_id_clipboard_label_description,
+ profile.name,
+ ),
+ profile.learnerId,
+ ).toLiveData()
+ .observe(fragment) {
+ if (it !is AsyncResult.Success) {
+ oppiaLogger.w(
+ "ProfileLearnerIdItemViewModel",
+ "Encountered unexpected non-successful result when copying to clipboard: $it",
+ )
+ }
}
- }
}
/**
* Returns a textual representation of the number of events awaiting upload for the profile
* corresponding to this view model.
*/
- fun computeEventsWaitingUploadLabelText(): String {
- return resourceHandler.getStringInLocaleWithWrapping(
- R.string.learner_analytics_learner_events_waiting_upload, profile.name
+ fun computeEventsWaitingUploadLabelText(): String =
+ resourceHandler.getStringInLocaleWithWrapping(
+ R.string.learner_analytics_learner_events_waiting_upload,
+ profile.name,
)
- }
/**
* Returns a textual representation of the number of events that have been uploaded for the
* profile corresponding to this view model.
*/
- fun computeEventsUploadedLabelText(): String {
- return resourceHandler.getStringInLocaleWithWrapping(
- R.string.learner_analytics_learner_events_uploaded, profile.name
+ fun computeEventsUploadedLabelText(): String =
+ resourceHandler.getStringInLocaleWithWrapping(
+ R.string.learner_analytics_learner_events_uploaded,
+ profile.name,
)
- }
/**
* Stats structure to provide context on the synchronization & uploading information of a specific
@@ -99,7 +102,7 @@ class ProfileLearnerIdItemViewModel private constructor(
*/
data class ProfileSpecificEventsUploadStats(
val learnerStats: CategorizedEventStats,
- val uncategorizedStats: CategorizedEventStats?
+ val uncategorizedStats: CategorizedEventStats?,
) {
/** Returns whether this stats report includes [uncategorizedStats]. */
fun hasUncategorizedStats(): Boolean = uncategorizedStats != null
@@ -115,20 +118,30 @@ class ProfileLearnerIdItemViewModel private constructor(
fun createFrom(
resourceHandler: AppLanguageResourceHandler,
profile: Profile,
- oppiaEventLogs: OppiaEventLogs
+ oppiaEventLogs: OppiaEventLogs,
): ProfileSpecificEventsUploadStats {
val logsToUploadMap = oppiaEventLogs.eventLogsToUploadList.associateByProfileId()
val uploadedLogsMap = oppiaEventLogs.uploadedEventLogsList.associateByProfileId()
return ProfileSpecificEventsUploadStats(
- learnerStats = CategorizedEventStats.createFrom(
- resourceHandler, profile.id, logsToUploadMap, uploadedLogsMap
- ),
- uncategorizedStats = if (profile.isAdmin) {
- // Admins should also show uncategorized stats.
+ learnerStats =
CategorizedEventStats.createFrom(
- resourceHandler, profileId = null, logsToUploadMap, uploadedLogsMap
- )
- } else null
+ resourceHandler,
+ profile.id,
+ logsToUploadMap,
+ uploadedLogsMap,
+ ),
+ uncategorizedStats =
+ if (profile.isAdmin) {
+ // Admins should also show uncategorized stats.
+ CategorizedEventStats.createFrom(
+ resourceHandler,
+ profileId = null,
+ logsToUploadMap,
+ uploadedLogsMap,
+ )
+ } else {
+ null
+ },
)
}
@@ -136,17 +149,16 @@ class ProfileLearnerIdItemViewModel private constructor(
* Returns a new [ProfileSpecificEventsUploadStats] with unknown event counts filled in
* (localized per [resourceHandler]).
*/
- fun createFromUnknown(
- resourceHandler: AppLanguageResourceHandler
- ): ProfileSpecificEventsUploadStats {
+ fun createFromUnknown(resourceHandler: AppLanguageResourceHandler): ProfileSpecificEventsUploadStats {
val unknownCountText =
resourceHandler.getStringInLocale(R.string.learner_analytics_unknown_event_count)
return ProfileSpecificEventsUploadStats(
- learnerStats = CategorizedEventStats(
- awaitingUploadEventCountText = unknownCountText,
- uploadedEventCountText = unknownCountText
- ),
- uncategorizedStats = null
+ learnerStats =
+ CategorizedEventStats(
+ awaitingUploadEventCountText = unknownCountText,
+ uploadedEventCountText = unknownCountText,
+ ),
+ uncategorizedStats = null,
)
}
}
@@ -164,7 +176,7 @@ class ProfileLearnerIdItemViewModel private constructor(
*/
data class CategorizedEventStats(
val awaitingUploadEventCountText: String,
- val uploadedEventCountText: String
+ val uploadedEventCountText: String,
) {
companion object {
/**
@@ -182,31 +194,30 @@ class ProfileLearnerIdItemViewModel private constructor(
resourceHandler: AppLanguageResourceHandler,
profileId: ProfileId?,
logsToUploadMap: Map>,
- uploadedLogsMap: Map>
+ uploadedLogsMap: Map>,
): CategorizedEventStats {
val logsToUpload = logsToUploadMap[profileId] ?: emptyList()
val uploadedLogs = uploadedLogsMap[profileId] ?: emptyList()
return CategorizedEventStats(
awaitingUploadEventCountText = resourceHandler.toHumanReadableString(logsToUpload.size),
- uploadedEventCountText = resourceHandler.toHumanReadableString(uploadedLogs.size)
+ uploadedEventCountText = resourceHandler.toHumanReadableString(uploadedLogs.size),
)
}
}
}
- private fun processCurrentClip(result: AsyncResult): String? {
- return if (result is AsyncResult.Success) {
+ private fun processCurrentClip(result: AsyncResult): String? =
+ if (result is AsyncResult.Success) {
when (val clip = result.value) {
is CurrentClip.SetWithAppText -> clip.text
CurrentClip.SetWithOtherContent, CurrentClip.Unknown -> null
}
- } else null
- }
+ } else {
+ null
+ }
- private fun processEventLogs(
- result: AsyncResult
- ): ProfileSpecificEventsUploadStats {
- return when (result) {
+ private fun processEventLogs(result: AsyncResult): ProfileSpecificEventsUploadStats =
+ when (result) {
is AsyncResult.Pending -> ProfileSpecificEventsUploadStats.createFromUnknown(resourceHandler)
is AsyncResult.Success ->
ProfileSpecificEventsUploadStats.createFrom(resourceHandler, profile, result.value)
@@ -215,28 +226,33 @@ class ProfileLearnerIdItemViewModel private constructor(
oppiaLogger.e(
"ProfileLearnerIdItemViewModel",
"Encountered unexpected failure when processing event logs",
- result.error
+ result.error,
)
}
}
}
- }
/** Factory for creating new [ProfileLearnerIdItemViewModel]s. */
- class Factory @Inject constructor(
- private val clipboardController: ClipboardController,
- private val resourceHandler: AppLanguageResourceHandler,
- private val analyticsController: AnalyticsController,
- private val oppiaLogger: OppiaLogger,
- private val fragment: Fragment
- ) {
- /** Returns a new [ProfileLearnerIdItemViewModel] corresponding to the specified [profile]. */
- fun create(profile: Profile): ProfileLearnerIdItemViewModel {
- return ProfileLearnerIdItemViewModel(
- profile, clipboardController, resourceHandler, analyticsController, oppiaLogger, fragment
- )
+ class Factory
+ @Inject
+ constructor(
+ private val clipboardController: ClipboardController,
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val analyticsController: AnalyticsController,
+ private val oppiaLogger: OppiaLogger,
+ private val fragment: Fragment,
+ ) {
+ /** Returns a new [ProfileLearnerIdItemViewModel] corresponding to the specified [profile]. */
+ fun create(profile: Profile): ProfileLearnerIdItemViewModel =
+ ProfileLearnerIdItemViewModel(
+ profile,
+ clipboardController,
+ resourceHandler,
+ analyticsController,
+ oppiaLogger,
+ fragment,
+ )
}
- }
private companion object {
private fun List.associateByProfileId(): Map> =
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileListViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileListViewModel.kt
index d559f1bc57f..c6974e3f86a 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileListViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileListViewModel.kt
@@ -24,33 +24,34 @@ class ProfileListViewModel private constructor(
private val deviceIdItemViewModelFactory: DeviceIdItemViewModel.Factory,
private val syncStatusItemViewModelFactory: SyncStatusItemViewModel.Factory,
private val profileLearnerIdItemViewModelFactory: ProfileLearnerIdItemViewModel.Factory,
- private val shareIdsViewModelFactory: ControlButtonsViewModel.Factory
+ private val shareIdsViewModelFactory: ControlButtonsViewModel.Factory,
) : ObservableViewModel() {
/** The list of [ProfileListItemViewModel] to display. */
val profileModels: LiveData> by lazy {
Transformations.map(profileManagementController.getProfiles().toLiveData(), ::processProfiles)
}
- private fun processProfiles(
- profilesResult: AsyncResult>
- ): List {
+ private fun processProfiles(profilesResult: AsyncResult>): List {
val deviceIdViewModel = deviceIdItemViewModelFactory.create()
- val learnerIdModels = when (profilesResult) {
- is AsyncResult.Pending -> listOf()
- is AsyncResult.Failure -> {
- oppiaLogger.e(
- "ProfileListViewModel", "Failed to retrieve the list of profiles", profilesResult.error
- )
- listOf()
- }
- is AsyncResult.Success -> {
- // Ensure that admins are listed first, then profiles should be sorted by name.
- profilesResult.value
- .sortedWith(compareByDescending(Profile::getIsAdmin).thenBy(Profile::getName))
- .map(profileLearnerIdItemViewModelFactory::create)
+ val learnerIdModels =
+ when (profilesResult) {
+ is AsyncResult.Pending -> listOf()
+ is AsyncResult.Failure -> {
+ oppiaLogger.e(
+ "ProfileListViewModel",
+ "Failed to retrieve the list of profiles",
+ profilesResult.error,
+ )
+ listOf()
+ }
+ is AsyncResult.Success -> {
+ // Ensure that admins are listed first, then profiles should be sorted by name.
+ profilesResult.value
+ .sortedWith(compareByDescending(Profile::getIsAdmin).thenBy(Profile::getName))
+ .map(profileLearnerIdItemViewModelFactory::create)
+ }
}
- }
val syncStatusViewModel = syncStatusItemViewModelFactory.create()
val displayViewModels = listOf(deviceIdViewModel) + learnerIdModels + syncStatusViewModel
@@ -67,7 +68,7 @@ class ProfileListViewModel private constructor(
* @property viewType the [ProfileListItemViewType] corresponding to this model.
*/
abstract class ProfileListItemViewModel(
- val viewType: ProfileListItemViewType
+ val viewType: ProfileListItemViewType,
) : ObservableViewModel()
/** Represents the different types of views that may be shown by [ProfileListViewModel]. */
@@ -82,28 +83,29 @@ class ProfileListViewModel private constructor(
SYNC_STATUS,
/** Corresponds to [ControlButtonsViewModel]. */
- SHARE_IDS
+ SHARE_IDS,
}
/** Factory to create new [ProfileListViewModel]s. */
- class Factory @Inject constructor(
- private val profileManagementController: ProfileManagementController,
- private val oppiaLogger: OppiaLogger,
- private val deviceIdItemViewModelFactory: DeviceIdItemViewModel.Factory,
- private val syncStatusItemViewModelFactory: SyncStatusItemViewModel.Factory,
- private val profileLearnerIdItemViewModelFactory: ProfileLearnerIdItemViewModel.Factory,
- private val controlButtonsViewModelFactory: ControlButtonsViewModel.Factory
- ) {
- /** Returns a new [ProfileListViewModel]. */
- fun create(): ProfileListViewModel {
- return ProfileListViewModel(
- profileManagementController,
- oppiaLogger,
- deviceIdItemViewModelFactory,
- syncStatusItemViewModelFactory,
- profileLearnerIdItemViewModelFactory,
- controlButtonsViewModelFactory
- )
+ class Factory
+ @Inject
+ constructor(
+ private val profileManagementController: ProfileManagementController,
+ private val oppiaLogger: OppiaLogger,
+ private val deviceIdItemViewModelFactory: DeviceIdItemViewModel.Factory,
+ private val syncStatusItemViewModelFactory: SyncStatusItemViewModel.Factory,
+ private val profileLearnerIdItemViewModelFactory: ProfileLearnerIdItemViewModel.Factory,
+ private val controlButtonsViewModelFactory: ControlButtonsViewModel.Factory,
+ ) {
+ /** Returns a new [ProfileListViewModel]. */
+ fun create(): ProfileListViewModel =
+ ProfileListViewModel(
+ profileManagementController,
+ oppiaLogger,
+ deviceIdItemViewModelFactory,
+ syncStatusItemViewModelFactory,
+ profileLearnerIdItemViewModelFactory,
+ controlButtonsViewModelFactory,
+ )
}
- }
}
diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/SyncStatusItemViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/SyncStatusItemViewModel.kt
index 04cad80889c..7865d4d487f 100644
--- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/SyncStatusItemViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/SyncStatusItemViewModel.kt
@@ -18,52 +18,56 @@ import javax.inject.Inject
class SyncStatusItemViewModel private constructor(
private val syncStatusManager: SyncStatusManager,
private val oppiaLogger: OppiaLogger,
- private val resourceHandler: AppLanguageResourceHandler
+ private val resourceHandler: AppLanguageResourceHandler,
) : ProfileListItemViewModel(ProfileListViewModel.ProfileListItemViewType.SYNC_STATUS) {
/** The current analytics event synchronization status, as a human-readable string. */
val syncStatus: LiveData by lazy {
Transformations.map(
- syncStatusManager.getSyncStatus().toLiveData(), ::processSyncStatusResult
+ syncStatusManager.getSyncStatus().toLiveData(),
+ ::processSyncStatusResult,
)
}
- private fun processSyncStatusResult(
- syncStatusResult: AsyncResult
- ): String {
- val resId = when (syncStatusResult) {
- is AsyncResult.Pending -> R.string.learner_analytics_sync_status_default
- is AsyncResult.Failure -> {
- oppiaLogger.e(
- "SyncStatusItemViewModel", "Failed to retrieve sync status", syncStatusResult.error
- )
- R.string.learner_analytics_sync_status_default
- }
- is AsyncResult.Success -> when (syncStatusResult.value) {
- SyncStatusManager.SyncStatus.INITIAL_UNKNOWN ->
+ private fun processSyncStatusResult(syncStatusResult: AsyncResult): String {
+ val resId =
+ when (syncStatusResult) {
+ is AsyncResult.Pending -> R.string.learner_analytics_sync_status_default
+ is AsyncResult.Failure -> {
+ oppiaLogger.e(
+ "SyncStatusItemViewModel",
+ "Failed to retrieve sync status",
+ syncStatusResult.error,
+ )
R.string.learner_analytics_sync_status_default
- SyncStatusManager.SyncStatus.WAITING_TO_START_UPLOADING ->
- R.string.learner_analytics_sync_status_waiting_to_upload
- SyncStatusManager.SyncStatus.DATA_UPLOADING ->
- R.string.learner_analytics_sync_status_data_uploading
- SyncStatusManager.SyncStatus.DATA_UPLOADED ->
- R.string.learner_analytics_sync_status_data_uploaded
- SyncStatusManager.SyncStatus.UPLOAD_ERROR ->
- R.string.learner_analytics_sync_status_upload_error
- SyncStatusManager.SyncStatus.NO_CONNECTIVITY ->
- R.string.learner_analytics_sync_status_no_internet_connectivity
+ }
+ is AsyncResult.Success ->
+ when (syncStatusResult.value) {
+ SyncStatusManager.SyncStatus.INITIAL_UNKNOWN ->
+ R.string.learner_analytics_sync_status_default
+ SyncStatusManager.SyncStatus.WAITING_TO_START_UPLOADING ->
+ R.string.learner_analytics_sync_status_waiting_to_upload
+ SyncStatusManager.SyncStatus.DATA_UPLOADING ->
+ R.string.learner_analytics_sync_status_data_uploading
+ SyncStatusManager.SyncStatus.DATA_UPLOADED ->
+ R.string.learner_analytics_sync_status_data_uploaded
+ SyncStatusManager.SyncStatus.UPLOAD_ERROR ->
+ R.string.learner_analytics_sync_status_upload_error
+ SyncStatusManager.SyncStatus.NO_CONNECTIVITY ->
+ R.string.learner_analytics_sync_status_no_internet_connectivity
+ }
}
- }
return resourceHandler.getStringInLocale(resId)
}
/** Factory for creating new [SyncStatusItemViewModel]s. */
- class Factory @Inject constructor(
- private val syncStatusManager: SyncStatusManager,
- private val oppiaLogger: OppiaLogger,
- private val resourceHandler: AppLanguageResourceHandler
- ) {
- /** Returns a new [SyncStatusItemViewModel]. */
- fun create(): SyncStatusItemViewModel =
- SyncStatusItemViewModel(syncStatusManager, oppiaLogger, resourceHandler)
- }
+ class Factory
+ @Inject
+ constructor(
+ private val syncStatusManager: SyncStatusManager,
+ private val oppiaLogger: OppiaLogger,
+ private val resourceHandler: AppLanguageResourceHandler,
+ ) {
+ /** Returns a new [SyncStatusItemViewModel]. */
+ fun create(): SyncStatusItemViewModel = SyncStatusItemViewModel(syncStatusManager, oppiaLogger, resourceHandler)
+ }
}
diff --git a/app/src/main/java/org/oppia/android/app/application/AbstractOppiaApplication.kt b/app/src/main/java/org/oppia/android/app/application/AbstractOppiaApplication.kt
index 11a3025ff14..199330f8dce 100644
--- a/app/src/main/java/org/oppia/android/app/application/AbstractOppiaApplication.kt
+++ b/app/src/main/java/org/oppia/android/app/application/AbstractOppiaApplication.kt
@@ -19,20 +19,22 @@ import org.oppia.android.domain.oppialogger.ApplicationStartupListener
/** The root base [Application] of the Oppia app. */
abstract class AbstractOppiaApplication(
- createComponentBuilder: () -> ApplicationComponent.Builder
+ createComponentBuilder: () -> ApplicationComponent.Builder,
) : MultiDexApplication(),
ActivityComponentFactory,
ApplicationInjectorProvider,
Configuration.Provider {
-
/** The root [ApplicationComponent]. */
private val component: ApplicationComponent by lazy {
createComponentBuilder().setApplication(this).build()
}
- override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent {
- return component.getActivityComponentBuilderProvider().get().setActivity(activity).build()
- }
+ override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent =
+ component
+ .getActivityComponentBuilderProvider()
+ .get()
+ .setActivity(activity)
+ .build()
override fun getApplicationInjector(): ApplicationInjector = component
@@ -72,7 +74,5 @@ abstract class AbstractOppiaApplication(
component.getApplicationStartupListeners().forEach(ApplicationStartupListener::onCreate)
}
- override fun getWorkManagerConfiguration(): Configuration {
- return component.getWorkManagerConfiguration()
- }
+ override fun getWorkManagerConfiguration(): Configuration = component.getWorkManagerConfiguration()
}
diff --git a/app/src/main/java/org/oppia/android/app/application/ApplicationInjectorProvider.kt b/app/src/main/java/org/oppia/android/app/application/ApplicationInjectorProvider.kt
index 4ef6fc55b3a..487c79369b9 100644
--- a/app/src/main/java/org/oppia/android/app/application/ApplicationInjectorProvider.kt
+++ b/app/src/main/java/org/oppia/android/app/application/ApplicationInjectorProvider.kt
@@ -25,8 +25,7 @@ interface ApplicationInjectorProvider :
override fun getDataProvidersInjector(): DataProvidersInjector = getApplicationInjector()
- override fun getAppLanguageApplicationInjector(): AppLanguageApplicationInjector =
- getApplicationInjector()
+ override fun getAppLanguageApplicationInjector(): AppLanguageApplicationInjector = getApplicationInjector()
override fun getOppiaClockInjector(): OppiaClockInjector = getApplicationInjector()
diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt
index 460ed8832b9..106e20258e0 100644
--- a/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt
+++ b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt
@@ -99,7 +99,7 @@ import javax.inject.Singleton
CpuPerformanceSnapshotterModule::class,
PerformanceMetricsAssessorModule::class, ExplorationProgressModule::class,
AuthenticationModule::class,
- ]
+ ],
)
interface AlphaApplicationComponent : ApplicationComponent {
/**
diff --git a/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt
index 45ccf6f11a8..8838375d3c4 100644
--- a/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt
+++ b/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt
@@ -99,7 +99,7 @@ import javax.inject.Singleton
ActivityRouterModule::class,
CpuPerformanceSnapshotterModule::class, PerformanceMetricsAssessorModule::class,
ExplorationProgressModule::class, AuthenticationModule::class,
- ]
+ ],
)
interface BetaApplicationComponent : ApplicationComponent {
/**
diff --git a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt
index 7607dfba33a..c4d9d3af21f 100644
--- a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt
+++ b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt
@@ -101,7 +101,7 @@ import javax.inject.Singleton
DeveloperBuildFlavorModule::class,
CpuPerformanceSnapshotterModule::class, ExplorationProgressModule::class,
AuthenticationModule::class,
- ]
+ ],
)
interface DeveloperApplicationComponent : ApplicationComponent {
/**
diff --git a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt
index 495a21d7a3e..886dbcc8937 100644
--- a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt
+++ b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt
@@ -3,6 +3,7 @@ package org.oppia.android.app.application.dev
import org.oppia.android.app.application.AbstractOppiaApplication
/** The root [AbstractOppiaApplication] for developer builds of the Oppia app. */
-class DeveloperOppiaApplication : AbstractOppiaApplication(
- DaggerDeveloperApplicationComponent::builder
-)
+class DeveloperOppiaApplication :
+ AbstractOppiaApplication(
+ DaggerDeveloperApplicationComponent::builder,
+ )
diff --git a/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt
index b4dd98d67c2..c16be8b99df 100644
--- a/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt
+++ b/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt
@@ -99,7 +99,7 @@ import javax.inject.Singleton
ActivityRouterModule::class,
CpuPerformanceSnapshotterModule::class, PerformanceMetricsAssessorModule::class,
ExplorationProgressModule::class, AuthenticationModule::class,
- ]
+ ],
)
interface GaApplicationComponent : ApplicationComponent {
/**
diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivity.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivity.kt
index 05b47050354..2744624d3b0 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivity.kt
@@ -55,12 +55,14 @@ class ClassroomListActivity :
companion object {
/** Returns a new [Intent] to route to [ClassroomListActivity] for a specified [profileId]. */
- fun createClassroomListActivity(context: Context, profileId: ProfileId?): Intent {
- return Intent(context, ClassroomListActivity::class.java).apply {
+ fun createClassroomListActivity(
+ context: Context,
+ profileId: ProfileId?,
+ ): Intent =
+ Intent(context, ClassroomListActivity::class.java).apply {
decorateWithScreenName(CLASSROOM_LIST_ACTIVITY)
profileId?.let { decorateWithUserProfileId(profileId) }
}
- }
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -83,8 +85,9 @@ class ClassroomListActivity :
.newBuilder()
.setHighlightItem(HighlightItem.NONE)
.build()
- val dialogFragment = ExitProfileDialogFragment
- .newInstance(exitProfileDialogArguments = exitProfileDialogArguments)
+ val dialogFragment =
+ ExitProfileDialogFragment
+ .newInstance(exitProfileDialogArguments = exitProfileDialogArguments)
dialogFragment.showNow(supportFragmentManager, TAG_SWITCH_PROFILE_DIALOG)
}
@@ -93,19 +96,24 @@ class ClassroomListActivity :
RecentlyPlayedActivityParams
.newBuilder()
.setProfileId(profileId)
- .setActivityTitle(recentlyPlayedActivityTitle).build()
+ .setActivityTitle(recentlyPlayedActivityTitle)
+ .build()
activityRouter.routeToScreen(
DestinationScreen
.newBuilder()
.setRecentlyPlayedActivityParams(recentlyPlayedActivityParams)
- .build()
+ .build(),
)
}
- override fun routeToTopic(profileId: ProfileId, classroomId: String, topicId: String) {
+ override fun routeToTopic(
+ profileId: ProfileId,
+ classroomId: String,
+ topicId: String,
+ ) {
startActivity(
- createTopicActivityIntent(this, profileId, classroomId, topicId)
+ createTopicActivityIntent(this, profileId, classroomId, topicId),
)
}
@@ -113,7 +121,7 @@ class ClassroomListActivity :
profileId: ProfileId,
classroomId: String,
topicId: String,
- storyId: String
+ storyId: String,
) {
startActivity(
createTopicPlayStoryActivityIntent(
@@ -121,8 +129,8 @@ class ClassroomListActivity :
profileId,
classroomId,
topicId,
- storyId
- )
+ storyId,
+ ),
)
}
@@ -134,15 +142,16 @@ class ClassroomListActivity :
}
val exitProfileDialogArguments =
ExitProfileDialogArguments
- .newBuilder().apply {
+ .newBuilder()
+ .apply {
if (enableOnboardingFlowV2.value) {
this.profileType = profileType
}
this.highlightItem = HighlightItem.NONE
- }
- .build()
- val dialogFragment = ExitProfileDialogFragment
- .newInstance(exitProfileDialogArguments = exitProfileDialogArguments)
+ }.build()
+ val dialogFragment =
+ ExitProfileDialogFragment
+ .newInstance(exitProfileDialogArguments = exitProfileDialogArguments)
dialogFragment.showNow(supportFragmentManager, TAG_SWITCH_PROFILE_DIALOG)
}
}
diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt
index 0b8c1d78f1e..f6e6e173e0c 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt
@@ -12,43 +12,50 @@ import javax.inject.Inject
private const val TAG_CLASSROOM_LIST_FRAGMENT = "CLASSROOM_LIST_FRAGMENT"
/** The presenter for [ClassroomListActivity]. */
-class ClassroomListActivityPresenter @Inject constructor(private val activity: AppCompatActivity) {
- private var navigationDrawerFragment: NavigationDrawerFragment? = null
+class ClassroomListActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ ) {
+ private var navigationDrawerFragment: NavigationDrawerFragment? = null
- /**
- * Handles the creation of the activity. Sets the content view, sets up the navigation drawer,
- * and adds the [ClassroomListFragment] if it's not already added.
- */
- fun handleOnCreate() {
- activity.setContentView(R.layout.classroom_list_activity)
- setUpNavigationDrawer()
- if (getClassroomListFragment() == null) {
- activity.supportFragmentManager.beginTransaction().add(
- R.id.classroom_list_fragment_placeholder,
- ClassroomListFragment(),
- TAG_CLASSROOM_LIST_FRAGMENT
- ).commitNow()
+ /**
+ * Handles the creation of the activity. Sets the content view, sets up the navigation drawer,
+ * and adds the [ClassroomListFragment] if it's not already added.
+ */
+ fun handleOnCreate() {
+ activity.setContentView(R.layout.classroom_list_activity)
+ setUpNavigationDrawer()
+ if (getClassroomListFragment() == null) {
+ activity.supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.classroom_list_fragment_placeholder,
+ ClassroomListFragment(),
+ TAG_CLASSROOM_LIST_FRAGMENT,
+ ).commitNow()
+ }
}
- }
- private fun setUpNavigationDrawer() {
- val toolbar = activity.findViewById(R.id.classroom_list_activity_toolbar) as Toolbar
- activity.setSupportActionBar(toolbar)
- activity.supportActionBar!!.setDisplayShowHomeEnabled(true)
- navigationDrawerFragment = activity
- .supportFragmentManager
- .findFragmentById(
- R.id.classroom_list_activity_fragment_navigation_drawer
- ) as NavigationDrawerFragment
- navigationDrawerFragment!!.setUpDrawer(
- activity.findViewById(R.id.classroom_list_activity_drawer_layout) as DrawerLayout,
- toolbar, R.id.nav_home
- )
- }
+ private fun setUpNavigationDrawer() {
+ val toolbar = activity.findViewById(R.id.classroom_list_activity_toolbar) as Toolbar
+ activity.setSupportActionBar(toolbar)
+ activity.supportActionBar!!.setDisplayShowHomeEnabled(true)
+ navigationDrawerFragment =
+ activity
+ .supportFragmentManager
+ .findFragmentById(
+ R.id.classroom_list_activity_fragment_navigation_drawer,
+ ) as NavigationDrawerFragment
+ navigationDrawerFragment!!.setUpDrawer(
+ activity.findViewById(R.id.classroom_list_activity_drawer_layout) as DrawerLayout,
+ toolbar,
+ R.id.nav_home,
+ )
+ }
- private fun getClassroomListFragment(): ClassroomListFragment? {
- return activity.supportFragmentManager.findFragmentById(
- R.id.classroom_list_fragment_placeholder
- ) as ClassroomListFragment?
+ private fun getClassroomListFragment(): ClassroomListFragment? =
+ activity.supportFragmentManager.findFragmentById(
+ R.id.classroom_list_fragment_placeholder,
+ ) as ClassroomListFragment?
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragment.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragment.kt
index 172665fd74f..9dfba9e81fa 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragment.kt
@@ -29,10 +29,8 @@ class ClassroomListFragment :
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return classroomListFragmentPresenter.handleCreateView(inflater, container)
- }
+ savedInstanceState: Bundle?,
+ ): View? = classroomListFragmentPresenter.handleCreateView(inflater, container)
override fun onTopicSummaryClicked(topicSummary: TopicSummary) {
classroomListFragmentPresenter.onTopicSummaryClicked(topicSummary)
diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragmentPresenter.kt
index fe3d0de4f62..1269c1276c1 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragmentPresenter.kt
@@ -78,6 +78,7 @@ import javax.inject.Inject
const val CLASSROOM_LIST_SCREEN_TEST_TAG = "TEST_TAG.classroom_list_screen"
/** The presenter for [ClassroomListFragment]. */
+
class ClassroomListFragmentPresenter @Inject constructor(
private val activity: AppCompatActivity,
private val fragment: Fragment,
@@ -104,15 +105,22 @@ class ClassroomListFragmentPresenter @Inject constructor(
private val profileId = activity.intent.extractCurrentUserProfileId()
private var onBackPressedCallback: OnBackPressedCallback? = null
- /** Creates and returns the view for the [ClassroomListFragment]. */
- fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
- binding = ClassroomListFragmentBinding.inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
- logHomeActivityEvent()
+ /** Creates and returns the view for the [ClassroomListFragment]. */
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ ): View? {
+ binding =
+ ClassroomListFragmentBinding.inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
+
+ logHomeActivityEvent()
+
classroomListViewModel = ClassroomListViewModel(
activity,
@@ -130,202 +138,213 @@ class ClassroomListFragmentPresenter @Inject constructor(
translationController
)
- classroomListViewModel.homeItemViewModelListLiveData.observe(activity) {
- refreshComposeView()
- }
-
- classroomListViewModel.topicList.addOnListChangedCallback(
- object : ObservableList.OnListChangedCallback>() {
- override fun onChanged(sender: ObservableList) {}
-
- override fun onItemRangeChanged(
- sender: ObservableList,
- positionStart: Int,
- itemCount: Int
- ) {}
-
- override fun onItemRangeInserted(
- sender: ObservableList,
- positionStart: Int,
- itemCount: Int
- ) {
- refreshComposeView()
- }
- override fun onItemRangeMoved(
- sender: ObservableList,
- fromPosition: Int,
- toPosition: Int,
- itemCount: Int
- ) {}
-
- override fun onItemRangeRemoved(
- sender: ObservableList,
- positionStart: Int,
- itemCount: Int
- ) {}
+ classroomListViewModel.homeItemViewModelListLiveData.observe(activity) {
+ refreshComposeView()
}
- )
- profileManagementController.getProfile(profileId).toLiveData().observe(fragment) {
- processProfileResult(it)
- }
+ classroomListViewModel.topicList.addOnListChangedCallback(
+ object : ObservableList.OnListChangedCallback>() {
+ override fun onChanged(sender: ObservableList) {}
- return binding.root
- }
+ override fun onItemRangeChanged(
+ sender: ObservableList,
+ positionStart: Int,
+ itemCount: Int,
+ ) {}
- /** Routes to the play story view for the first story in the given topic summary. */
- fun onTopicSummaryClicked(topicSummary: TopicSummary) {
- routeToTopicPlayStoryListener.routeToTopicPlayStory(
- profileId,
- topicSummary.classroomId,
- topicSummary.topicId,
- topicSummary.firstStoryId
- )
- }
+ override fun onItemRangeInserted(
+ sender: ObservableList,
+ positionStart: Int,
+ itemCount: Int,
+ ) {
+ refreshComposeView()
+ }
- /** Triggers the view model to update the topic list. */
- fun onClassroomSummaryClicked(classroomSummary: ClassroomSummary) {
- val classroomId = classroomSummary.classroomId
- profileManagementController.updateLastSelectedClassroomId(profileId, classroomId)
- classroomListViewModel.fetchAndUpdateTopicList(classroomId)
- }
+ override fun onItemRangeMoved(
+ sender: ObservableList,
+ fromPosition: Int,
+ toPosition: Int,
+ itemCount: Int,
+ ) {}
- private fun refreshComposeView() {
- binding.composeView.apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- MaterialTheme {
- ClassroomListScreen()
- }
+ override fun onItemRangeRemoved(
+ sender: ObservableList,
+ positionStart: Int,
+ itemCount: Int,
+ ) {}
+ },
+ )
+
+ profileManagementController.getProfile(profileId).toLiveData().observe(fragment) {
+ processProfileResult(it)
}
+
+ return binding.root
}
- }
- /** Display a list of classroom-related items grouped by their types. */
- @OptIn(ExperimentalFoundationApi::class)
- @Composable
- fun ClassroomListScreen() {
- val groupedItems = (
- classroomListViewModel.homeItemViewModelListLiveData.value.orEmpty() +
- classroomListViewModel.topicList
+ /** Routes to the play story view for the first story in the given topic summary. */
+ fun onTopicSummaryClicked(topicSummary: TopicSummary) {
+ routeToTopicPlayStoryListener.routeToTopicPlayStory(
+ profileId,
+ topicSummary.classroomId,
+ topicSummary.topicId,
+ topicSummary.firstStoryId,
)
- .groupBy { it::class }
- val topicListSpanCount = integerResource(id = R.integer.home_span_count)
- val listState = rememberLazyListState()
- val classroomListIndex = groupedItems
- .flatMap { (type, items) -> items.map { type to it } }
- .indexOfFirst { it.first == AllClassroomsViewModel::class }
-
- LazyColumn(
- modifier = Modifier.testTag(CLASSROOM_LIST_SCREEN_TEST_TAG),
- state = listState
- ) {
- groupedItems.forEach { (type, items) ->
- when (type) {
- WelcomeViewModel::class -> items.forEach { item ->
- item { WelcomeText(welcomeViewModel = item as WelcomeViewModel) }
- }
- PromotedStoryListViewModel::class -> items.forEach { item ->
- item {
- PromotedStoryList(
- promotedStoryListViewModel = item as PromotedStoryListViewModel,
- machineLocale = machineLocale
- )
- }
- }
- ComingSoonTopicListViewModel::class -> items.forEach { item ->
- item {
- ComingSoonTopicList(
- comingSoonTopicListViewModel = item as ComingSoonTopicListViewModel,
- machineLocale = machineLocale
- )
- }
- }
- AllClassroomsViewModel::class -> items.forEach { _ ->
- item { AllClassroomsHeaderText() }
- }
- ClassroomSummaryViewModel::class -> stickyHeader {
- ClassroomList(
- classroomSummaryList = items.map { it as ClassroomSummaryViewModel },
- selectedClassroomId = classroomListViewModel.selectedClassroomId.get().orEmpty(),
- isSticky = listState.firstVisibleItemIndex >= classroomListIndex
- )
- }
- AllTopicsViewModel::class -> items.forEach { _ ->
- item { AllTopicsHeaderText() }
- }
- TopicSummaryViewModel::class -> {
- gridItems(
- data = items.map { it as TopicSummaryViewModel },
- columnCount = topicListSpanCount,
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- modifier = Modifier
- ) { itemData ->
- TopicCard(topicSummaryViewModel = itemData)
- }
+ }
+
+ /** Triggers the view model to update the topic list. */
+ fun onClassroomSummaryClicked(classroomSummary: ClassroomSummary) {
+ val classroomId = classroomSummary.classroomId
+ profileManagementController.updateLastSelectedClassroomId(profileId, classroomId)
+ classroomListViewModel.fetchAndUpdateTopicList(classroomId)
+ }
+
+ private fun refreshComposeView() {
+ binding.composeView.apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ MaterialTheme {
+ ClassroomListScreen()
}
}
}
}
- }
- private fun processProfileResult(result: AsyncResult) {
- when (result) {
- is AsyncResult.Success -> {
- val profile = result.value
- val profileType = profile.profileType
-
- if (enableOnboardingFlowV2.value && !profile.completedProfileOnboarding) {
- // These asynchronous API calls do not block or wait for their results. They execute in
- // the background and have minimal chances of interfering with the synchronous
- // `handleBackPress` call below.
- profileManagementController.markProfileOnboardingEnded(profileId)
- if (profileType == ProfileType.SOLE_LEARNER || profileType == ProfileType.SUPERVISOR) {
- appStartupStateController.markOnboardingFlowCompleted(profileId)
+ /** Display a list of classroom-related items grouped by their types. */
+ @OptIn(ExperimentalFoundationApi::class)
+ @Composable
+ fun ClassroomListScreen() {
+ val groupedItems =
+ (
+ classroomListViewModel.homeItemViewModelListLiveData.value.orEmpty() +
+ classroomListViewModel.topicList
+ ).groupBy { it::class }
+ val topicListSpanCount = integerResource(id = R.integer.home_span_count)
+ val listState = rememberLazyListState()
+ val classroomListIndex =
+ groupedItems
+ .flatMap { (type, items) -> items.map { type to it } }
+ .indexOfFirst { it.first == AllClassroomsViewModel::class }
+
+ LazyColumn(
+ modifier = Modifier.testTag(CLASSROOM_LIST_SCREEN_TEST_TAG),
+ state = listState,
+ ) {
+ groupedItems.forEach { (type, items) ->
+ when (type) {
+ WelcomeViewModel::class ->
+ items.forEach { item ->
+ item { WelcomeText(welcomeViewModel = item as WelcomeViewModel) }
+ }
+ PromotedStoryListViewModel::class ->
+ items.forEach { item ->
+ item {
+ PromotedStoryList(
+ promotedStoryListViewModel = item as PromotedStoryListViewModel,
+ machineLocale = machineLocale,
+ )
+ }
+ }
+ ComingSoonTopicListViewModel::class ->
+ items.forEach { item ->
+ item {
+ ComingSoonTopicList(
+ comingSoonTopicListViewModel = item as ComingSoonTopicListViewModel,
+ machineLocale = machineLocale,
+ )
+ }
+ }
+ AllClassroomsViewModel::class ->
+ items.forEach { _ ->
+ item { AllClassroomsHeaderText() }
+ }
+ ClassroomSummaryViewModel::class ->
+ stickyHeader {
+ ClassroomList(
+ classroomSummaryList = items.map { it as ClassroomSummaryViewModel },
+ selectedClassroomId = classroomListViewModel.selectedClassroomId.get().orEmpty(),
+ isSticky = listState.firstVisibleItemIndex >= classroomListIndex,
+ )
+ }
+ AllTopicsViewModel::class ->
+ items.forEach { _ ->
+ item { AllTopicsHeaderText() }
+ }
+ TopicSummaryViewModel::class -> {
+ gridItems(
+ data = items.map { it as TopicSummaryViewModel },
+ columnCount = topicListSpanCount,
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ modifier = Modifier,
+ ) { itemData ->
+ TopicCard(topicSummaryViewModel = itemData)
+ }
+ }
}
}
-
- // This synchronous function call executes independently of the async calls above.
- handleBackPress(profileType)
- }
- is AsyncResult.Failure -> {
- oppiaLogger.e(
- "ClassroomListFragment", "Failed to fetch profile with id:$profileId", result.error
- )
- Profile.getDefaultInstance()
- }
- is AsyncResult.Pending -> {
- Profile.getDefaultInstance()
}
}
- }
- private fun logHomeActivityEvent() {
- analyticsController.logImportantEvent(
- oppiaLogger.createOpenHomeContext(),
- profileId
- )
- }
+ private fun processProfileResult(result: AsyncResult) {
+ when (result) {
+ is AsyncResult.Success -> {
+ val profile = result.value
+ val profileType = profile.profileType
- private fun handleBackPress(profileType: ProfileType) {
- onBackPressedCallback?.remove()
+ if (enableOnboardingFlowV2.value && !profile.completedProfileOnboarding) {
+ // These asynchronous API calls do not block or wait for their results. They execute in
+ // the background and have minimal chances of interfering with the synchronous
+ // `handleBackPress` call below.
+ profileManagementController.markProfileOnboardingEnded(profileId)
+ if (profileType == ProfileType.SOLE_LEARNER || profileType == ProfileType.SUPERVISOR) {
+ appStartupStateController.markOnboardingFlowCompleted(profileId)
+ }
+ }
- onBackPressedCallback = object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- exitProfileListener.exitProfile(profileType)
- // The dispatcher can hold a reference to the host
- // so we need to null it out to prevent memory leaks.
- this.remove()
- onBackPressedCallback = null
+ // This synchronous function call executes independently of the async calls above.
+ handleBackPress(profileType)
+ }
+ is AsyncResult.Failure -> {
+ oppiaLogger.e(
+ "ClassroomListFragment",
+ "Failed to fetch profile with id:$profileId",
+ result.error,
+ )
+ Profile.getDefaultInstance()
+ }
+ is AsyncResult.Pending -> {
+ Profile.getDefaultInstance()
+ }
}
}
- onBackPressedCallback?.let { callback ->
- activity.onBackPressedDispatcher.addCallback(fragment, callback)
+ private fun logHomeActivityEvent() {
+ analyticsController.logImportantEvent(
+ oppiaLogger.createOpenHomeContext(),
+ profileId,
+ )
+ }
+
+ private fun handleBackPress(profileType: ProfileType) {
+ onBackPressedCallback?.remove()
+
+ onBackPressedCallback =
+ object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ exitProfileListener.exitProfile(profileType)
+ // The dispatcher can hold a reference to the host
+ // so we need to null it out to prevent memory leaks.
+ this.remove()
+ onBackPressedCallback = null
+ }
+ }
+
+ onBackPressedCallback?.let { callback ->
+ activity.onBackPressedDispatcher.addCallback(fragment, callback)
+ }
}
}
-}
/** Adds a grid of items to a LazyListScope with specified arrangement and item content. */
fun LazyListScope.gridItems(
@@ -344,14 +363,14 @@ fun LazyListScope.gridItems(
// Create a row with the specified horizontal arrangement and padding.
Row(
horizontalArrangement = horizontalArrangement,
- modifier = modifier
- .background(
- colorResource(id = R.color.component_color_classroom_topic_list_background_color)
- )
- .padding(
- horizontal = dimensionResource(id = R.dimen.classrooms_text_margin_start),
- vertical = 10.dp
- )
+ modifier =
+ modifier
+ .background(
+ colorResource(id = R.color.component_color_classroom_topic_list_background_color),
+ ).padding(
+ horizontal = dimensionResource(id = R.dimen.classrooms_text_margin_start),
+ vertical = 10.dp,
+ ),
) {
// Populate the row with columns.
for (columnIndex in 0 until columnCount) {
@@ -359,7 +378,7 @@ fun LazyListScope.gridItems(
if (itemIndex < size) {
Box(
modifier = Modifier.weight(1F, fill = true),
- propagateMinConstraints = true
+ propagateMinConstraints = true,
) {
itemContent(data[itemIndex]) // Provide content for each item.
}
@@ -372,20 +391,21 @@ fun LazyListScope.gridItems(
// Add bottom padding if it's the last row.
if (rowIndex == rows - 1) {
Spacer(
- modifier = Modifier
- .fillMaxWidth()
- .height(dimensionResource(id = R.dimen.home_fragment_padding_bottom))
- .background(
- colorResource(id = R.color.component_color_classroom_topic_list_background_color)
- )
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .height(dimensionResource(id = R.dimen.home_fragment_padding_bottom))
+ .background(
+ colorResource(id = R.color.component_color_classroom_topic_list_background_color),
+ ),
)
}
}
}
/** Retrieves the drawable resource ID for the lesson thumbnail based on its graphic type. */
-fun LessonThumbnail.getDrawableResource(): Int {
- return when (thumbnailGraphic) {
+fun LessonThumbnail.getDrawableResource(): Int =
+ when (thumbnailGraphic) {
LessonThumbnailGraphic.BAKER ->
R.drawable.lesson_thumbnail_graphic_baker
LessonThumbnailGraphic.CHILD_WITH_BOOK ->
@@ -433,4 +453,3 @@ fun LessonThumbnail.getDrawableResource(): Int {
else ->
R.drawable.topic_fractions_01
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListViewModel.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListViewModel.kt
index 2bf94934a1b..c1241e5d437 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListViewModel.kt
@@ -60,12 +60,12 @@ class ClassroomListViewModel(
private val storyEntityType: String,
private val resourceHandler: AppLanguageResourceHandler,
private val dateTimeUtil: DateTimeUtil,
- private val translationController: TranslationController
+ private val translationController: TranslationController,
) : ObservableViewModel() {
-
- private val promotedStoryListLimit = activity.resources.getInteger(
- R.integer.promoted_story_list_limit
- )
+ private val promotedStoryListLimit =
+ activity.resources.getInteger(
+ R.integer.promoted_story_list_limit,
+ )
/** An observable boolean property indicating the visibility state of the progress bar. */
val isProgressBarVisible = ObservableField(true)
@@ -98,24 +98,25 @@ class ClassroomListViewModel(
// This will block until all data providers return initial results (which may be default
// instances). If any of the data providers are pending or failed, the combined result will also
// be pending or failed.
- profileDataProvider.combineWith(
- promotedActivityListSummaryDataProvider,
- PROFILE_AND_PROMOTED_ACTIVITY_COMBINED_PROVIDER_ID
- ) { profile, promotedActivityList ->
- if (profile.numberOfLogins > 1) {
- listOfNotNull(
- computeWelcomeViewModel(profile),
- computePromotedActivityListViewModel(promotedActivityList)
- )
- } else {
- listOfNotNull(computeWelcomeViewModel(profile))
+ profileDataProvider
+ .combineWith(
+ promotedActivityListSummaryDataProvider,
+ PROFILE_AND_PROMOTED_ACTIVITY_COMBINED_PROVIDER_ID,
+ ) { profile, promotedActivityList ->
+ if (profile.numberOfLogins > 1) {
+ listOfNotNull(
+ computeWelcomeViewModel(profile),
+ computePromotedActivityListViewModel(promotedActivityList),
+ )
+ } else {
+ listOfNotNull(computeWelcomeViewModel(profile))
+ }
+ }.combineWith(
+ classroomSummaryListDataProvider,
+ CLASSROOM_LIST_FRAGMENT_COMBINED_PROVIDER_ID,
+ ) { homeItemViewModelList, classroomSummaryList ->
+ homeItemViewModelList + computeClassroomItemViewModelList(classroomSummaryList)
}
- }.combineWith(
- classroomSummaryListDataProvider,
- CLASSROOM_LIST_FRAGMENT_COMBINED_PROVIDER_ID
- ) { homeItemViewModelList, classroomSummaryList ->
- homeItemViewModelList + computeClassroomItemViewModelList(classroomSummaryList)
- }
}
/**
@@ -131,7 +132,7 @@ class ClassroomListViewModel(
oppiaLogger.e(
"ClassroomListFragment",
"No classroom list fragment available -- failed to retrieve fragment data.",
- itemListResult.error
+ itemListResult.error,
)
listOf()
}
@@ -148,11 +149,12 @@ class ClassroomListViewModel(
* Returns a [HomeItemViewModel] corresponding to the welcome message (see [WelcomeViewModel]), or null if
* the specified profile has insufficient information to show the welcome message.
*/
- private fun computeWelcomeViewModel(profile: Profile): HomeItemViewModel? {
- return if (profile.name.isNotEmpty()) {
+ private fun computeWelcomeViewModel(profile: Profile): HomeItemViewModel? =
+ if (profile.name.isNotEmpty()) {
WelcomeViewModel(profile.name, resourceHandler, dateTimeUtil)
- } else null
- }
+ } else {
+ null
+ }
/**
* Returns a [HomeItemViewModel] corresponding to the promoted stories(Recommended, Recently-played and
@@ -160,32 +162,36 @@ class ClassroomListViewModel(
* to be displayed for this learner or null if this profile does not have any promoted stories.
* Promoted stories are determined by any recent stories last-played stories or suggested stories started by this profile.
*/
- private fun computePromotedActivityListViewModel(
- promotedActivityList: PromotedActivityList
- ): HomeItemViewModel? {
+ private fun computePromotedActivityListViewModel(promotedActivityList: PromotedActivityList): HomeItemViewModel? {
when (promotedActivityList.recommendationTypeCase) {
PromotedActivityList.RecommendationTypeCase.PROMOTED_STORY_LIST -> {
- val storyViewModelList = computePromotedStoryViewModelList(
- promotedActivityList.promotedStoryList
- )
+ val storyViewModelList =
+ computePromotedStoryViewModelList(
+ promotedActivityList.promotedStoryList,
+ )
return if (storyViewModelList.isNotEmpty()) {
return PromotedStoryListViewModel(
activity,
storyViewModelList,
promotedActivityList,
- resourceHandler
+ resourceHandler,
)
- } else null
+ } else {
+ null
+ }
}
PromotedActivityList.RecommendationTypeCase.COMING_SOON_TOPIC_LIST -> {
- val comingSoonTopicsList = computeComingSoonTopicViewModelList(
- promotedActivityList.comingSoonTopicList
- )
+ val comingSoonTopicsList =
+ computeComingSoonTopicViewModelList(
+ promotedActivityList.comingSoonTopicList,
+ )
return if (comingSoonTopicsList.isNotEmpty()) {
return ComingSoonTopicListViewModel(
- comingSoonTopicsList
+ comingSoonTopicsList,
)
- } else null
+ } else {
+ null
+ }
}
else -> return null
}
@@ -196,32 +202,32 @@ class ClassroomListViewModel(
* for this profile (see [PromotedStoryViewModel]), or an empty list if the profile does not have any
* ongoing stories at all.
*/
- private fun computePromotedStoryViewModelList(
- promotedStoryList: PromotedStoryList
- ): List {
+ private fun computePromotedStoryViewModelList(promotedStoryList: PromotedStoryList): List {
with(promotedStoryList) {
- val storyList = when {
- suggestedStoryList.isNotEmpty() -> {
- if (recentlyPlayedStoryList.isNotEmpty() || olderPlayedStoryList.isNotEmpty()) {
- recentlyPlayedStoryList +
- olderPlayedStoryList +
+ val storyList =
+ when {
+ suggestedStoryList.isNotEmpty() -> {
+ if (recentlyPlayedStoryList.isNotEmpty() || olderPlayedStoryList.isNotEmpty()) {
+ recentlyPlayedStoryList +
+ olderPlayedStoryList +
+ suggestedStoryList
+ } else {
suggestedStoryList
- } else {
- suggestedStoryList
+ }
+ }
+ recentlyPlayedStoryList.isNotEmpty() -> {
+ recentlyPlayedStoryList
+ }
+ else -> {
+ olderPlayedStoryList
}
}
- recentlyPlayedStoryList.isNotEmpty() -> {
- recentlyPlayedStoryList
- }
- else -> {
- olderPlayedStoryList
- }
- }
// Check if at least one story in topic is completed. Prioritize recommended story over
// completed story topic.
val sortedStoryList = storyList.sortedByDescending { !it.isTopicLearned }
- return sortedStoryList.take(promotedStoryListLimit)
+ return sortedStoryList
+ .take(promotedStoryListLimit)
.mapIndexed { index, promotedStory ->
PromotedStoryViewModel(
activity,
@@ -230,7 +236,7 @@ class ClassroomListViewModel(
storyEntityType,
promotedStory,
translationController,
- index
+ index,
)
}
}
@@ -241,19 +247,16 @@ class ClassroomListViewModel(
* displayed for this profile (see [ComingSoonTopicsViewModel]), or an empty list if the profile does not have any
* ongoing stories at all.
*/
- private fun computeComingSoonTopicViewModelList(
- comingSoonTopicList: ComingSoonTopicList
- ): List {
- return comingSoonTopicList.upcomingTopicList.map { topicSummary ->
+ private fun computeComingSoonTopicViewModelList(comingSoonTopicList: ComingSoonTopicList): List =
+ comingSoonTopicList.upcomingTopicList.map { topicSummary ->
ComingSoonTopicsViewModel(
activity,
topicSummary,
topicEntityType,
comingSoonTopicList,
- translationController
+ translationController,
)
}
- }
/**
* Returns a list of [HomeItemViewModel]s corresponding to all the classroom summaries available
@@ -261,19 +264,18 @@ class ClassroomListViewModel(
* an empty list if there are no classrooms to display to the learner (caused by either
* error or pending data providers).
*/
- private fun computeClassroomItemViewModelList(
- classroomList: ClassroomList
- ): List {
- return listOf(AllClassroomsViewModel) +
+ private fun computeClassroomItemViewModelList(classroomList: ClassroomList): List =
+ listOf(AllClassroomsViewModel) +
classroomList.classroomSummaryList.map { ephemeralClassroomSummary ->
ClassroomSummaryViewModel(
fragment as ClassroomSummaryClickListener,
ephemeralClassroomSummary,
+
classroomEntityType,
translationController
+
)
}
- }
/**
* Returns a list of [HomeItemViewModel]s corresponding to all the lesson topics available and to
@@ -281,46 +283,50 @@ class ClassroomListViewModel(
* associated topics list header (see [AllTopicsViewModel]). Returns an empty list if there are
* no topics to display to the learner (caused by either error or pending data providers).
*/
- private fun computeAllTopicsItemsViewModelList(
- topicList: TopicList
- ): List {
- val allTopicsList = topicList.topicSummaryList.mapIndexed { topicIndex, ephemeralSummary ->
- TopicSummaryViewModel(
- activity,
- ephemeralSummary,
- topicEntityType,
- fragment as TopicSummaryClickListener,
- position = topicIndex,
- resourceHandler,
- translationController
- )
- }
+ private fun computeAllTopicsItemsViewModelList(topicList: TopicList): List {
+ val allTopicsList =
+ topicList.topicSummaryList.mapIndexed { topicIndex, ephemeralSummary ->
+ TopicSummaryViewModel(
+ activity,
+ ephemeralSummary,
+ topicEntityType,
+ fragment as TopicSummaryClickListener,
+ position = topicIndex,
+ resourceHandler,
+ translationController,
+ )
+ }
return if (allTopicsList.isNotEmpty()) {
listOf(AllTopicsViewModel) + allTopicsList
- } else emptyList()
+ } else {
+ emptyList()
+ }
}
/** Fetches and updates the topic list based on the provided or last selected classroom ID. */
fun fetchAndUpdateTopicList(classroomId: String = "") {
if (classroomId.isBlank()) {
// Retrieve the last selected classroom ID if no specific classroom ID is provided.
- profileManagementController.retrieveLastSelectedClassroomId(profileId)
- .toLiveData().observe(fragment) { lastSelectedClassroomIdResult ->
+ profileManagementController
+ .retrieveLastSelectedClassroomId(profileId)
+ .toLiveData()
+ .observe(fragment) { lastSelectedClassroomIdResult ->
when (lastSelectedClassroomIdResult) {
is AsyncResult.Success -> {
val lastSelectedClassroomId = lastSelectedClassroomIdResult.value
updateTopicList(
- if (lastSelectedClassroomId.isNullOrBlank())
+ if (lastSelectedClassroomId.isNullOrBlank()) {
TEST_CLASSROOM_ID_0
- else
+ } else {
lastSelectedClassroomId
+ },
)
}
is AsyncResult.Failure -> {
oppiaLogger.e(
"ClassroomListFragment",
"Failed to retrieve last selected classroom ID",
- lastSelectedClassroomIdResult.error
+ lastSelectedClassroomIdResult.error,
)
// Use a default classroom ID in case of failure.
updateTopicList(TEST_CLASSROOM_ID_0)
@@ -336,26 +342,28 @@ class ClassroomListViewModel(
private fun updateTopicList(classroomId: String) {
selectedClassroomId.set(classroomId)
- classroomController.getTopicList(
- profileId,
- classroomId
- ).toLiveData().observe(fragment) { topicListResult ->
- when (topicListResult) {
- is AsyncResult.Success -> {
- topicList.clear()
- computeAllTopicsItemsViewModelList(topicListResult.value).map { itemViewModel ->
- topicList.add(itemViewModel)
+ classroomController
+ .getTopicList(
+ profileId,
+ classroomId,
+ ).toLiveData()
+ .observe(fragment) { topicListResult ->
+ when (topicListResult) {
+ is AsyncResult.Success -> {
+ topicList.clear()
+ computeAllTopicsItemsViewModelList(topicListResult.value).map { itemViewModel ->
+ topicList.add(itemViewModel)
+ }
}
+ is AsyncResult.Failure -> {
+ oppiaLogger.e(
+ "ClassroomListFragment",
+ "Failed to retrieve topic list.",
+ topicListResult.error,
+ )
+ }
+ is AsyncResult.Pending -> {}
}
- is AsyncResult.Failure -> {
- oppiaLogger.e(
- "ClassroomListFragment",
- "Failed to retrieve topic list.",
- topicListResult.error
- )
- }
- is AsyncResult.Pending -> {}
}
- }
}
}
diff --git a/app/src/main/java/org/oppia/android/app/classroom/classroomlist/AllClassroomsHeaderText.kt b/app/src/main/java/org/oppia/android/app/classroom/classroomlist/AllClassroomsHeaderText.kt
index 2e611b069d2..ce3ba643449 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/classroomlist/AllClassroomsHeaderText.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/classroomlist/AllClassroomsHeaderText.kt
@@ -25,12 +25,13 @@ fun AllClassroomsHeaderText() {
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = dimensionResource(id = R.dimen.classrooms_list_header_text_size).value.sp,
- modifier = Modifier
- .testTag(ALL_CLASSROOMS_HEADER_TEST_TAG)
- .padding(
- start = dimensionResource(id = R.dimen.classrooms_text_margin_start),
- top = dimensionResource(id = R.dimen.classrooms_text_margin_top),
- end = dimensionResource(id = R.dimen.classrooms_text_margin_end),
- ),
+ modifier =
+ Modifier
+ .testTag(ALL_CLASSROOMS_HEADER_TEST_TAG)
+ .padding(
+ start = dimensionResource(id = R.dimen.classrooms_text_margin_start),
+ top = dimensionResource(id = R.dimen.classrooms_text_margin_top),
+ end = dimensionResource(id = R.dimen.classrooms_text_margin_end),
+ ),
)
}
diff --git a/app/src/main/java/org/oppia/android/app/classroom/classroomlist/ClassroomList.kt b/app/src/main/java/org/oppia/android/app/classroom/classroomlist/ClassroomList.kt
index be1182d7dcc..0829340ab94 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/classroomlist/ClassroomList.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/classroomlist/ClassroomList.kt
@@ -45,16 +45,18 @@ fun ClassroomList(
isSticky: Boolean,
) {
LazyRow(
- modifier = Modifier
- .testTag(CLASSROOM_LIST_TEST_TAG)
- .background(
- color = colorResource(id = R.color.component_color_shared_screen_primary_background_color)
+ modifier =
+ Modifier
+ .testTag(CLASSROOM_LIST_TEST_TAG)
+ .background(
+ color = colorResource(id = R.color.component_color_shared_screen_primary_background_color),
+ ),
+ contentPadding =
+ PaddingValues(
+ start = dimensionResource(id = R.dimen.classrooms_text_margin_start),
+ top = dimensionResource(id = R.dimen.classrooms_text_margin_bottom),
+ end = dimensionResource(id = R.dimen.classrooms_text_margin_end),
),
- contentPadding = PaddingValues(
- start = dimensionResource(id = R.dimen.classrooms_text_margin_start),
- top = dimensionResource(id = R.dimen.classrooms_text_margin_bottom),
- end = dimensionResource(id = R.dimen.classrooms_text_margin_end),
- ),
) {
items(classroomSummaryList) {
ClassroomCard(classroomSummaryViewModel = it, selectedClassroomId, isSticky)
@@ -71,36 +73,40 @@ fun ClassroomCard(
) {
val isCardSelected = classroomSummaryViewModel.classroomSummary.classroomId == selectedClassroomId
Card(
- modifier = Modifier
- .width(getClassroomCardWidth())
- .padding(
- start = dimensionResource(R.dimen.classrooms_card_margin_start),
- end = dimensionResource(R.dimen.classrooms_card_margin_end),
- )
- .clickable {
- classroomSummaryViewModel.handleClassroomClick()
+ modifier =
+ Modifier
+ .width(getClassroomCardWidth())
+ .padding(
+ start = dimensionResource(R.dimen.classrooms_card_margin_start),
+ end = dimensionResource(R.dimen.classrooms_card_margin_end),
+ ).clickable {
+ classroomSummaryViewModel.handleClassroomClick()
+ },
+ backgroundColor =
+ if (isCardSelected) {
+ colorResource(id = R.color.component_color_classroom_card_selected_color)
+ } else {
+ colorResource(id = R.color.component_color_classroom_card_color)
},
- backgroundColor = if (isCardSelected) {
- colorResource(id = R.color.component_color_classroom_card_selected_color)
- } else {
- colorResource(id = R.color.component_color_classroom_card_color)
- },
border = BorderStroke(2.dp, color = colorResource(id = R.color.color_def_oppia_green)),
elevation = dimensionResource(id = R.dimen.classrooms_card_elevation),
) {
Column(
- modifier = Modifier.padding(
- horizontal = dimensionResource(id = R.dimen.classrooms_card_padding_horizontal),
- vertical = if (isSticky) {
- dimensionResource(id = R.dimen.classrooms_card_collapsed_padding_vertical)
- } else {
- dimensionResource(id = R.dimen.classrooms_card_padding_vertical)
- },
- ),
+ modifier =
+ Modifier.padding(
+ horizontal = dimensionResource(id = R.dimen.classrooms_card_padding_horizontal),
+ vertical =
+ if (isSticky) {
+ dimensionResource(id = R.dimen.classrooms_card_collapsed_padding_vertical)
+ } else {
+ dimensionResource(id = R.dimen.classrooms_card_padding_vertical)
+ },
+ ),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
AnimatedVisibility(visible = !isSticky) {
+
ThumbnailImage(
entityId = classroomSummaryViewModel.classroomSummary.classroomId,
entityType = classroomSummaryViewModel.entityType,
@@ -109,6 +115,7 @@ fun ClassroomCard(
.testTag("${CLASSROOM_CARD_ICON_TEST_TAG}_${classroomSummaryViewModel.title}")
.padding(bottom = dimensionResource(id = R.dimen.classrooms_card_icon_padding_bottom))
.size(size = dimensionResource(id = R.dimen.classrooms_card_icon_size)),
+
)
}
Text(
@@ -130,8 +137,9 @@ private fun getClassroomCardWidth(): Dp {
val topicCardHorizontalMargin = 8.dp
val topicListSpanCount = integerResource(id = R.integer.home_span_count)
- val totalTopicCardWidth = screenWidth -
- (horizontalPadding.times(2) + (topicCardHorizontalMargin * (topicListSpanCount - 1) * 2))
+ val totalTopicCardWidth =
+ screenWidth -
+ (horizontalPadding.times(2) + (topicCardHorizontalMargin * (topicListSpanCount - 1) * 2))
return totalTopicCardWidth.div(topicListSpanCount)
}
diff --git a/app/src/main/java/org/oppia/android/app/classroom/promotedlist/ComingSoonTopicList.kt b/app/src/main/java/org/oppia/android/app/classroom/promotedlist/ComingSoonTopicList.kt
index cdd42b37da8..ee1b1ead06a 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/promotedlist/ComingSoonTopicList.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/promotedlist/ComingSoonTopicList.kt
@@ -51,24 +51,25 @@ fun ComingSoonTopicList(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Medium,
fontSize = dimensionResource(id = R.dimen.coming_soon_topic_list_header_text_size).value.sp,
- modifier = Modifier
- .padding(
- start = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_start),
- top = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_top),
- end = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_end),
- )
- .testTag(COMING_SOON_TOPIC_LIST_HEADER_TEST_TAG),
+ modifier =
+ Modifier
+ .padding(
+ start = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_start),
+ top = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_top),
+ end = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_end),
+ ).testTag(COMING_SOON_TOPIC_LIST_HEADER_TEST_TAG),
)
LazyRow(
- modifier = Modifier
- .padding(
- top = dimensionResource(id = R.dimen.coming_soon_topic_list_padding)
- )
- .testTag(COMING_SOON_TOPIC_LIST_TEST_TAG),
- contentPadding = PaddingValues(
- start = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_start),
- end = dimensionResource(id = R.dimen.home_padding_end),
- ),
+ modifier =
+ Modifier
+ .padding(
+ top = dimensionResource(id = R.dimen.coming_soon_topic_list_padding),
+ ).testTag(COMING_SOON_TOPIC_LIST_TEST_TAG),
+ contentPadding =
+ PaddingValues(
+ start = dimensionResource(id = R.dimen.coming_soon_topic_list_layout_margin_start),
+ end = dimensionResource(id = R.dimen.home_padding_end),
+ ),
) {
items(comingSoonTopicListViewModel.comingSoonTopicList) {
ComingSoonTopicCard(
@@ -86,43 +87,48 @@ fun ComingSoonTopicCard(
machineLocale: OppiaLocale.MachineLocale,
) {
Card(
- modifier = Modifier
- .width(dimensionResource(id = R.dimen.coming_soon_topic_card_width))
- .padding(
- start = dimensionResource(id = R.dimen.coming_soon_topic_card_layout_margin_start),
- end = dimensionResource(id = R.dimen.coming_soon_topic_card_layout_margin_end),
- bottom = dimensionResource(id = R.dimen.coming_soon_topic_card_layout_margin_bottom),
- ),
+ modifier =
+ Modifier
+ .width(dimensionResource(id = R.dimen.coming_soon_topic_card_width))
+ .padding(
+ start = dimensionResource(id = R.dimen.coming_soon_topic_card_layout_margin_start),
+ end = dimensionResource(id = R.dimen.coming_soon_topic_card_layout_margin_end),
+ bottom = dimensionResource(id = R.dimen.coming_soon_topic_card_layout_margin_bottom),
+ ),
elevation = dimensionResource(id = R.dimen.topic_card_elevation),
) {
Box(
- contentAlignment = Alignment.TopEnd
+ contentAlignment = Alignment.TopEnd,
) {
Column(
verticalArrangement = Arrangement.Center,
) {
+
ThumbnailImage(
entityId = comingSoonTopicsViewModel.topicSummary.topicId,
entityType = comingSoonTopicsViewModel.entityType,
lessonThumbnail = comingSoonTopicsViewModel.topicSummary.lessonThumbnail,
modifier = Modifier.aspectRatio(4f / 3f)
+
)
ComingSoonTopicCardTextSection(comingSoonTopicsViewModel)
}
Text(
- text = machineLocale
- .run { stringResource(id = R.string.coming_soon).toMachineUpperCase() },
- modifier = Modifier
- .background(
- color = colorResource(
- id = R.color.component_color_coming_soon_rect_background_start_color
+ text =
+ machineLocale
+ .run { stringResource(id = R.string.coming_soon).toMachineUpperCase() },
+ modifier =
+ Modifier
+ .background(
+ color =
+ colorResource(
+ id = R.color.component_color_coming_soon_rect_background_start_color,
+ ),
+ shape = RoundedCornerShape(topEnd = 4.dp, bottomStart = 12.dp),
+ ).padding(
+ horizontal = dimensionResource(id = R.dimen.coming_soon_text_padding_horizontal),
+ vertical = dimensionResource(id = R.dimen.coming_soon_text_padding_vertical),
),
- shape = RoundedCornerShape(topEnd = 4.dp, bottomStart = 12.dp),
- )
- .padding(
- horizontal = dimensionResource(id = R.dimen.coming_soon_text_padding_horizontal),
- vertical = dimensionResource(id = R.dimen.coming_soon_text_padding_vertical),
- ),
fontSize = 12.sp,
color = colorResource(id = R.color.component_color_shared_secondary_4_text_color),
fontFamily = FontFamily.SansSerif,
@@ -136,25 +142,28 @@ fun ComingSoonTopicCard(
@Composable
fun ComingSoonTopicCardTextSection(comingSoonTopicsViewModel: ComingSoonTopicsViewModel) {
Column(
- modifier = Modifier
- .fillMaxWidth()
- .background(
- color = colorResource(
- id = R.color.component_color_shared_topic_card_item_background_color
- )
- ),
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .background(
+ color =
+ colorResource(
+ id = R.color.component_color_shared_topic_card_item_background_color,
+ ),
+ ),
verticalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = comingSoonTopicsViewModel.topicTitle,
- modifier = Modifier
- .fillMaxWidth()
- .padding(
- start = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding),
- top = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding),
- end = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding),
- bottom = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding_bottom),
- ),
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(
+ start = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding),
+ top = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding),
+ end = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding),
+ bottom = dimensionResource(id = R.dimen.coming_soon_topic_card_text_padding_bottom),
+ ),
color = colorResource(id = R.color.component_color_shared_secondary_4_text_color),
fontFamily = FontFamily.SansSerif,
fontSize = dimensionResource(id = R.dimen.topic_list_item_text_size).value.sp,
diff --git a/app/src/main/java/org/oppia/android/app/classroom/promotedlist/PromotedStoryList.kt b/app/src/main/java/org/oppia/android/app/classroom/promotedlist/PromotedStoryList.kt
index b668febb073..d195d6be568 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/promotedlist/PromotedStoryList.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/promotedlist/PromotedStoryList.kt
@@ -53,14 +53,15 @@ fun PromotedStoryList(
contentAlignment = Alignment.Center,
) {
Row(
- modifier = Modifier
- .testTag(PROMOTED_STORY_LIST_HEADER_TEST_TAG)
- .fillMaxWidth()
- .padding(
- start = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_start),
- top = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_top),
- end = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_end),
- ),
+ modifier =
+ Modifier
+ .testTag(PROMOTED_STORY_LIST_HEADER_TEST_TAG)
+ .fillMaxWidth()
+ .padding(
+ start = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_start),
+ top = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_top),
+ end = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_end),
+ ),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
@@ -70,8 +71,9 @@ fun PromotedStoryList(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = dimensionResource(id = R.dimen.promoted_story_list_header_text_size).value.sp,
- modifier = Modifier
- .weight(weight = 1f, fill = false),
+ modifier =
+ Modifier
+ .weight(weight = 1f, fill = false),
)
if (promotedStoryListViewModel.getViewAllButtonVisibility() == View.VISIBLE) {
Text(
@@ -79,31 +81,34 @@ fun PromotedStoryList(
color = colorResource(id = R.color.component_color_home_activity_view_all_text_color),
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Medium,
- fontSize = dimensionResource(
- id = R.dimen.promoted_story_list_view_all_text_size
- ).value.sp,
- modifier = Modifier
- .padding(
- start = dimensionResource(id = R.dimen.promoted_story_list_view_all_padding_start)
- )
- .clickable { promotedStoryListViewModel.clickOnViewAll() },
+ fontSize =
+ dimensionResource(
+ id = R.dimen.promoted_story_list_view_all_text_size,
+ ).value.sp,
+ modifier =
+ Modifier
+ .padding(
+ start = dimensionResource(id = R.dimen.promoted_story_list_view_all_padding_start),
+ ).clickable { promotedStoryListViewModel.clickOnViewAll() },
)
}
}
}
LazyRow(
- modifier = Modifier
- .testTag(PROMOTED_STORY_LIST_TEST_TAG)
- .padding(top = dimensionResource(id = R.dimen.promoted_story_list_padding)),
- contentPadding = PaddingValues(
- start = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_start),
- end = promotedStoryListViewModel.endPadding.dp,
- ),
+ modifier =
+ Modifier
+ .testTag(PROMOTED_STORY_LIST_TEST_TAG)
+ .padding(top = dimensionResource(id = R.dimen.promoted_story_list_padding)),
+ contentPadding =
+ PaddingValues(
+ start = dimensionResource(id = R.dimen.promoted_story_list_layout_margin_start),
+ end = promotedStoryListViewModel.endPadding.dp,
+ ),
) {
items(promotedStoryListViewModel.promotedStoryList) {
PromotedStoryCard(
promotedStoryViewModel = it,
- machineLocale = machineLocale
+ machineLocale = machineLocale,
)
}
}
@@ -117,106 +122,125 @@ fun PromotedStoryCard(
) {
val cardLayoutWidth = promotedStoryViewModel.computeLayoutWidth()
val cardColumnModifier =
- if (cardLayoutWidth == ViewGroup.LayoutParams.MATCH_PARENT) Modifier.fillMaxWidth()
- else Modifier.width(promotedStoryViewModel.computeLayoutWidth().dp)
+ if (cardLayoutWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
+ Modifier.fillMaxWidth()
+ } else {
+ Modifier.width(promotedStoryViewModel.computeLayoutWidth().dp)
+ }
Card(
- modifier = Modifier
- .width(width = dimensionResource(id = R.dimen.promoted_story_card_layout_width))
- .padding(
- start = dimensionResource(id = R.dimen.promoted_story_card_layout_margin_start),
- end = dimensionResource(id = R.dimen.promoted_story_card_layout_margin_end),
- bottom = dimensionResource(id = R.dimen.promoted_story_card_layout_margin_bottom),
- )
- .clickable { promotedStoryViewModel.clickOnStoryTile() },
- backgroundColor = colorResource(
- id = R.color.component_color_classroom_promoted_list_card_background_color
- ),
+ modifier =
+ Modifier
+ .width(width = dimensionResource(id = R.dimen.promoted_story_card_layout_width))
+ .padding(
+ start = dimensionResource(id = R.dimen.promoted_story_card_layout_margin_start),
+ end = dimensionResource(id = R.dimen.promoted_story_card_layout_margin_end),
+ bottom = dimensionResource(id = R.dimen.promoted_story_card_layout_margin_bottom),
+ ).clickable { promotedStoryViewModel.clickOnStoryTile() },
+ backgroundColor =
+ colorResource(
+ id = R.color.component_color_classroom_promoted_list_card_background_color,
+ ),
elevation = dimensionResource(id = R.dimen.promoted_story_card_elevation),
) {
Column(
- modifier = cardColumnModifier
+ modifier = cardColumnModifier,
) {
+
ThumbnailImage(
entityId = promotedStoryViewModel.promotedStory.storyId,
entityType = promotedStoryViewModel.entityType,
lessonThumbnail = promotedStoryViewModel.promotedStory.lessonThumbnail,
modifier = Modifier.aspectRatio(16f / 9f)
+
)
Text(
text = promotedStoryViewModel.nextChapterTitle,
- modifier = Modifier.padding(
- start = dimensionResource(
- id = R.dimen.promoted_story_card_padding_horizontal
+ modifier =
+ Modifier.padding(
+ start =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_padding_horizontal,
+ ),
+ top =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_padding_vertical,
+ ),
+ end =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_padding_horizontal,
+ ),
),
- top = dimensionResource(
- id = R.dimen.promoted_story_card_padding_vertical
- ),
- end = dimensionResource(
- id = R.dimen.promoted_story_card_padding_horizontal
- ),
- ),
color = colorResource(id = R.color.component_color_shared_primary_text_color),
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Medium,
- fontSize = dimensionResource(
- id = R.dimen.promoted_story_card_chapter_title_text_size
- ).value.sp,
+ fontSize =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_chapter_title_text_size,
+ ).value.sp,
textAlign = TextAlign.Start,
maxLines = 1,
- overflow = TextOverflow.Ellipsis
+ overflow = TextOverflow.Ellipsis,
)
Text(
text = machineLocale.run { promotedStoryViewModel.topicTitle.toMachineUpperCase() },
- modifier = Modifier.padding(
- start = dimensionResource(
- id = R.dimen.promoted_story_card_padding_horizontal
- ),
- top = dimensionResource(
- id = R.dimen.promoted_story_card_padding_vertical
+ modifier =
+ Modifier.padding(
+ start =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_padding_horizontal,
+ ),
+ top =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_padding_vertical,
+ ),
+ end =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_padding_horizontal,
+ ),
),
- end = dimensionResource(
- id = R.dimen.promoted_story_card_padding_horizontal
+ color =
+ colorResource(
+ id = R.color.component_color_shared_story_card_topic_name_text_color,
),
- ),
- color = colorResource(
- id = R.color.component_color_shared_story_card_topic_name_text_color
- ),
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Light,
- fontSize = dimensionResource(
- id = R.dimen.promoted_story_card_topic_title_text_size
- ).value.sp,
+ fontSize =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_topic_title_text_size,
+ ).value.sp,
textAlign = TextAlign.Start,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = machineLocale.run { promotedStoryViewModel.classroomTitle.toMachineUpperCase() },
- modifier = Modifier
- .padding(
- horizontal = dimensionResource(id = R.dimen.promoted_story_card_padding_horizontal),
- vertical = dimensionResource(id = R.dimen.promoted_story_card_padding_vertical),
- )
- .border(
- width = 2.dp,
- color = colorResource(
- id = R.color.component_color_classroom_promoted_list_classroom_label_color
+ modifier =
+ Modifier
+ .padding(
+ horizontal = dimensionResource(id = R.dimen.promoted_story_card_padding_horizontal),
+ vertical = dimensionResource(id = R.dimen.promoted_story_card_padding_vertical),
+ ).border(
+ width = 2.dp,
+ color =
+ colorResource(
+ id = R.color.component_color_classroom_promoted_list_classroom_label_color,
+ ),
+ shape = RoundedCornerShape(50),
+ ).padding(
+ horizontal = dimensionResource(id = R.dimen.promoted_story_card_padding_horizontal),
+ vertical = dimensionResource(id = R.dimen.promoted_story_card_padding_vertical),
),
- shape = RoundedCornerShape(50)
- )
- .padding(
- horizontal = dimensionResource(id = R.dimen.promoted_story_card_padding_horizontal),
- vertical = dimensionResource(id = R.dimen.promoted_story_card_padding_vertical),
+ color =
+ colorResource(
+ id = R.color.component_color_classroom_promoted_list_classroom_label_color,
),
- color = colorResource(
- id = R.color.component_color_classroom_promoted_list_classroom_label_color
- ),
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Medium,
- fontSize = dimensionResource(
- id = R.dimen.promoted_story_card_classroom_title_text_size
- ).value.sp,
+ fontSize =
+ dimensionResource(
+ id = R.dimen.promoted_story_card_classroom_title_text_size,
+ ).value.sp,
textAlign = TextAlign.Start,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
diff --git a/app/src/main/java/org/oppia/android/app/classroom/topiclist/AllTopicsHeaderText.kt b/app/src/main/java/org/oppia/android/app/classroom/topiclist/AllTopicsHeaderText.kt
index a7f6336759c..aeba8a575a9 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/topiclist/AllTopicsHeaderText.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/topiclist/AllTopicsHeaderText.kt
@@ -27,17 +27,17 @@ fun AllTopicsHeaderText() {
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Medium,
fontSize = dimensionResource(id = R.dimen.all_topics_text_size).value.sp,
- modifier = Modifier
- .testTag(ALL_TOPICS_HEADER_TEST_TAG)
- .fillMaxWidth()
- .background(
- colorResource(id = R.color.component_color_classroom_topic_list_background_color)
- )
- .padding(
- start = dimensionResource(id = R.dimen.all_topics_text_margin_start),
- top = dimensionResource(id = R.dimen.all_topics_text_margin_top),
- end = dimensionResource(id = R.dimen.all_topics_text_margin_end),
- bottom = dimensionResource(id = R.dimen.all_topics_text_margin_bottom),
- ),
+ modifier =
+ Modifier
+ .testTag(ALL_TOPICS_HEADER_TEST_TAG)
+ .fillMaxWidth()
+ .background(
+ colorResource(id = R.color.component_color_classroom_topic_list_background_color),
+ ).padding(
+ start = dimensionResource(id = R.dimen.all_topics_text_margin_start),
+ top = dimensionResource(id = R.dimen.all_topics_text_margin_top),
+ end = dimensionResource(id = R.dimen.all_topics_text_margin_end),
+ bottom = dimensionResource(id = R.dimen.all_topics_text_margin_bottom),
+ ),
)
}
diff --git a/app/src/main/java/org/oppia/android/app/classroom/topiclist/TopicCard.kt b/app/src/main/java/org/oppia/android/app/classroom/topiclist/TopicCard.kt
index 8707851f5bf..e71ac05c1e7 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/topiclist/TopicCard.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/topiclist/TopicCard.kt
@@ -27,22 +27,24 @@ import org.oppia.android.app.home.topiclist.TopicSummaryViewModel
@Composable
fun TopicCard(topicSummaryViewModel: TopicSummaryViewModel) {
Card(
- modifier = Modifier
- .padding(
- start = dimensionResource(R.dimen.topic_card_margin_start),
- end = dimensionResource(R.dimen.topic_card_margin_end),
- )
- .clickable { topicSummaryViewModel.clickOnSummaryTile() },
+ modifier =
+ Modifier
+ .padding(
+ start = dimensionResource(R.dimen.topic_card_margin_start),
+ end = dimensionResource(R.dimen.topic_card_margin_end),
+ ).clickable { topicSummaryViewModel.clickOnSummaryTile() },
elevation = dimensionResource(id = R.dimen.topic_card_elevation),
) {
Column(
verticalArrangement = Arrangement.Center,
) {
+
ThumbnailImage(
entityId = topicSummaryViewModel.topicSummary.topicId,
entityType = topicSummaryViewModel.entityType,
lessonThumbnail = topicSummaryViewModel.topicSummary.topicThumbnail,
modifier = Modifier.aspectRatio(4f / 3f)
+
)
TopicCardTextSection(topicSummaryViewModel)
}
@@ -53,23 +55,26 @@ fun TopicCard(topicSummaryViewModel: TopicSummaryViewModel) {
@Composable
fun TopicCardTextSection(topicSummaryViewModel: TopicSummaryViewModel) {
Column(
- modifier = Modifier
- .fillMaxWidth()
- .background(
- color = colorResource(
- id = R.color.component_color_shared_topic_card_item_background_color
- )
- ),
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .background(
+ color =
+ colorResource(
+ id = R.color.component_color_shared_topic_card_item_background_color,
+ ),
+ ),
verticalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = topicSummaryViewModel.title,
- modifier = Modifier
- .padding(
- start = dimensionResource(id = R.dimen.topic_list_item_text_padding),
- top = dimensionResource(id = R.dimen.topic_list_item_text_padding),
- end = dimensionResource(id = R.dimen.topic_list_item_text_padding)
- ),
+ modifier =
+ Modifier
+ .padding(
+ start = dimensionResource(id = R.dimen.topic_list_item_text_padding),
+ top = dimensionResource(id = R.dimen.topic_list_item_text_padding),
+ end = dimensionResource(id = R.dimen.topic_list_item_text_padding),
+ ),
color = colorResource(id = R.color.component_color_shared_secondary_4_text_color),
fontFamily = FontFamily.SansSerif,
fontSize = dimensionResource(id = R.dimen.topic_list_item_text_size).value.sp,
@@ -79,8 +84,9 @@ fun TopicCardTextSection(topicSummaryViewModel: TopicSummaryViewModel) {
)
Text(
text = topicSummaryViewModel.computeLessonCountText(),
- modifier = Modifier
- .padding(all = dimensionResource(id = R.dimen.topic_list_item_text_padding)),
+ modifier =
+ Modifier
+ .padding(all = dimensionResource(id = R.dimen.topic_list_item_text_padding)),
color = colorResource(id = R.color.component_color_shared_secondary_4_text_color),
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Light,
diff --git a/app/src/main/java/org/oppia/android/app/classroom/welcome/WelcomeText.kt b/app/src/main/java/org/oppia/android/app/classroom/welcome/WelcomeText.kt
index f70bcd293b0..7ec1f728660 100644
--- a/app/src/main/java/org/oppia/android/app/classroom/welcome/WelcomeText.kt
+++ b/app/src/main/java/org/oppia/android/app/classroom/welcome/WelcomeText.kt
@@ -23,29 +23,30 @@ const val WELCOME_TEST_TAG = "TEST_TAG.welcome"
fun WelcomeText(welcomeViewModel: WelcomeViewModel) {
val outerPadding = dimensionResource(id = R.dimen.home_welcome_outer_padding)
val textMarginEnd = dimensionResource(id = R.dimen.home_welcome_text_view_margin_end)
- val greetingLineColor = colorResource(
- id = R.color.component_color_home_activity_layout_greeting_text_line_color
- )
+ val greetingLineColor =
+ colorResource(
+ id = R.color.component_color_home_activity_layout_greeting_text_line_color,
+ )
Text(
text = welcomeViewModel.computeWelcomeText(),
- modifier = Modifier
- .testTag(WELCOME_TEST_TAG)
- .padding(
- start = outerPadding,
- top = outerPadding,
- end = outerPadding + textMarginEnd,
- )
- .drawBehind {
- val strokeWidthPx = 6.dp.toPx()
- val verticalOffset = size.height + 4.dp.toPx()
- drawLine(
- color = greetingLineColor,
- strokeWidth = strokeWidthPx,
- start = Offset(x = 0f, y = verticalOffset),
- end = Offset(x = size.width, y = verticalOffset),
- )
- },
+ modifier =
+ Modifier
+ .testTag(WELCOME_TEST_TAG)
+ .padding(
+ start = outerPadding,
+ top = outerPadding,
+ end = outerPadding + textMarginEnd,
+ ).drawBehind {
+ val strokeWidthPx = 6.dp.toPx()
+ val verticalOffset = size.height + 4.dp.toPx()
+ drawLine(
+ color = greetingLineColor,
+ strokeWidth = strokeWidthPx,
+ start = Offset(x = 0f, y = verticalOffset),
+ end = Offset(x = size.width, y = verticalOffset),
+ )
+ },
color = colorResource(id = R.color.component_color_shared_primary_text_color),
fontSize = dimensionResource(id = R.dimen.home_welcome_text_size).value.sp,
fontFamily = FontFamily.SansSerif,
diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryItemViewModel.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryItemViewModel.kt
index cb28de75047..98198fdaa3a 100644
--- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryItemViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryItemViewModel.kt
@@ -15,18 +15,22 @@ class CompletedStoryItemViewModel(
val completedStory: CompletedStory,
val entityType: String,
private val intentFactoryShim: IntentFactoryShim,
- translationController: TranslationController
-) : ObservableViewModel(), RouteToTopicPlayStoryListener {
+ translationController: TranslationController,
+) : ObservableViewModel(),
+ RouteToTopicPlayStoryListener {
/** Holds lazily loaded completedStoryName [String] value. */
val completedStoryName by lazy {
translationController.extractString(
- completedStory.storyTitle, completedStory.storyWrittenTranslationContext
+ completedStory.storyTitle,
+ completedStory.storyWrittenTranslationContext,
)
}
+
/** Holds lazily loaded topicName [String] value. */
val topicName by lazy {
translationController.extractString(
- completedStory.topicTitle, completedStory.topicWrittenTranslationContext
+ completedStory.topicTitle,
+ completedStory.topicWrittenTranslationContext,
)
}
@@ -36,7 +40,7 @@ class CompletedStoryItemViewModel(
profileId,
completedStory.classroomId,
completedStory.topicId,
- completedStory.storyId
+ completedStory.storyId,
)
}
@@ -44,15 +48,16 @@ class CompletedStoryItemViewModel(
profileId: ProfileId,
classroomId: String,
topicId: String,
- storyId: String
+ storyId: String,
) {
- val intent = intentFactoryShim.createTopicPlayStoryActivityIntent(
- activity.applicationContext,
- profileId.internalId,
- classroomId,
- topicId,
- storyId
- )
+ val intent =
+ intentFactoryShim.createTopicPlayStoryActivityIntent(
+ activity.applicationContext,
+ profileId.internalId,
+ classroomId,
+ topicId,
+ storyId,
+ )
activity.startActivity(intent)
}
}
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 6d4fe12f3d9..0a934dd1ef0 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
@@ -29,12 +29,16 @@ class CompletedStoryListActivity : InjectableAutoLocalizedAppCompatActivity() {
// TODO(#1655): Re-restrict access to fields in tests post-Gradle.
/** Returns a new [Intent] to route to [CompletedStoryListActivity] for a specified profile ID. */
- fun createCompletedStoryListActivityIntent(context: Context, internalProfileId: Int): Intent {
+ fun createCompletedStoryListActivityIntent(
+ context: Context,
+ internalProfileId: Int,
+ ): Intent {
val profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build()
- val intent = Intent(context, CompletedStoryListActivity::class.java).apply {
- decorateWithUserProfileId(profileId)
- decorateWithScreenName(COMPLETED_STORY_LIST_ACTIVITY)
- }
+ 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/CompletedStoryListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt
index cb0cebfc2fa..95eacb1cd50 100644
--- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt
@@ -8,30 +8,44 @@ import javax.inject.Inject
/** The presenter for [CompletedStoryListActivity]. */
@ActivityScope
-class CompletedStoryListActivityPresenter @Inject constructor(
- private val activity: AppCompatActivity
-) {
+class CompletedStoryListActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ ) {
+ /** Initializes views for [CompletedStoryListActivity] and binds [CompletedStoryListFragment]. */
+ fun handleOnCreate(internalProfileId: Int) {
+ activity.setContentView(R.layout.completed_story_list_activity)
+ if (getCompletedStoryListFragment() == null) {
+ activity
+ .supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.completed_story_list_fragment_placeholder,
+ CompletedStoryListFragment.newInstance(internalProfileId),
+ CompletedStoryListFragment.COMPLETED_STORY_LIST_FRAGMENT_TAG,
+ ).commitNow()
+ }
+ }
+<<<<<<< HEAD
+ private fun getCompletedStoryListFragment(): CompletedStoryListFragment? =
+=======
/** Initializes views for [CompletedStoryListActivity] and binds [CompletedStoryListFragment]. */
fun handleOnCreate(profileId: ProfileId) {
activity.setContentView(R.layout.completed_story_list_activity)
if (getCompletedStoryListFragment() == null) {
+>>>>>>> 42210e8069394528330be84c5f4893bb2dafc2bf
activity
.supportFragmentManager
- .beginTransaction()
- .add(
+ .findFragmentById(
R.id.completed_story_list_fragment_placeholder,
+<<<<<<< HEAD
+ ) as CompletedStoryListFragment?
+=======
CompletedStoryListFragment.newInstance(profileId),
CompletedStoryListFragment.COMPLETED_STORY_LIST_FRAGMENT_TAG
).commitNow()
}
+>>>>>>> 42210e8069394528330be84c5f4893bb2dafc2bf
}
-
- private fun getCompletedStoryListFragment(): CompletedStoryListFragment? {
- return activity
- .supportFragmentManager
- .findFragmentById(
- R.id.completed_story_list_fragment_placeholder
- ) as CompletedStoryListFragment?
- }
-}
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 e1f0bb3b4aa..a6a3787c293 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
@@ -22,9 +22,10 @@ class CompletedStoryListFragment : InjectableFragment() {
/** Returns a new [CompletedStoryListFragment] to display corresponding to the specified profile ID. */
fun newInstance(profileId: ProfileId): CompletedStoryListFragment {
return CompletedStoryListFragment().apply {
- arguments = Bundle().apply {
- decorateWithUserProfileId(profileId)
- }
+ arguments =
+ Bundle().apply {
+ decorateWithUserProfileId(profileId)
+ }
}
}
}
@@ -40,16 +41,21 @@ class CompletedStoryListFragment : InjectableFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
+ savedInstanceState: Bundle?,
): View? {
- val arguments = checkNotNull(arguments) {
- "Expected arguments to be passed to CompletedStoryListFragment"
- }
+ val arguments =
+ checkNotNull(arguments) {
+ "Expected arguments to be passed to CompletedStoryListFragment"
+ }
val profileId = arguments.extractCurrentUserProfileId()
return completedStoryListFragmentPresenter.handleCreateView(
inflater,
container,
+<<<<<<< HEAD
+ internalProfileId,
+=======
profileId
+>>>>>>> 42210e8069394528330be84c5f4893bb2dafc2bf
)
}
}
diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt
index 02b2ae9cd68..ea4aa0ea642 100644
--- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt
@@ -14,15 +14,48 @@ import org.oppia.android.databinding.CompletedStoryListFragmentBinding
import javax.inject.Inject
/** The presenter for [CompletedStoryListFragment]. */
-class CompletedStoryListFragmentPresenter @Inject constructor(
- private val activity: AppCompatActivity,
- private val fragment: Fragment,
- private val viewModel: CompletedStoryListViewModel,
- private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory
-) {
+class CompletedStoryListFragmentPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val fragment: Fragment,
+ private val viewModel: CompletedStoryListViewModel,
+ private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory,
+ ) {
+ private lateinit var binding: CompletedStoryListFragmentBinding
- private lateinit var binding: CompletedStoryListFragmentBinding
+ /** Initializes and creates the views for [CompletedStoryListFragment]. */
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ internalProfileId: Int,
+ ): View? {
+ viewModel.setProfileId(internalProfileId)
+<<<<<<< HEAD
+ binding =
+ CompletedStoryListFragmentBinding
+ .inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
+ binding.completedStoryListToolbar.setNavigationOnClickListener {
+ (activity as CompletedStoryListActivity).finish()
+ }
+ binding.completedStoryList.apply {
+ val spanCount = activity.resources.getInteger(R.integer.completed_story_span_count)
+ layoutManager = GridLayoutManager(context, spanCount)
+ adapter = createRecyclerViewAdapter()
+ }
+ binding.let {
+ it.lifecycleOwner = fragment
+ it.viewModel = viewModel
+ }
+ return binding.root
+ }
+=======
/** Initializes and creates the views for [CompletedStoryListFragment]. */
fun handleCreateView(
inflater: LayoutInflater,
@@ -30,34 +63,13 @@ class CompletedStoryListFragmentPresenter @Inject constructor(
profileId: ProfileId
): View? {
viewModel.setProfileId(profileId)
+>>>>>>> 42210e8069394528330be84c5f4893bb2dafc2bf
- binding = CompletedStoryListFragmentBinding
- .inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
- binding.completedStoryListToolbar.setNavigationOnClickListener {
- (activity as CompletedStoryListActivity).finish()
- }
- binding.completedStoryList.apply {
- val spanCount = activity.resources.getInteger(R.integer.completed_story_span_count)
- layoutManager = GridLayoutManager(context, spanCount)
- adapter = createRecyclerViewAdapter()
- }
- binding.let {
- it.lifecycleOwner = fragment
- it.viewModel = viewModel
- }
- return binding.root
- }
-
- private fun createRecyclerViewAdapter(): BindableAdapter {
- return singleTypeBuilderFactory.create()
- .registerViewDataBinderWithSameModelType(
- inflateDataBinding = CompletedStoryItemBinding::inflate,
- setViewModel = CompletedStoryItemBinding::setViewModel
- )
- .build()
+ private fun createRecyclerViewAdapter(): BindableAdapter =
+ singleTypeBuilderFactory
+ .create()
+ .registerViewDataBinderWithSameModelType(
+ inflateDataBinding = CompletedStoryItemBinding::inflate,
+ setViewModel = CompletedStoryItemBinding::setViewModel,
+ ).build()
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListViewModel.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListViewModel.kt
index e2a02cc711d..557dc922c01 100644
--- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListViewModel.kt
@@ -18,6 +18,27 @@ import javax.inject.Inject
/** The ObservableViewModel for [CompletedStoryListFragment]. */
@FragmentScope
+<<<<<<< HEAD
+class CompletedStoryListViewModel
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val intentFactoryShim: IntentFactoryShim,
+ private val topicController: TopicController,
+ private val oppiaLogger: OppiaLogger,
+ private val translationController: TranslationController,
+ @StoryHtmlParserEntityType private val entityType: String,
+ ) : ObservableViewModel() {
+ /** [internalProfileId] needs to be set before any of the live data members can be accessed. */
+ private var internalProfileId: Int = -1
+
+ private val completedStoryListResultLiveData: LiveData> by lazy {
+ topicController
+ .getCompletedStoryList(
+ ProfileId.newBuilder().setInternalId(internalProfileId).build(),
+ ).toLiveData()
+ }
+=======
class CompletedStoryListViewModel @Inject constructor(
private val activity: AppCompatActivity,
private val intentFactoryShim: IntentFactoryShim,
@@ -32,37 +53,62 @@ class CompletedStoryListViewModel @Inject constructor(
private val completedStoryListResultLiveData: LiveData> by lazy {
topicController.getCompletedStoryList(profileId).toLiveData()
}
+>>>>>>> 42210e8069394528330be84c5f4893bb2dafc2bf
- private val completedStoryLiveData: LiveData by lazy {
- Transformations.map(completedStoryListResultLiveData, ::processCompletedStoryListResult)
- }
+ private val completedStoryLiveData: LiveData by lazy {
+ Transformations.map(completedStoryListResultLiveData, ::processCompletedStoryListResult)
+ }
- /** [LiveData] list displayed to user on [CompletedStoryListFragment]. */
- val completedStoryListLiveData: LiveData> by lazy {
- Transformations.map(completedStoryLiveData, ::processCompletedStoryList)
- }
+ /** [LiveData] list displayed to user on [CompletedStoryListFragment]. */
+ val completedStoryListLiveData: LiveData> by lazy {
+ Transformations.map(completedStoryLiveData, ::processCompletedStoryList)
+ }
+<<<<<<< HEAD
+ /** Sets internalProfileId to this ViewModel. */
+ fun setProfileId(internalProfileId: Int) {
+ this.internalProfileId = internalProfileId
+ }
+=======
/** Sets internalProfileId to this ViewModel. */
fun setProfileId(profileId: ProfileId) {
this.profileId = profileId
}
+>>>>>>> 42210e8069394528330be84c5f4893bb2dafc2bf
- private fun processCompletedStoryListResult(
- completedStoryListResult: AsyncResult
- ): CompletedStoryList {
- return when (completedStoryListResult) {
- is AsyncResult.Failure -> {
- oppiaLogger.e(
- "CompletedStoryListFragment",
- "Failed to retrieve CompletedStory list: ",
- completedStoryListResult.error
- )
- CompletedStoryList.getDefaultInstance()
+ private fun processCompletedStoryListResult(completedStoryListResult: AsyncResult): CompletedStoryList =
+ when (completedStoryListResult) {
+ is AsyncResult.Failure -> {
+ oppiaLogger.e(
+ "CompletedStoryListFragment",
+ "Failed to retrieve CompletedStory list: ",
+ completedStoryListResult.error,
+ )
+ CompletedStoryList.getDefaultInstance()
+ }
+ is AsyncResult.Pending -> CompletedStoryList.getDefaultInstance()
+ is AsyncResult.Success -> completedStoryListResult.value
}
- is AsyncResult.Pending -> CompletedStoryList.getDefaultInstance()
- is AsyncResult.Success -> completedStoryListResult.value
+
+ private fun processCompletedStoryList(completedStoryList: CompletedStoryList): List {
+ val itemViewModelList: MutableList = mutableListOf()
+ itemViewModelList.addAll(
+ completedStoryList.completedStoryList.map { completedStory ->
+ CompletedStoryItemViewModel(
+ activity,
+ internalProfileId,
+ completedStory,
+ entityType,
+ intentFactoryShim,
+ translationController,
+ )
+ },
+ )
+ return itemViewModelList
}
}
+<<<<<<< HEAD
+=======
private fun processCompletedStoryList(
completedStoryList: CompletedStoryList
@@ -83,3 +129,4 @@ class CompletedStoryListViewModel @Inject constructor(
return itemViewModelList
}
}
+>>>>>>> 42210e8069394528330be84c5f4893bb2dafc2bf
diff --git a/app/src/main/java/org/oppia/android/app/customview/ChapterNotStartedContainerConstraintLayout.kt b/app/src/main/java/org/oppia/android/app/customview/ChapterNotStartedContainerConstraintLayout.kt
index b20920c763a..9e3ee2134c7 100644
--- a/app/src/main/java/org/oppia/android/app/customview/ChapterNotStartedContainerConstraintLayout.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/ChapterNotStartedContainerConstraintLayout.kt
@@ -15,53 +15,54 @@ import org.oppia.android.app.view.ViewComponentImpl
import javax.inject.Inject
/** Custom view that represents an incomplete chapter. */
-class ChapterNotStartedContainerConstraintLayout @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : ConstraintLayout(context, attrs, defStyleAttr) {
+class ChapterNotStartedContainerConstraintLayout
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ ) : ConstraintLayout(context, attrs, defStyleAttr) {
+ private var index: Int = -1
+ private var isSpotlit = false
- private var index: Int = -1
- private var isSpotlit = false
+ @Inject
+ lateinit var fragment: Fragment
- @Inject
- lateinit var fragment: Fragment
+ @Inject
+ lateinit var resourceHandler: AppLanguageResourceHandler
- @Inject
- lateinit var resourceHandler: AppLanguageResourceHandler
-
- /** Sets the index of the story of which this custom view is a part of. */
- fun setStoryIndex(index: Int) {
- // Only spotlight the first chapter of the "first" story. We know for sure that for a new user,
- // the first chapter shall be a type of not started chapter view. The index tells which story
- // are we on.
- this.index = index
- }
+ /** Sets the index of the story of which this custom view is a part of. */
+ fun setStoryIndex(index: Int) {
+ // Only spotlight the first chapter of the "first" story. We know for sure that for a new user,
+ // the first chapter shall be a type of not started chapter view. The index tells which story
+ // are we on.
+ this.index = index
+ }
- private fun getSpotlightManager(): SpotlightManager? {
- return fragment.requireActivity().supportFragmentManager.findFragmentByTag(
- SpotlightManager.SPOTLIGHT_FRAGMENT_TAG
- ) as? SpotlightManager
- }
+ private fun getSpotlightManager(): SpotlightManager? =
+ fragment.requireActivity().supportFragmentManager.findFragmentByTag(
+ SpotlightManager.SPOTLIGHT_FRAGMENT_TAG,
+ ) as? SpotlightManager
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
- val viewComponentFactory =
- FragmentManager.findFragment(this) as ViewComponentFactory
- val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
- viewComponent.inject(this)
+ val viewComponentFactory =
+ FragmentManager.findFragment(this) as ViewComponentFactory
+ val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
+ viewComponent.inject(this)
- if (!isSpotlit) {
- isSpotlit = true
- val spotlightTarget = SpotlightTarget(
- this,
- resourceHandler.getStringInLocale(R.string.first_chapter_spotlight_hint),
- feature = Spotlight.FeatureCase.FIRST_CHAPTER
- )
- if (index == 0) {
- checkNotNull(getSpotlightManager()).requestSpotlightViewWithDelayedLayout(spotlightTarget)
+ if (!isSpotlit) {
+ isSpotlit = true
+ val spotlightTarget =
+ SpotlightTarget(
+ this,
+ resourceHandler.getStringInLocale(R.string.first_chapter_spotlight_hint),
+ feature = Spotlight.FeatureCase.FIRST_CHAPTER,
+ )
+ if (index == 0) {
+ checkNotNull(getSpotlightManager()).requestSpotlightViewWithDelayedLayout(spotlightTarget)
+ }
}
}
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/ContinueButtonView.kt b/app/src/main/java/org/oppia/android/app/customview/ContinueButtonView.kt
index dd599820187..9f164a22a76 100644
--- a/app/src/main/java/org/oppia/android/app/customview/ContinueButtonView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/ContinueButtonView.kt
@@ -17,113 +17,120 @@ import javax.inject.Inject
private const val INTERVAL_BETWEEN_CONTINUE_BUTTON_ANIM_MS = 8000L
/** A custom [AppCompatButton] used to show continue button animations. */
-class ContinueButtonView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = R.style.StateButtonActive
-) : androidx.appcompat.widget.AppCompatButton(context, attrs, defStyleAttr) {
-
- @Inject
- lateinit var fragment: Fragment
- @Inject
- lateinit var oppiaClock: OppiaClock
- @Inject
- lateinit var lifecycleSafeTimerFactory: LifecycleSafeTimerFactory
- @Inject
- lateinit var oppiaLogger: OppiaLogger
-
- private var shouldAnimateContinueButtonLateinit: Boolean? = null
- private val shouldAnimateContinueButton: Boolean
- get() = checkNotNull(shouldAnimateContinueButtonLateinit) {
- "Expected shouldAnimateContinueButtonLateinit to be initialized by this point."
- }
+class ContinueButtonView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.style.StateButtonActive,
+ ) : androidx.appcompat.widget.AppCompatButton(context, attrs, defStyleAttr) {
+ @Inject
+ lateinit var fragment: Fragment
+
+ @Inject
+ lateinit var oppiaClock: OppiaClock
+
+ @Inject
+ lateinit var lifecycleSafeTimerFactory: LifecycleSafeTimerFactory
+
+ @Inject
+ lateinit var oppiaLogger: OppiaLogger
+
+ private var shouldAnimateContinueButtonLateinit: Boolean? = null
+ private val shouldAnimateContinueButton: Boolean
+ get() =
+ checkNotNull(shouldAnimateContinueButtonLateinit) {
+ "Expected shouldAnimateContinueButtonLateinit to be initialized by this point."
+ }
- private var continueButtonAnimationTimestampMsLateinit: Long? = null
- private val continueButtonAnimationTimestampMs: Long
- get() = checkNotNull(continueButtonAnimationTimestampMsLateinit) {
- "Expected continueButtonAnimationTimestampMsLateinit to be initialized by this point."
- }
+ private var continueButtonAnimationTimestampMsLateinit: Long? = null
+ private val continueButtonAnimationTimestampMs: Long
+ get() =
+ checkNotNull(continueButtonAnimationTimestampMsLateinit) {
+ "Expected continueButtonAnimationTimestampMsLateinit to be initialized by this point."
+ }
- private val hasAnimationTimerFinished: Boolean
- get() = continueButtonAnimationTimestampMs < oppiaClock.getCurrentTimeMs()
+ private val hasAnimationTimerFinished: Boolean
+ get() = continueButtonAnimationTimestampMs < oppiaClock.getCurrentTimeMs()
- private var animationStartTimer: LiveData? = null
- private var currentAnimationReuseCount = 0
+ private var animationStartTimer: LiveData? = null
+ private var currentAnimationReuseCount = 0
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- val viewComponentFactory =
- FragmentManager.findFragment(this) as ViewComponentFactory
- val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
- viewComponent.inject(this)
- maybeInitializeAnimation()
- }
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ val viewComponentFactory =
+ FragmentManager.findFragment(this) as ViewComponentFactory
+ val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
+ viewComponent.inject(this)
+ maybeInitializeAnimation()
+ }
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
- // Make sure state can't leak across rebinding boundaries (since this view may be reused).
- cancelOngoingTimer()
- }
+ // Make sure state can't leak across rebinding boundaries (since this view may be reused).
+ cancelOngoingTimer()
+ }
- /** Sets whether the view should animate to catch a user's attention. */
- fun setShouldAnimateContinueButton(shouldAnimateContinueButton: Boolean) {
- shouldAnimateContinueButtonLateinit = shouldAnimateContinueButton
- maybeInitializeAnimation()
- }
+ /** Sets whether the view should animate to catch a user's attention. */
+ fun setShouldAnimateContinueButton(shouldAnimateContinueButton: Boolean) {
+ shouldAnimateContinueButtonLateinit = shouldAnimateContinueButton
+ maybeInitializeAnimation()
+ }
- /**
- * Sets when, in clock time, the animation controlled by [setShouldAnimateContinueButton] should
- * play.
- */
- fun setContinueButtonAnimationTimestampMs(continueButtonAnimationTimestampMs: Long) {
- continueButtonAnimationTimestampMsLateinit = continueButtonAnimationTimestampMs
- maybeInitializeAnimation()
- }
+ /**
+ * Sets when, in clock time, the animation controlled by [setShouldAnimateContinueButton] should
+ * play.
+ */
+ fun setContinueButtonAnimationTimestampMs(continueButtonAnimationTimestampMs: Long) {
+ continueButtonAnimationTimestampMsLateinit = continueButtonAnimationTimestampMs
+ maybeInitializeAnimation()
+ }
- private fun maybeInitializeAnimation() {
- if (::oppiaClock.isInitialized &&
- shouldAnimateContinueButtonLateinit != null &&
- continueButtonAnimationTimestampMsLateinit != null
- ) {
- when {
- !shouldAnimateContinueButton -> clearAnimation()
- hasAnimationTimerFinished -> startAnimating()
- else -> {
- val timeLeftToAnimate = continueButtonAnimationTimestampMs - oppiaClock.getCurrentTimeMs()
- startAnimatingWithDelay(delayMs = timeLeftToAnimate)
+ private fun maybeInitializeAnimation() {
+ if (::oppiaClock.isInitialized &&
+ shouldAnimateContinueButtonLateinit != null &&
+ continueButtonAnimationTimestampMsLateinit != null
+ ) {
+ when {
+ !shouldAnimateContinueButton -> clearAnimation()
+ hasAnimationTimerFinished -> startAnimating()
+ else -> {
+ val timeLeftToAnimate = continueButtonAnimationTimestampMs - oppiaClock.getCurrentTimeMs()
+ startAnimatingWithDelay(delayMs = timeLeftToAnimate)
+ }
}
}
}
- }
- private fun startAnimatingWithDelay(delayMs: Long) {
- cancelOngoingTimer()
- val sequenceNumber = currentAnimationReuseCount
- lifecycleSafeTimerFactory.createTimer(delayMs).observe(fragment) {
- // Only play the animation if it's still valid to do so (since the view may have been recycled
- // for a new context that may not want the animation to play).
- if (sequenceNumber == currentAnimationReuseCount) {
- startAnimating()
+ private fun startAnimatingWithDelay(delayMs: Long) {
+ cancelOngoingTimer()
+ val sequenceNumber = currentAnimationReuseCount
+ lifecycleSafeTimerFactory.createTimer(delayMs).observe(fragment) {
+ // Only play the animation if it's still valid to do so (since the view may have been recycled
+ // for a new context that may not want the animation to play).
+ if (sequenceNumber == currentAnimationReuseCount) {
+ startAnimating()
+ }
}
}
- }
- private fun cancelOngoingTimer() {
- currentAnimationReuseCount++
- animationStartTimer?.let {
- it.removeObservers(fragment)
- animationStartTimer = null
+ private fun cancelOngoingTimer() {
+ currentAnimationReuseCount++
+ animationStartTimer?.let {
+ it.removeObservers(fragment)
+ animationStartTimer = null
+ }
}
- }
- private fun startAnimating() {
- val animation = AnimationUtils.loadAnimation(context, R.anim.wobble_button_animation)
- startAnimation(animation)
- // Repeat the animation after a fixed interval.
- lifecycleSafeTimerFactory.createTimer(INTERVAL_BETWEEN_CONTINUE_BUTTON_ANIM_MS)
- .observe(fragment) {
- startAnimating()
- }
+ private fun startAnimating() {
+ val animation = AnimationUtils.loadAnimation(context, R.anim.wobble_button_animation)
+ startAnimation(animation)
+ // Repeat the animation after a fixed interval.
+ lifecycleSafeTimerFactory
+ .createTimer(INTERVAL_BETWEEN_CONTINUE_BUTTON_ANIM_MS)
+ .observe(fragment) {
+ startAnimating()
+ }
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt b/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt
index 254b680437c..f6c996e7b15 100644
--- a/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt
@@ -21,137 +21,125 @@ import org.oppia.android.util.parser.image.ThumbnailDownloadUrlTemplate
import javax.inject.Inject
/** A custom [AppCompatImageView] used to show lesson thumbnails. */
-class LessonThumbnailImageView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : AppCompatImageView(context, attrs, defStyleAttr) {
- // TODO(#3098): Add dedicated tests for this class.
-
- private val imageView = this
- private var isBlurred: Boolean = false
- private lateinit var lessonThumbnail: LessonThumbnail
- private lateinit var entityId: String
- private lateinit var entityType: String
-
- @Inject
- lateinit var imageLoader: ImageLoader
-
- @Inject
- @field:DefaultResourceBucketName
- lateinit var resourceBucketName: String
-
- // TODO(#1571): Investigate this issue to fix the initialization error.
- @Inject
- @field:ThumbnailDownloadUrlTemplate
- lateinit var thumbnailDownloadUrlTemplate: String
-
- @Inject
- @field:DefaultGcsPrefix
- lateinit var gcsPrefix: String
-
- @Inject
- lateinit var oppiaLogger: OppiaLogger
-
- @Inject
- lateinit var machineLocale: OppiaLocale.MachineLocale
-
- /** Sets the entityId of the current [LessonThumbnailImageView] being displayed. */
- fun setEntityId(entityId: String) {
- this.entityId = entityId
- checkIfLoadingIsPossible()
- }
-
- /** Sets the entityType of the current [LessonThumbnailImageView] being displayed. */
- fun setEntityType(entityType: String) {
- this.entityType = entityType
- checkIfLoadingIsPossible()
- }
-
- /** Sets the [LessonThumbnail] of the current [LessonThumbnailImageView] being displayed. */
- fun setLessonThumbnail(lessonThumbnail: LessonThumbnail?) {
- if (lessonThumbnail != null) {
- this.lessonThumbnail = lessonThumbnail
+class LessonThumbnailImageView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ ) : AppCompatImageView(context, attrs, defStyleAttr) {
+ // TODO(#3098): Add dedicated tests for this class.
+
+ private val imageView = this
+ private var isBlurred: Boolean = false
+ private lateinit var lessonThumbnail: LessonThumbnail
+ private lateinit var entityId: String
+ private lateinit var entityType: String
+
+ @Inject
+ lateinit var imageLoader: ImageLoader
+
+ @Inject
+ @field:DefaultResourceBucketName
+ lateinit var resourceBucketName: String
+
+ // TODO(#1571): Investigate this issue to fix the initialization error.
+ @Inject
+ @field:ThumbnailDownloadUrlTemplate
+ lateinit var thumbnailDownloadUrlTemplate: String
+
+ @Inject
+ @field:DefaultGcsPrefix
+ lateinit var gcsPrefix: String
+
+ @Inject
+ lateinit var oppiaLogger: OppiaLogger
+
+ @Inject
+ lateinit var machineLocale: OppiaLocale.MachineLocale
+
+ /** Sets the entityId of the current [LessonThumbnailImageView] being displayed. */
+ fun setEntityId(entityId: String) {
+ this.entityId = entityId
checkIfLoadingIsPossible()
}
- }
- /** Sets whether the current [LessonThumbnailImageView] being displayed should be blurred. */
- fun setIsBlurred(isBlurred: Boolean) {
- this.isBlurred = isBlurred
- }
+ /** Sets the entityType of the current [LessonThumbnailImageView] being displayed. */
+ fun setEntityType(entityType: String) {
+ this.entityType = entityType
+ checkIfLoadingIsPossible()
+ }
- private fun checkIfLoadingIsPossible() {
- if (::entityId.isInitialized &&
- ::entityType.isInitialized &&
- ::lessonThumbnail.isInitialized &&
- ::thumbnailDownloadUrlTemplate.isInitialized &&
- ::resourceBucketName.isInitialized &&
- ::gcsPrefix.isInitialized &&
- ::imageLoader.isInitialized &&
- ::oppiaLogger.isInitialized
- ) {
- loadLessonThumbnail()
+ /** Sets the [LessonThumbnail] of the current [LessonThumbnailImageView] being displayed. */
+ fun setLessonThumbnail(lessonThumbnail: LessonThumbnail?) {
+ if (lessonThumbnail != null) {
+ this.lessonThumbnail = lessonThumbnail
+ checkIfLoadingIsPossible()
+ }
}
- }
- private fun loadLessonThumbnail() {
- val transformations = if (isBlurred) {
- listOf(ImageTransformation.BLUR)
- } else {
- listOf()
+ /** Sets whether the current [LessonThumbnailImageView] being displayed should be blurred. */
+ fun setIsBlurred(isBlurred: Boolean) {
+ this.isBlurred = isBlurred
}
- if (lessonThumbnail.thumbnailFilename.isNotEmpty()) {
- loadImage(lessonThumbnail.thumbnailFilename, transformations)
- } else {
- imageLoader.loadDrawable(
- getLessonDrawableResource(lessonThumbnail),
- ImageViewTarget(this),
- transformations
- )
+
+ private fun checkIfLoadingIsPossible() {
+ if (::entityId.isInitialized &&
+ ::entityType.isInitialized &&
+ ::lessonThumbnail.isInitialized &&
+ ::thumbnailDownloadUrlTemplate.isInitialized &&
+ ::resourceBucketName.isInitialized &&
+ ::gcsPrefix.isInitialized &&
+ ::imageLoader.isInitialized &&
+ ::oppiaLogger.isInitialized
+ ) {
+ loadLessonThumbnail()
+ }
}
- imageView.setBackgroundColor(
- (0xff000000L or lessonThumbnail.backgroundColorRgb.toLong()).toInt()
- )
- imageView.scaleType = ScaleType.FIT_CENTER
- }
- /** Loads an image using Glide from [filename]. */
- private fun loadImage(filename: String, transformations: List) {
- val imageName = machineLocale.run {
- thumbnailDownloadUrlTemplate.formatForMachines(
- entityType,
- entityId,
- filename
+ private fun loadLessonThumbnail() {
+ val transformations =
+ if (isBlurred) {
+ listOf(ImageTransformation.BLUR)
+ } else {
+ listOf()
+ }
+ if (lessonThumbnail.thumbnailFilename.isNotEmpty()) {
+ loadImage(lessonThumbnail.thumbnailFilename, transformations)
+ } else {
+ imageLoader.loadDrawable(
+ getLessonDrawableResource(lessonThumbnail),
+ ImageViewTarget(this),
+ transformations,
+ )
+ }
+ imageView.setBackgroundColor(
+ (0xff000000L or lessonThumbnail.backgroundColorRgb.toLong()).toInt(),
)
+ imageView.scaleType = ScaleType.FIT_CENTER
}
- val imageUrl = "$gcsPrefix/$resourceBucketName/$imageName"
- if (machineLocale.run { imageUrl.endsWithIgnoreCase("svg") }) {
- imageLoader.loadBlockSvg(imageUrl, ImageViewTarget(this), transformations)
- } else {
- imageLoader.loadBitmap(imageUrl, ImageViewTarget(this), transformations)
- }
- }
- override fun onAttachedToWindow() {
- try {
- super.onAttachedToWindow()
-
- val viewComponentFactory =
- FragmentManager.findFragment(this) as ViewComponentFactory
- val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
- viewComponent.inject(this)
-
- checkIfLoadingIsPossible()
- } catch (e: IllegalStateException) {
- if (::oppiaLogger.isInitialized)
- oppiaLogger.e(
- "LessonThumbnailImageView",
- "Throws exception on attach to window",
- e
- )
+ /** Loads an image using Glide from [filename]. */
+ private fun loadImage(
+ filename: String,
+ transformations: List,
+ ) {
+ val imageName =
+ machineLocale.run {
+ thumbnailDownloadUrlTemplate.formatForMachines(
+ entityType,
+ entityId,
+ filename,
+ )
+ }
+ val imageUrl = "$gcsPrefix/$resourceBucketName/$imageName"
+ if (machineLocale.run { imageUrl.endsWithIgnoreCase("svg") }) {
+ imageLoader.loadBlockSvg(imageUrl, ImageViewTarget(this), transformations)
+ } else {
+ imageLoader.loadBitmap(imageUrl, ImageViewTarget(this), transformations)
+ }
}
- }
+
private fun getLessonDrawableResource(lessonThumbnail: LessonThumbnail): Int {
return when (lessonThumbnail.thumbnailGraphic) {
@@ -201,6 +189,50 @@ class LessonThumbnailImageView @JvmOverloads constructor(
R.drawable.ic_english
else ->
R.drawable.topic_fractions_01
+
}
+
+ private fun getLessonDrawableResource(lessonThumbnail: LessonThumbnail): Int =
+ when (lessonThumbnail.thumbnailGraphic) {
+ LessonThumbnailGraphic.BAKER ->
+ R.drawable.lesson_thumbnail_graphic_baker
+ LessonThumbnailGraphic.CHILD_WITH_BOOK ->
+ R.drawable.lesson_thumbnail_graphic_child_with_book
+ LessonThumbnailGraphic.CHILD_WITH_CUPCAKES ->
+ R.drawable.lesson_thumbnail_graphic_child_with_cupcakes
+ LessonThumbnailGraphic.CHILD_WITH_FRACTIONS_HOMEWORK ->
+ R.drawable.lesson_thumbnail_graphic_child_with_fractions_homework
+ LessonThumbnailGraphic.DUCK_AND_CHICKEN ->
+ R.drawable.lesson_thumbnail_graphic_duck_and_chicken
+ LessonThumbnailGraphic.PERSON_WITH_PIE_CHART ->
+ R.drawable.lesson_thumbnail_graphic_person_with_pie_chart
+ LessonThumbnailGraphic.IDENTIFYING_THE_PARTS_OF_A_FRACTION ->
+ R.drawable.topic_fractions_01
+ LessonThumbnailGraphic.WRITING_FRACTIONS ->
+ R.drawable.topic_fractions_02
+ LessonThumbnailGraphic.EQUIVALENT_FRACTIONS ->
+ R.drawable.topic_fractions_03
+ LessonThumbnailGraphic.MIXED_NUMBERS_AND_IMPROPER_FRACTIONS ->
+ R.drawable.topic_fractions_04
+ LessonThumbnailGraphic.COMPARING_FRACTIONS ->
+ R.drawable.topic_fractions_05
+ LessonThumbnailGraphic.ADDING_AND_SUBTRACTING_FRACTIONS ->
+ R.drawable.topic_fractions_06
+ LessonThumbnailGraphic.MULTIPLYING_FRACTIONS ->
+ R.drawable.topic_fractions_07
+ LessonThumbnailGraphic.DIVIDING_FRACTIONS ->
+ R.drawable.topic_fractions_08
+ LessonThumbnailGraphic.DERIVE_A_RATIO ->
+ R.drawable.topic_ratios_01
+ LessonThumbnailGraphic.WHAT_IS_A_FRACTION ->
+ R.drawable.topic_fractions_01
+ LessonThumbnailGraphic.FRACTION_OF_A_GROUP ->
+ R.drawable.topic_fractions_02
+ LessonThumbnailGraphic.ADDING_FRACTIONS ->
+ R.drawable.topic_fractions_03
+ LessonThumbnailGraphic.MIXED_NUMBERS ->
+ R.drawable.topic_fractions_04
+ else ->
+ R.drawable.topic_fractions_01
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/OppiaCurveBackgroundView.kt b/app/src/main/java/org/oppia/android/app/customview/OppiaCurveBackgroundView.kt
index 30809c6f902..90188419c82 100644
--- a/app/src/main/java/org/oppia/android/app/customview/OppiaCurveBackgroundView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/OppiaCurveBackgroundView.kt
@@ -23,109 +23,117 @@ import javax.inject.Inject
* Reference: // https://proandroiddev.com/how-i-drew-custom-shapes-in-bottom-bar-c4539d86afd7 and
* // https://ciechanow.ski/drawing-bezier-curves/
*/
-class OppiaCurveBackgroundView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : View(context, attrs, defStyleAttr) {
- /**
- * Used to retrieve the layout direction that should be used to mirror the direction of the
- * curve based on locale.
- */
- @Inject lateinit var resourceHandler: AppLanguageResourceHandler
-
- private val isRtl by lazy {
- resourceHandler.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL
- }
+class OppiaCurveBackgroundView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ ) : View(context, attrs, defStyleAttr) {
+ /**
+ * Used to retrieve the layout direction that should be used to mirror the direction of the
+ * curve based on locale.
+ */
+ @Inject lateinit var resourceHandler: AppLanguageResourceHandler
+
+ private val isRtl by lazy {
+ resourceHandler.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL
+ }
- private val orientation = Resources.getSystem().configuration.orientation
+ private val orientation = Resources.getSystem().configuration.orientation
- private var customBackgroundColor = Color.WHITE // Default color.
+ private var customBackgroundColor = Color.WHITE // Default color.
- private lateinit var paint: Paint
- private lateinit var path: Path
- private var strokeWidth = 2f
+ private lateinit var paint: Paint
+ private lateinit var path: Path
+ private var strokeWidth = 2f
- /** Sets the desired background color to the view and initializes the view. */
- fun setCustomBackgroundColor(colorRes: Int) {
- this.customBackgroundColor = colorRes
- setupCurvePaint()
- }
+ /** Sets the desired background color to the view and initializes the view. */
+ fun setCustomBackgroundColor(colorRes: Int) {
+ this.customBackgroundColor = colorRes
+ setupCurvePaint()
+ }
- override fun onDraw(canvas: Canvas) {
- if (isRtl)
- rotationY = 180f
- super.onDraw(canvas)
+ override fun onDraw(canvas: Canvas) {
+ if (isRtl) {
+ rotationY = 180f
+ }
+ super.onDraw(canvas)
- canvas.drawPath(path, paint)
- }
-
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
- super.onSizeChanged(w, h, oldw, oldh)
- val width = this.width.toFloat()
- val height = this.height.toFloat()
-
- val controlPoint1X: Float
- val controlPoint1Y: Float
-
- val controlPoint2X: Float
- val controlPoint2Y: Float
-
- val controlPoint3X: Float
- val controlPoint3Y: Float
-
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- controlPoint1X = width * 0.55f
- controlPoint1Y = 0f
- controlPoint2X = width * 0.52f
- controlPoint2Y = height * 0.2f
- controlPoint3X = width * 1f
- controlPoint3Y = height * 0.1f
- } else {
- controlPoint1X = width * 0.40f
- controlPoint1Y = 0f
- controlPoint2X = width * 0.60f
- controlPoint2Y = height * 0.40f
- controlPoint3X = width * 1f
- controlPoint3Y = height * 0.2f
+ canvas.drawPath(path, paint)
}
- path.reset()
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- path.moveTo(0f, height * 0.10f)
- } else {
- path.moveTo(0f, height * 0.30f)
+ override fun onSizeChanged(
+ w: Int,
+ h: Int,
+ oldw: Int,
+ oldh: Int,
+ ) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ val width = this.width.toFloat()
+ val height = this.height.toFloat()
+
+ val controlPoint1X: Float
+ val controlPoint1Y: Float
+
+ val controlPoint2X: Float
+ val controlPoint2Y: Float
+
+ val controlPoint3X: Float
+ val controlPoint3Y: Float
+
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ controlPoint1X = width * 0.55f
+ controlPoint1Y = 0f
+ controlPoint2X = width * 0.52f
+ controlPoint2Y = height * 0.2f
+ controlPoint3X = width * 1f
+ controlPoint3Y = height * 0.1f
+ } else {
+ controlPoint1X = width * 0.40f
+ controlPoint1Y = 0f
+ controlPoint2X = width * 0.60f
+ controlPoint2Y = height * 0.40f
+ controlPoint3X = width * 1f
+ controlPoint3Y = height * 0.2f
+ }
+
+ path.reset()
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ path.moveTo(0f, height * 0.10f)
+ } else {
+ path.moveTo(0f, height * 0.30f)
+ }
+ path.cubicTo(
+ controlPoint1X,
+ controlPoint1Y,
+ controlPoint2X,
+ controlPoint2Y,
+ controlPoint3X,
+ controlPoint3Y,
+ )
+
+ path.lineTo(width, height)
+ path.lineTo(0f, height)
+ path.lineTo(0f, 0f)
}
- path.cubicTo(
- controlPoint1X,
- controlPoint1Y,
- controlPoint2X,
- controlPoint2Y,
- controlPoint3X,
- controlPoint3Y
- )
-
- path.lineTo(width, height)
- path.lineTo(0f, height)
- path.lineTo(0f, 0f)
- }
- private fun setupCurvePaint() {
- path = Path()
- paint = Paint(Paint.ANTI_ALIAS_FLAG)
- paint.apply {
- style = Paint.Style.FILL
- strokeWidth = this@OppiaCurveBackgroundView.strokeWidth
- color = customBackgroundColor
+ private fun setupCurvePaint() {
+ path = Path()
+ paint = Paint(Paint.ANTI_ALIAS_FLAG)
+ paint.apply {
+ style = Paint.Style.FILL
+ strokeWidth = this@OppiaCurveBackgroundView.strokeWidth
+ color = customBackgroundColor
+ }
+ setBackgroundColor(Color.TRANSPARENT)
}
- setBackgroundColor(Color.TRANSPARENT)
- }
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
- val viewComponentFactory = FragmentManager.findFragment(this) as ViewComponentFactory
- val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
- viewComponent.inject(this)
+ val viewComponentFactory = FragmentManager.findFragment(this) as ViewComponentFactory
+ val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
+ viewComponent.inject(this)
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/PromotedStoryCardView.kt b/app/src/main/java/org/oppia/android/app/customview/PromotedStoryCardView.kt
index ca104409df7..6ceb194fa26 100644
--- a/app/src/main/java/org/oppia/android/app/customview/PromotedStoryCardView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/PromotedStoryCardView.kt
@@ -15,46 +15,47 @@ import org.oppia.android.app.view.ViewComponentImpl
import javax.inject.Inject
/** [MaterialCardView] that represents stories promoted to the learner. */
-class PromotedStoryCardView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : MaterialCardView(context, attrs, defStyleAttr) {
-
- @Inject
- lateinit var fragment: Fragment
-
- @Inject
- lateinit var resourceHandler: AppLanguageResourceHandler
-
- private var isSpotlit = false
-
- /** Sets the index at which this custom view is located inside the recycler view. */
- fun setPromotedStoryIndex(index: Int) {
- // This view can get attached multiple times and we must make sure that the spotlight is
- // requested only once. Only spotlight the item at the first index of the recycler view.
- if (!isSpotlit && index == 0) {
- isSpotlit = true
- val spotlightTarget = SpotlightTarget(
- this,
- resourceHandler.getStringInLocale(R.string.promoted_story_spotlight_hint),
- feature = Spotlight.FeatureCase.PROMOTED_STORIES
- )
- checkNotNull(getSpotlightManager()).requestSpotlightViewWithDelayedLayout(spotlightTarget)
+class PromotedStoryCardView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ ) : MaterialCardView(context, attrs, defStyleAttr) {
+ @Inject
+ lateinit var fragment: Fragment
+
+ @Inject
+ lateinit var resourceHandler: AppLanguageResourceHandler
+
+ private var isSpotlit = false
+
+ /** Sets the index at which this custom view is located inside the recycler view. */
+ fun setPromotedStoryIndex(index: Int) {
+ // This view can get attached multiple times and we must make sure that the spotlight is
+ // requested only once. Only spotlight the item at the first index of the recycler view.
+ if (!isSpotlit && index == 0) {
+ isSpotlit = true
+ val spotlightTarget =
+ SpotlightTarget(
+ this,
+ resourceHandler.getStringInLocale(R.string.promoted_story_spotlight_hint),
+ feature = Spotlight.FeatureCase.PROMOTED_STORIES,
+ )
+ checkNotNull(getSpotlightManager()).requestSpotlightViewWithDelayedLayout(spotlightTarget)
+ }
}
- }
-
- private fun getSpotlightManager(): SpotlightManager? {
- return fragment.requireActivity().supportFragmentManager.findFragmentByTag(
- SpotlightManager.SPOTLIGHT_FRAGMENT_TAG
- ) as? SpotlightManager
- }
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- val viewComponentFactory =
- FragmentManager.findFragment(this) as ViewComponentFactory
- val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
- viewComponent.inject(this)
+ private fun getSpotlightManager(): SpotlightManager? =
+ fragment.requireActivity().supportFragmentManager.findFragmentByTag(
+ SpotlightManager.SPOTLIGHT_FRAGMENT_TAG,
+ ) as? SpotlightManager
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ val viewComponentFactory =
+ FragmentManager.findFragment(this) as ViewComponentFactory
+ val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
+ viewComponent.inject(this)
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt b/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt
index c81e19e0873..bb5280f6c2f 100644
--- a/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt
@@ -25,182 +25,187 @@ private const val STROKE_DASH_GAP_IN_DEGREE = 12
*
* Reference: // https://stackoverflow.com/a/39210676
*/
-class SegmentedCircularProgressView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : View(context, attrs, defStyleAttr) {
- @Inject
- lateinit var resourceHandler: AppLanguageResourceHandler
-
- private var sweepAngle = 0f
- private var strokeWidth = 0f
- private val isRtl by lazy {
- resourceHandler.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL
- }
+class SegmentedCircularProgressView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ ) : View(context, attrs, defStyleAttr) {
+ @Inject
+ lateinit var resourceHandler: AppLanguageResourceHandler
+
+ private var sweepAngle = 0f
+ private var strokeWidth = 0f
+ private val isRtl by lazy {
+ resourceHandler.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL
+ }
- private lateinit var baseRect: RectF
- private lateinit var chapterFinishedArcPaint: Paint
- private lateinit var chapterInProgressArcPaint: Paint
- private lateinit var chapterNotStartedArcPaint: Paint
-
- private var chaptersNotStarted: Int = 0
- private var chaptersFinished: Int = 0
- private var chaptersInProgress: Int = 0
- private var totalChapters: Int = 0
-
- /** Sets StoryChapterDetails to this view for displaying and finally initialises the view. */
- fun setStoryChapterDetails(
- totalChaptersCount: Int,
- chaptersFinishedCount: Int,
- chaptersInProgressCount: Int
- ) {
- if (this.totalChapters != totalChaptersCount ||
- this.chaptersFinished != chaptersFinishedCount ||
- this.chaptersInProgress != chaptersInProgressCount
+ private lateinit var baseRect: RectF
+ private lateinit var chapterFinishedArcPaint: Paint
+ private lateinit var chapterInProgressArcPaint: Paint
+ private lateinit var chapterNotStartedArcPaint: Paint
+
+ private var chaptersNotStarted: Int = 0
+ private var chaptersFinished: Int = 0
+ private var chaptersInProgress: Int = 0
+ private var totalChapters: Int = 0
+
+ /** Sets StoryChapterDetails to this view for displaying and finally initialises the view. */
+ fun setStoryChapterDetails(
+ totalChaptersCount: Int,
+ chaptersFinishedCount: Int,
+ chaptersInProgressCount: Int,
) {
- this.totalChapters = totalChaptersCount
- this.chaptersFinished = chaptersFinishedCount
- this.chaptersInProgress = chaptersInProgressCount
- this.chaptersNotStarted = totalChaptersCount - chaptersFinishedCount - chaptersInProgressCount
- initialise()
+ if (this.totalChapters != totalChaptersCount ||
+ this.chaptersFinished != chaptersFinishedCount ||
+ this.chaptersInProgress != chaptersInProgressCount
+ ) {
+ this.totalChapters = totalChaptersCount
+ this.chaptersFinished = chaptersFinishedCount
+ this.chaptersInProgress = chaptersInProgressCount
+ this.chaptersNotStarted = totalChaptersCount - chaptersFinishedCount - chaptersInProgressCount
+ initialise()
+ }
}
- }
- private fun initialise() {
- chaptersNotStarted = totalChapters - chaptersFinished - chaptersInProgress
- strokeWidth = dpToPx(4)
- calculateSweepAngle()
-
- chapterFinishedArcPaint = Paint(Paint.ANTI_ALIAS_FLAG)
- setupArcPaint(
- chapterFinishedArcPaint,
- R.color.component_color_lessons_tab_activity_chapter_completed_progress_color
- )
-
- chapterInProgressArcPaint = Paint(Paint.ANTI_ALIAS_FLAG)
- setupArcPaint(
- chapterInProgressArcPaint,
- R.color.component_color_lessons_tab_activity_chapter_in_progress_progress_color
- )
-
- chapterNotStartedArcPaint = Paint(Paint.ANTI_ALIAS_FLAG)
- if (chaptersFinished != 0) {
+ private fun initialise() {
+ chaptersNotStarted = totalChapters - chaptersFinished - chaptersInProgress
+ strokeWidth = dpToPx(4)
+ calculateSweepAngle()
+
+ chapterFinishedArcPaint = Paint(Paint.ANTI_ALIAS_FLAG)
setupArcPaint(
- chapterNotStartedArcPaint,
- R.color.component_color_lessons_tab_activity_chapter_not_finished_progress_color
+ chapterFinishedArcPaint,
+ R.color.component_color_lessons_tab_activity_chapter_completed_progress_color,
)
- } else {
+
+ chapterInProgressArcPaint = Paint(Paint.ANTI_ALIAS_FLAG)
setupArcPaint(
- chapterNotStartedArcPaint,
- R.color.component_color_lessons_tab_activity_chapter_not_started_progress_color
+ chapterInProgressArcPaint,
+ R.color.component_color_lessons_tab_activity_chapter_in_progress_progress_color,
)
- }
- }
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
-
- val viewComponentFactory = FragmentManager.findFragment(this) as ViewComponentFactory
- val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
- viewComponent.inject(this)
- }
+ chapterNotStartedArcPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ if (chaptersFinished != 0) {
+ setupArcPaint(
+ chapterNotStartedArcPaint,
+ R.color.component_color_lessons_tab_activity_chapter_not_finished_progress_color,
+ )
+ } else {
+ setupArcPaint(
+ chapterNotStartedArcPaint,
+ R.color.component_color_lessons_tab_activity_chapter_not_started_progress_color,
+ )
+ }
+ }
- override fun onDraw(canvas: Canvas) {
- if (isRtl)
- rotationY = 180f
- super.onDraw(canvas)
- if (!this::baseRect.isInitialized) {
- val centerX = measuredWidth / 2
- val centerY = measuredHeight / 2
- val radius = Math.min(centerX, centerY)
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
- val startTop = (strokeWidth / 2).toInt()
- val startLeft = (strokeWidth / 2).toInt()
+ val viewComponentFactory = FragmentManager.findFragment(this) as ViewComponentFactory
+ val viewComponent = viewComponentFactory.createViewComponent(this) as ViewComponentImpl
+ viewComponent.inject(this)
+ }
- val endBottom = 2 * radius - startTop
- val endRight = 2 * radius - startTop
+ override fun onDraw(canvas: Canvas) {
+ if (isRtl) {
+ rotationY = 180f
+ }
+ super.onDraw(canvas)
+ if (!this::baseRect.isInitialized) {
+ val centerX = measuredWidth / 2
+ val centerY = measuredHeight / 2
+ val radius = Math.min(centerX, centerY)
- // baseRect will define the drawing space for drawArc().
- baseRect =
- RectF(startLeft.toFloat(), startTop.toFloat(), endRight.toFloat(), endBottom.toFloat())
- }
+ val startTop = (strokeWidth / 2).toInt()
+ val startLeft = (strokeWidth / 2).toInt()
- var angleStartPoint = -90f
+ val endBottom = 2 * radius - startTop
+ val endRight = 2 * radius - startTop
- if (totalChapters > 1) {
- // Draws arc for every finished chapter.
- for (i in 0 until chaptersFinished) {
- val startAngle =
- angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) +
- STROKE_DASH_GAP_IN_DEGREE / 2
- canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterFinishedArcPaint)
- }
- angleStartPoint += chaptersFinished * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE)
- // Draws arc for every chapter that is in progress.
- for (i in 0 until chaptersInProgress) {
- val startAngle =
- angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) +
- STROKE_DASH_GAP_IN_DEGREE / 2
- canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterInProgressArcPaint)
- }
- angleStartPoint += chaptersInProgress * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE)
- // Draws arc for every chapter that is not started.
- for (i in 0 until chaptersNotStarted) {
- val startAngle =
- angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) +
- STROKE_DASH_GAP_IN_DEGREE / 2
- canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterNotStartedArcPaint)
+ // baseRect will define the drawing space for drawArc().
+ baseRect =
+ RectF(startLeft.toFloat(), startTop.toFloat(), endRight.toFloat(), endBottom.toFloat())
}
- } else if (totalChapters == 1) {
- // Draws entire circle for finished an unfinished chapter.
- if (chaptersFinished == 1) {
- canvas.drawArc(
- baseRect,
- angleStartPoint,
- 360f,
- false,
- chapterFinishedArcPaint
- )
- } else if (chaptersInProgress == 1) {
- canvas.drawArc(
- baseRect,
- angleStartPoint,
- 360f,
- false,
- chapterInProgressArcPaint
- )
- } else {
- canvas.drawArc(
- baseRect,
- angleStartPoint,
- 360f,
- false,
- chapterNotStartedArcPaint
- )
+
+ var angleStartPoint = -90f
+
+ if (totalChapters > 1) {
+ // Draws arc for every finished chapter.
+ for (i in 0 until chaptersFinished) {
+ val startAngle =
+ angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) +
+ STROKE_DASH_GAP_IN_DEGREE / 2
+ canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterFinishedArcPaint)
+ }
+ angleStartPoint += chaptersFinished * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE)
+ // Draws arc for every chapter that is in progress.
+ for (i in 0 until chaptersInProgress) {
+ val startAngle =
+ angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) +
+ STROKE_DASH_GAP_IN_DEGREE / 2
+ canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterInProgressArcPaint)
+ }
+ angleStartPoint += chaptersInProgress * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE)
+ // Draws arc for every chapter that is not started.
+ for (i in 0 until chaptersNotStarted) {
+ val startAngle =
+ angleStartPoint + i * (sweepAngle + STROKE_DASH_GAP_IN_DEGREE) +
+ STROKE_DASH_GAP_IN_DEGREE / 2
+ canvas.drawArc(baseRect, startAngle, sweepAngle, false, chapterNotStartedArcPaint)
+ }
+ } else if (totalChapters == 1) {
+ // Draws entire circle for finished an unfinished chapter.
+ if (chaptersFinished == 1) {
+ canvas.drawArc(
+ baseRect,
+ angleStartPoint,
+ 360f,
+ false,
+ chapterFinishedArcPaint,
+ )
+ } else if (chaptersInProgress == 1) {
+ canvas.drawArc(
+ baseRect,
+ angleStartPoint,
+ 360f,
+ false,
+ chapterInProgressArcPaint,
+ )
+ } else {
+ canvas.drawArc(
+ baseRect,
+ angleStartPoint,
+ 360f,
+ false,
+ chapterNotStartedArcPaint,
+ )
+ }
}
}
- }
- private fun calculateSweepAngle() {
- val totalRemainingDegrees = (360 - STROKE_DASH_GAP_IN_DEGREE * totalChapters).toFloat()
- sweepAngle = totalRemainingDegrees / totalChapters
- }
+ private fun calculateSweepAngle() {
+ val totalRemainingDegrees = (360 - STROKE_DASH_GAP_IN_DEGREE * totalChapters).toFloat()
+ sweepAngle = totalRemainingDegrees / totalChapters
+ }
- private fun dpToPx(dp: Int): Float {
- return TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- dp.toFloat(),
- resources.displayMetrics
- )
- }
+ private fun dpToPx(dp: Int): Float =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ dp.toFloat(),
+ resources.displayMetrics,
+ )
- private fun setupArcPaint(arcPaint: Paint, color: Int) {
- arcPaint.apply {
- style = Paint.Style.STROKE
- strokeCap = Paint.Cap.ROUND
- strokeWidth = this@SegmentedCircularProgressView.strokeWidth
- this.color = ContextCompat.getColor(context, color)
+ private fun setupArcPaint(
+ arcPaint: Paint,
+ color: Int,
+ ) {
+ arcPaint.apply {
+ style = Paint.Style.STROKE
+ strokeCap = Paint.Cap.ROUND
+ strokeWidth = this@SegmentedCircularProgressView.strokeWidth
+ this.color = ContextCompat.getColor(context, color)
+ }
}
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/VerticalDashedLineView.kt b/app/src/main/java/org/oppia/android/app/customview/VerticalDashedLineView.kt
index 59349f1c03f..cb15067303e 100644
--- a/app/src/main/java/org/oppia/android/app/customview/VerticalDashedLineView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/VerticalDashedLineView.kt
@@ -22,46 +22,49 @@ private const val DASH_THICKNESS_IN_DP = 6
*
* Reference: https://stackoverflow.com/a/27054463
*/
-class VerticalDashedLineView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null
-) : View(context, attrs) {
- private val paint: Paint
+class VerticalDashedLineView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ ) : View(context, attrs) {
+ private val paint: Paint
- init {
- val dashGap: Float = dpToPx(DASH_GAP_IN_DP)
- val dashLength: Float = dpToPx(DASH_LENGTH_IN_DP)
- val dashThickness: Float = dpToPx(DASH_THICKNESS_IN_DP)
+ init {
+ val dashGap: Float = dpToPx(DASH_GAP_IN_DP)
+ val dashLength: Float = dpToPx(DASH_LENGTH_IN_DP)
+ val dashThickness: Float = dpToPx(DASH_THICKNESS_IN_DP)
- paint = Paint().apply {
- isAntiAlias = true
- color = ContextCompat.getColor(
- context,
- R.color.component_color_story_chapter_default_progress_solid_color
- )
- style = Paint.Style.STROKE
- strokeCap = Paint.Cap.ROUND
- strokeWidth = dashThickness
- pathEffect = DashPathEffect(floatArrayOf(dashLength, dashGap), /* phase= */ 0F)
+ paint =
+ Paint().apply {
+ isAntiAlias = true
+ color =
+ ContextCompat.getColor(
+ context,
+ R.color.component_color_story_chapter_default_progress_solid_color,
+ )
+ style = Paint.Style.STROKE
+ strokeCap = Paint.Cap.ROUND
+ strokeWidth = dashThickness
+ pathEffect = DashPathEffect(floatArrayOf(dashLength, dashGap), /* phase= */ 0F)
+ }
}
- }
- /** Sets the color. */
- fun setColor(colorId: Int) {
- paint.color = colorId
- invalidate()
- }
+ /** Sets the color. */
+ fun setColor(colorId: Int) {
+ paint.color = colorId
+ invalidate()
+ }
- override fun onDraw(canvas: Canvas) {
- val center = width * .5f
- canvas.drawLine(center, /* startY= */ 0f, center, height.toFloat(), paint)
- }
+ override fun onDraw(canvas: Canvas) {
+ val center = width * .5f
+ canvas.drawLine(center, /* startY= */ 0f, center, height.toFloat(), paint)
+ }
- private fun dpToPx(dp: Int): Float {
- return TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- dp.toFloat(),
- resources.displayMetrics
- )
+ private fun dpToPx(dp: Int): Float =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ dp.toFloat(),
+ resources.displayMetrics,
+ )
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt b/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt
index 547b38feaf2..251bc3194b8 100644
--- a/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt
@@ -21,55 +21,64 @@ import org.oppia.android.app.utility.KeyboardHelper.Companion.showSoftKeyboard
// maxLength="200".
/** The custom AppCompatEditText class for fraction input interaction view. */
-class FractionInputInteractionView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = android.R.attr.editTextStyle
-) : AppCompatEditText(context, attrs, defStyle), View.OnFocusChangeListener {
- private var hintText: CharSequence = ""
- private val stateKeyboardButtonListener: StateKeyboardButtonListener
+class FractionInputInteractionView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = android.R.attr.editTextStyle,
+ ) : AppCompatEditText(context, attrs, defStyle),
+ View.OnFocusChangeListener {
+ private var hintText: CharSequence = ""
+ private val stateKeyboardButtonListener: StateKeyboardButtonListener
- init {
- onFocusChangeListener = this
- // Assume multi-line for the purpose of properly showing long hints.
- isSingleLine = hint != null
- stateKeyboardButtonListener = context as StateKeyboardButtonListener
- }
-
- // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
- override fun onFocusChange(v: View, hasFocus: Boolean) = if (hasFocus) {
- hintText = hint
- hideHint()
- showSoftKeyboard(v, context)
- } else {
- restoreHint()
- hideSoftKeyboard(v, context)
- }
+ init {
+ onFocusChangeListener = this
+ // Assume multi-line for the purpose of properly showing long hints.
+ isSingleLine = hint != null
+ stateKeyboardButtonListener = context as StateKeyboardButtonListener
+ }
- override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
- if (event.keyCode == KEYCODE_BACK && event.action == ACTION_UP) {
- clearFocus()
+ // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
+ override fun onFocusChange(
+ v: View,
+ hasFocus: Boolean,
+ ) = if (hasFocus) {
+ hintText = hint
+ hideHint()
+ showSoftKeyboard(v, context)
+ } else {
restoreHint()
+ hideSoftKeyboard(v, context)
}
- return super.onKeyPreIme(keyCode, event)
- }
- override fun onEditorAction(actionCode: Int) {
- if (actionCode == EditorInfo.IME_ACTION_DONE) {
- stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ override fun onKeyPreIme(
+ keyCode: Int,
+ event: KeyEvent,
+ ): Boolean {
+ if (event.keyCode == KEYCODE_BACK && event.action == ACTION_UP) {
+ clearFocus()
+ restoreHint()
+ }
+ return super.onKeyPreIme(keyCode, event)
}
- super.onEditorAction(actionCode)
- }
- private fun hideHint() {
- hint = ""
- typeface = Typeface.DEFAULT
- isSingleLine = true
- }
+ override fun onEditorAction(actionCode: Int) {
+ if (actionCode == EditorInfo.IME_ACTION_DONE) {
+ stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ }
+ super.onEditorAction(actionCode)
+ }
- private fun restoreHint() {
- hint = hintText
- if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
- isSingleLine = false
+ private fun hideHint() {
+ hint = ""
+ typeface = Typeface.DEFAULT
+ isSingleLine = true
+ }
+
+ private fun restoreHint() {
+ hint = hintText
+ if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
+ isSingleLine = false
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsView.kt b/app/src/main/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsView.kt
index 63416dd5196..bb7502799b9 100644
--- a/app/src/main/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsView.kt
@@ -24,68 +24,77 @@ import org.oppia.android.app.utility.KeyboardHelper.Companion.showSoftKeyboard
* Note that the hint should be set via [setPlaceholder] to ensure that it's properly initialized if
* using databinding, otherwise setting the hint through android:hint should work fine.
*/
-class MathExpressionInteractionsView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = android.R.attr.editTextStyle
-) : AppCompatEditText(context, attrs, defStyle), View.OnFocusChangeListener {
- private var hintText: CharSequence = ""
- private val stateKeyboardButtonListener: StateKeyboardButtonListener
+class MathExpressionInteractionsView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = android.R.attr.editTextStyle,
+ ) : AppCompatEditText(context, attrs, defStyle),
+ View.OnFocusChangeListener {
+ private var hintText: CharSequence = ""
+ private val stateKeyboardButtonListener: StateKeyboardButtonListener
- init {
- onFocusChangeListener = this
- // Assume multi-line for the purpose of properly showing long hints.
- isSingleLine = hint != null
- stateKeyboardButtonListener = context as StateKeyboardButtonListener
- }
-
- // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
- override fun onFocusChange(v: View, hasFocus: Boolean) = if (hasFocus) {
- hintText = hint
- hideHint()
- showSoftKeyboard(v, context)
- } else {
- restoreHint()
- hideSoftKeyboard(v, context)
- }
+ init {
+ onFocusChangeListener = this
+ // Assume multi-line for the purpose of properly showing long hints.
+ isSingleLine = hint != null
+ stateKeyboardButtonListener = context as StateKeyboardButtonListener
+ }
- override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
- if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
- clearFocus()
+ // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
+ override fun onFocusChange(
+ v: View,
+ hasFocus: Boolean,
+ ) = if (hasFocus) {
+ hintText = hint
+ hideHint()
+ showSoftKeyboard(v, context)
+ } else {
restoreHint()
+ hideSoftKeyboard(v, context)
}
- return super.onKeyPreIme(keyCode, event)
- }
- override fun onEditorAction(actionCode: Int) {
- if (actionCode == EditorInfo.IME_ACTION_DONE) {
- stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ override fun onKeyPreIme(
+ keyCode: Int,
+ event: KeyEvent,
+ ): Boolean {
+ if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
+ clearFocus()
+ restoreHint()
+ }
+ return super.onKeyPreIme(keyCode, event)
}
- super.onEditorAction(actionCode)
- }
- /**
- * Sets the current placeholder text used by the view to [placeholderText].
- *
- * See the class's KDoc for caveats on how this relates to the text view's hint.
- */
- fun setPlaceholder(placeholderText: CharSequence) {
- hintText = placeholderText
- if (!hasFocus()) {
- hint = placeholderText
+ override fun onEditorAction(actionCode: Int) {
+ if (actionCode == EditorInfo.IME_ACTION_DONE) {
+ stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ }
+ super.onEditorAction(actionCode)
}
- }
- private fun hideHint() {
- hint = ""
- typeface = Typeface.DEFAULT
- isSingleLine = true
- }
+ /**
+ * Sets the current placeholder text used by the view to [placeholderText].
+ *
+ * See the class's KDoc for caveats on how this relates to the text view's hint.
+ */
+ fun setPlaceholder(placeholderText: CharSequence) {
+ hintText = placeholderText
+ if (!hasFocus()) {
+ hint = placeholderText
+ }
+ }
- private fun restoreHint() {
- hint = hintText
+ private fun hideHint() {
+ hint = ""
+ typeface = Typeface.DEFAULT
+ isSingleLine = true
+ }
+
+ private fun restoreHint() {
+ hint = hintText
- if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
- isSingleLine = false
+ if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
+ isSingleLine = false
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/interaction/NumericInputInteractionView.kt b/app/src/main/java/org/oppia/android/app/customview/interaction/NumericInputInteractionView.kt
index 9af64db917a..e4e2d90fa20 100644
--- a/app/src/main/java/org/oppia/android/app/customview/interaction/NumericInputInteractionView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/interaction/NumericInputInteractionView.kt
@@ -21,55 +21,64 @@ import org.oppia.android.app.utility.KeyboardHelper.Companion.showSoftKeyboard
// maxLength="200".
/** The custom AppCompatEditText class for numeric input interaction view. */
-class NumericInputInteractionView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = android.R.attr.editTextStyle
-) : AppCompatEditText(context, attrs, defStyle), View.OnFocusChangeListener {
- private val stateKeyboardButtonListener: StateKeyboardButtonListener
- private var hintText: CharSequence = ""
+class NumericInputInteractionView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = android.R.attr.editTextStyle,
+ ) : AppCompatEditText(context, attrs, defStyle),
+ View.OnFocusChangeListener {
+ private val stateKeyboardButtonListener: StateKeyboardButtonListener
+ private var hintText: CharSequence = ""
- init {
- onFocusChangeListener = this
- // Assume multi-line for the purpose of properly showing long hints.
- isSingleLine = hint != null
- stateKeyboardButtonListener = context as StateKeyboardButtonListener
- }
-
- // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
- override fun onFocusChange(v: View, hasFocus: Boolean) = if (hasFocus) {
- hintText = hint
- hideHint()
- showSoftKeyboard(v, context)
- } else {
- restoreHint()
- hideSoftKeyboard(v, context)
- }
+ init {
+ onFocusChangeListener = this
+ // Assume multi-line for the purpose of properly showing long hints.
+ isSingleLine = hint != null
+ stateKeyboardButtonListener = context as StateKeyboardButtonListener
+ }
- override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
- if (event.keyCode == KEYCODE_BACK && event.action == ACTION_UP) {
- clearFocus()
+ // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
+ override fun onFocusChange(
+ v: View,
+ hasFocus: Boolean,
+ ) = if (hasFocus) {
+ hintText = hint
+ hideHint()
+ showSoftKeyboard(v, context)
+ } else {
restoreHint()
+ hideSoftKeyboard(v, context)
}
- return super.onKeyPreIme(keyCode, event)
- }
- override fun onEditorAction(actionCode: Int) {
- if (actionCode == EditorInfo.IME_ACTION_DONE) {
- stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ override fun onKeyPreIme(
+ keyCode: Int,
+ event: KeyEvent,
+ ): Boolean {
+ if (event.keyCode == KEYCODE_BACK && event.action == ACTION_UP) {
+ clearFocus()
+ restoreHint()
+ }
+ return super.onKeyPreIme(keyCode, event)
}
- super.onEditorAction(actionCode)
- }
- private fun hideHint() {
- hint = ""
- typeface = Typeface.DEFAULT
- isSingleLine = true
- }
+ override fun onEditorAction(actionCode: Int) {
+ if (actionCode == EditorInfo.IME_ACTION_DONE) {
+ stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ }
+ super.onEditorAction(actionCode)
+ }
- private fun restoreHint() {
- hint = hintText
- if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
- isSingleLine = false
+ private fun hideHint() {
+ hint = ""
+ typeface = Typeface.DEFAULT
+ isSingleLine = true
+ }
+
+ private fun restoreHint() {
+ hint = hintText
+ if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
+ isSingleLine = false
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/interaction/RatioInputInteractionView.kt b/app/src/main/java/org/oppia/android/app/customview/interaction/RatioInputInteractionView.kt
index 1ad77b316b2..b6c1fa4669d 100644
--- a/app/src/main/java/org/oppia/android/app/customview/interaction/RatioInputInteractionView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/interaction/RatioInputInteractionView.kt
@@ -11,55 +11,64 @@ import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener
import org.oppia.android.app.utility.KeyboardHelper
/** The custom AppCompatEditText class for ratio input interaction view. */
-class RatioInputInteractionView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = android.R.attr.editTextStyle
-) : AppCompatEditText(context, attrs, defStyle), View.OnFocusChangeListener {
- private var hintText: CharSequence = ""
- private val stateKeyboardButtonListener: StateKeyboardButtonListener
+class RatioInputInteractionView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = android.R.attr.editTextStyle,
+ ) : AppCompatEditText(context, attrs, defStyle),
+ View.OnFocusChangeListener {
+ private var hintText: CharSequence = ""
+ private val stateKeyboardButtonListener: StateKeyboardButtonListener
- init {
- onFocusChangeListener = this
- // Assume multi-line for the purpose of properly showing long hints.
- isSingleLine = hint != null
- stateKeyboardButtonListener = context as StateKeyboardButtonListener
- }
-
- // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
- override fun onFocusChange(v: View, hasFocus: Boolean) = if (hasFocus) {
- hintText = hint
- hideHint()
- KeyboardHelper.showSoftKeyboard(v, context)
- } else {
- restoreHint()
- KeyboardHelper.hideSoftKeyboard(v, context)
- }
+ init {
+ onFocusChangeListener = this
+ // Assume multi-line for the purpose of properly showing long hints.
+ isSingleLine = hint != null
+ stateKeyboardButtonListener = context as StateKeyboardButtonListener
+ }
- override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
- if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
- clearFocus()
+ // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
+ override fun onFocusChange(
+ v: View,
+ hasFocus: Boolean,
+ ) = if (hasFocus) {
+ hintText = hint
+ hideHint()
+ KeyboardHelper.showSoftKeyboard(v, context)
+ } else {
restoreHint()
+ KeyboardHelper.hideSoftKeyboard(v, context)
}
- return super.onKeyPreIme(keyCode, event)
- }
- override fun onEditorAction(actionCode: Int) {
- if (actionCode == EditorInfo.IME_ACTION_DONE) {
- stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ override fun onKeyPreIme(
+ keyCode: Int,
+ event: KeyEvent,
+ ): Boolean {
+ if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
+ clearFocus()
+ restoreHint()
+ }
+ return super.onKeyPreIme(keyCode, event)
}
- super.onEditorAction(actionCode)
- }
- private fun hideHint() {
- hint = ""
- typeface = Typeface.DEFAULT
- isSingleLine = true
- }
+ override fun onEditorAction(actionCode: Int) {
+ if (actionCode == EditorInfo.IME_ACTION_DONE) {
+ stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ }
+ super.onEditorAction(actionCode)
+ }
- private fun restoreHint() {
- hint = hintText
- if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
- isSingleLine = false
+ private fun hideHint() {
+ hint = ""
+ typeface = Typeface.DEFAULT
+ isSingleLine = true
+ }
+
+ private fun restoreHint() {
+ hint = hintText
+ if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
+ isSingleLine = false
+ }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/customview/interaction/TextInputInteractionView.kt b/app/src/main/java/org/oppia/android/app/customview/interaction/TextInputInteractionView.kt
index 56217ef9fa4..366b4b0ada2 100644
--- a/app/src/main/java/org/oppia/android/app/customview/interaction/TextInputInteractionView.kt
+++ b/app/src/main/java/org/oppia/android/app/customview/interaction/TextInputInteractionView.kt
@@ -18,55 +18,64 @@ import org.oppia.android.app.utility.KeyboardHelper.Companion.showSoftKeyboard
// maxLength="200".
/** The custom AppCompatEditText class for text input interaction view. */
-class TextInputInteractionView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = android.R.attr.editTextStyle
-) : AppCompatEditText(context, attrs, defStyle), View.OnFocusChangeListener {
- private var hintText: CharSequence = ""
- private val stateKeyboardButtonListener: StateKeyboardButtonListener
+class TextInputInteractionView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = android.R.attr.editTextStyle,
+ ) : AppCompatEditText(context, attrs, defStyle),
+ View.OnFocusChangeListener {
+ private var hintText: CharSequence = ""
+ private val stateKeyboardButtonListener: StateKeyboardButtonListener
- init {
- onFocusChangeListener = this
- // Assume multi-line for the purpose of properly showing long hints.
- isSingleLine = hint != null
- stateKeyboardButtonListener = context as StateKeyboardButtonListener
- }
-
- // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
- override fun onFocusChange(v: View, hasFocus: Boolean) = if (hasFocus) {
- hintText = hint
- hideHint()
- showSoftKeyboard(v, context)
- } else {
- restoreHint()
- hideSoftKeyboard(v, context)
- }
+ init {
+ onFocusChangeListener = this
+ // Assume multi-line for the purpose of properly showing long hints.
+ isSingleLine = hint != null
+ stateKeyboardButtonListener = context as StateKeyboardButtonListener
+ }
- override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
- if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
- clearFocus()
+ // TODO(#4574): Add tests to verify that the placeholder correctly shows/doesn’t show when expected
+ override fun onFocusChange(
+ v: View,
+ hasFocus: Boolean,
+ ) = if (hasFocus) {
+ hintText = hint
+ hideHint()
+ showSoftKeyboard(v, context)
+ } else {
restoreHint()
+ hideSoftKeyboard(v, context)
}
- return super.onKeyPreIme(keyCode, event)
- }
- override fun onEditorAction(actionCode: Int) {
- if (actionCode == EditorInfo.IME_ACTION_DONE) {
- stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ override fun onKeyPreIme(
+ keyCode: Int,
+ event: KeyEvent,
+ ): Boolean {
+ if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
+ clearFocus()
+ restoreHint()
+ }
+ return super.onKeyPreIme(keyCode, event)
}
- super.onEditorAction(actionCode)
- }
- private fun hideHint() {
- hint = ""
- typeface = Typeface.DEFAULT
- isSingleLine = true
- }
+ override fun onEditorAction(actionCode: Int) {
+ if (actionCode == EditorInfo.IME_ACTION_DONE) {
+ stateKeyboardButtonListener.onEditorAction(EditorInfo.IME_ACTION_DONE)
+ }
+ super.onEditorAction(actionCode)
+ }
- private fun restoreHint() {
- hint = hintText
- if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
- isSingleLine = false
+ private fun hideHint() {
+ hint = ""
+ typeface = Typeface.DEFAULT
+ isSingleLine = true
+ }
+
+ private fun restoreHint() {
+ hint = hintText
+ if (text?.isEmpty() == true) setTypeface(typeface, Typeface.ITALIC)
+ isSingleLine = false
+ }
}
-}
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 eda9f03bc9a..13bf2607e43 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
@@ -30,7 +30,6 @@ class DeveloperOptionsActivity :
RouteToViewEventLogsListener,
RouteToForceNetworkTypeListener,
RouteToMathExpressionParserTestListener {
-
@Inject
lateinit var developerOptionsActivityPresenter: DeveloperOptionsActivityPresenter
@@ -50,22 +49,24 @@ class DeveloperOptionsActivity :
override fun routeToMarkChaptersCompleted() {
startActivity(
MarkChaptersCompletedActivity.createMarkChaptersCompletedIntent(
- context = this, internalProfileId, showConfirmationNotice = false
- )
+ context = this,
+ internalProfileId,
+ showConfirmationNotice = false,
+ ),
)
}
override fun routeToMarkStoriesCompleted() {
startActivity(
MarkStoriesCompletedActivity
- .createMarkStoriesCompletedIntent(this, internalProfileId)
+ .createMarkStoriesCompletedIntent(this, internalProfileId),
)
}
override fun routeToMarkTopicsCompleted() {
startActivity(
MarkTopicsCompletedActivity
- .createMarkTopicsCompletedIntent(this, internalProfileId)
+ .createMarkTopicsCompletedIntent(this, internalProfileId),
)
}
@@ -82,15 +83,15 @@ class DeveloperOptionsActivity :
}
companion object {
-
/** Function to create intent for DeveloperOptionsActivity. */
- fun createDeveloperOptionsActivityIntent(context: Context, profileId: ProfileId): Intent {
-
- return Intent(context, DeveloperOptionsActivity::class.java).apply {
+ fun createDeveloperOptionsActivityIntent(
+ context: Context,
+ profileId: ProfileId,
+ ): Intent =
+ Intent(context, DeveloperOptionsActivity::class.java).apply {
decorateWithScreenName(DEVELOPER_OPTIONS_ACTIVITY)
decorateWithUserProfileId(profileId)
}
- }
}
override fun forceCrash() {
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt
index 9caf3ee3bfb..7d08e85d544 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt
@@ -10,52 +10,56 @@ import javax.inject.Inject
/** The presenter for [DeveloperOptionsActivity]. */
@ActivityScope
-class DeveloperOptionsActivityPresenter @Inject constructor(
- private val activity: AppCompatActivity
-) {
- private lateinit var navigationDrawerFragment: NavigationDrawerFragment
- private lateinit var binding: DeveloperOptionsActivityBinding
+class DeveloperOptionsActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ ) {
+ private lateinit var navigationDrawerFragment: NavigationDrawerFragment
+ private lateinit var binding: DeveloperOptionsActivityBinding
- fun handleOnCreate() {
- binding = DataBindingUtil.setContentView(
- activity,
- R.layout.developer_options_activity
- )
- setUpNavigationDrawer()
- val previousFragment = getDeveloperOptionsFragment()
- if (previousFragment == null) {
- activity.supportFragmentManager.beginTransaction().add(
- R.id.developer_options_fragment_placeholder,
- DeveloperOptionsFragment.newInstance()
- ).commitNow()
+ fun handleOnCreate() {
+ binding =
+ DataBindingUtil.setContentView(
+ activity,
+ R.layout.developer_options_activity,
+ )
+ setUpNavigationDrawer()
+ val previousFragment = getDeveloperOptionsFragment()
+ if (previousFragment == null) {
+ activity.supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.developer_options_fragment_placeholder,
+ DeveloperOptionsFragment.newInstance(),
+ ).commitNow()
+ }
}
- }
- private fun setUpNavigationDrawer() {
- val toolbar = binding.developerOptionsActivityToolbar
- activity.setSupportActionBar(toolbar)
- activity.supportActionBar!!.setDisplayShowHomeEnabled(true)
- navigationDrawerFragment = activity
- .supportFragmentManager
- .findFragmentById(
- R.id.developer_options_activity_fragment_navigation_drawer
- ) as NavigationDrawerFragment
- navigationDrawerFragment.setUpDrawer(
- binding.developerOptionsActivityDrawerLayout,
- toolbar, menuItemId = -1
- )
- }
+ private fun setUpNavigationDrawer() {
+ val toolbar = binding.developerOptionsActivityToolbar
+ activity.setSupportActionBar(toolbar)
+ activity.supportActionBar!!.setDisplayShowHomeEnabled(true)
+ navigationDrawerFragment =
+ activity
+ .supportFragmentManager
+ .findFragmentById(
+ R.id.developer_options_activity_fragment_navigation_drawer,
+ ) as NavigationDrawerFragment
+ navigationDrawerFragment.setUpDrawer(
+ binding.developerOptionsActivityDrawerLayout,
+ toolbar,
+ menuItemId = -1,
+ )
+ }
- private fun getDeveloperOptionsFragment(): DeveloperOptionsFragment? {
- return activity
- .supportFragmentManager
- .findFragmentById(
- R.id.developer_options_fragment_placeholder
- ) as DeveloperOptionsFragment?
- }
+ private fun getDeveloperOptionsFragment(): DeveloperOptionsFragment? =
+ activity
+ .supportFragmentManager
+ .findFragmentById(
+ R.id.developer_options_fragment_placeholder,
+ ) as DeveloperOptionsFragment?
- /** Called when the 'force crash' button is clicked by the user. This function crashes the app and will not return. */
- fun forceCrash(): Nothing {
- throw RuntimeException("Force crash occurred")
+ /** Called when the 'force crash' button is clicked by the user. This function crashes the app and will not return. */
+ fun forceCrash(): Nothing = throw RuntimeException("Force crash occurred")
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragment.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragment.kt
index 7da0ba78cc0..b9ba758a675 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragment.kt
@@ -15,9 +15,7 @@ class DeveloperOptionsFragment : InjectableFragment() {
lateinit var developerOptionsFragmentPresenter: DeveloperOptionsFragmentPresenter
companion object {
- fun newInstance(): DeveloperOptionsFragment {
- return DeveloperOptionsFragment()
- }
+ fun newInstance(): DeveloperOptionsFragment = DeveloperOptionsFragment()
}
override fun onAttach(context: Context) {
@@ -28,8 +26,6 @@ class DeveloperOptionsFragment : InjectableFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return developerOptionsFragmentPresenter.handleCreateView(inflater, container)
- }
+ savedInstanceState: Bundle?,
+ ): View? = developerOptionsFragmentPresenter.handleCreateView(inflater, container)
}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentPresenter.kt
index 1b280beb5ef..dcf2aaed1cd 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentPresenter.kt
@@ -22,96 +22,94 @@ import javax.inject.Inject
/** The presenter for [DeveloperOptionsFragment]. */
@FragmentScope
-class DeveloperOptionsFragmentPresenter @Inject constructor(
- private val activity: AppCompatActivity,
- private val fragment: Fragment,
- private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory
-) {
+class DeveloperOptionsFragmentPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val fragment: Fragment,
+ private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory,
+ ) {
+ private lateinit var binding: DeveloperOptionsFragmentBinding
+ private lateinit var linearLayoutManager: LinearLayoutManager
- private lateinit var binding: DeveloperOptionsFragmentBinding
- private lateinit var linearLayoutManager: LinearLayoutManager
+ @Inject
+ lateinit var developerOptionsViewModel: DeveloperOptionsViewModel
- @Inject
- lateinit var developerOptionsViewModel: DeveloperOptionsViewModel
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ ): View? {
+ binding =
+ DeveloperOptionsFragmentBinding.inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
- fun handleCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?
- ): View? {
- binding = DeveloperOptionsFragmentBinding.inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
+ linearLayoutManager = LinearLayoutManager(activity.applicationContext)
- linearLayoutManager = LinearLayoutManager(activity.applicationContext)
+ binding.developerOptionsList.apply {
+ layoutManager = linearLayoutManager
+ adapter = createRecyclerViewAdapter()
+ }
- binding.developerOptionsList.apply {
- layoutManager = linearLayoutManager
- adapter = createRecyclerViewAdapter()
- }
+ binding.apply {
+ this.viewModel = developerOptionsViewModel
+ this.lifecycleOwner = fragment
+ }
- binding.apply {
- this.viewModel = developerOptionsViewModel
- this.lifecycleOwner = fragment
+ return binding.root
}
- return binding.root
- }
+ private fun createRecyclerViewAdapter(): BindableAdapter =
+ multiTypeBuilderFactory
+ .create { viewModel ->
+ when (viewModel) {
+ is DeveloperOptionsModifyLessonProgressViewModel -> {
+ viewModel.itemIndex.set(0)
+ ViewType.VIEW_TYPE_MODIFY_LESSON_PROGRESS
+ }
+ is DeveloperOptionsViewLogsViewModel -> {
+ viewModel.itemIndex.set(1)
+ ViewType.VIEW_TYPE_VIEW_LOGS
+ }
+ is DeveloperOptionsOverrideAppBehaviorsViewModel -> {
+ viewModel.itemIndex.set(2)
+ ViewType.VIEW_TYPE_OVERRIDE_APP_BEHAVIORS
+ }
+ is DeveloperOptionsTestParsersViewModel -> {
+ viewModel.itemIndex.set(3)
+ ViewType.VIEW_TYPE_TEST_PARSERS
+ }
+ else -> throw IllegalArgumentException("Encountered unexpected view model: $viewModel")
+ }
+ }.registerViewDataBinder(
+ viewType = ViewType.VIEW_TYPE_MODIFY_LESSON_PROGRESS,
+ inflateDataBinding = DeveloperOptionsModifyLessonProgressViewBinding::inflate,
+ setViewModel = DeveloperOptionsModifyLessonProgressViewBinding::setViewModel,
+ transformViewModel = { it as DeveloperOptionsModifyLessonProgressViewModel },
+ ).registerViewDataBinder(
+ viewType = ViewType.VIEW_TYPE_VIEW_LOGS,
+ inflateDataBinding = DeveloperOptionsViewLogsViewBinding::inflate,
+ setViewModel = DeveloperOptionsViewLogsViewBinding::setViewModel,
+ transformViewModel = { it as DeveloperOptionsViewLogsViewModel },
+ ).registerViewDataBinder(
+ viewType = ViewType.VIEW_TYPE_OVERRIDE_APP_BEHAVIORS,
+ inflateDataBinding = DeveloperOptionsOverrideAppBehaviorsViewBinding::inflate,
+ setViewModel = DeveloperOptionsOverrideAppBehaviorsViewBinding::setViewModel,
+ transformViewModel = { it as DeveloperOptionsOverrideAppBehaviorsViewModel },
+ ).registerViewDataBinder(
+ viewType = ViewType.VIEW_TYPE_TEST_PARSERS,
+ inflateDataBinding = DeveloperOptionsTestParsersViewBinding::inflate,
+ setViewModel = DeveloperOptionsTestParsersViewBinding::setViewModel,
+ transformViewModel = { it as DeveloperOptionsTestParsersViewModel },
+ ).build()
- private fun createRecyclerViewAdapter(): BindableAdapter {
- return multiTypeBuilderFactory.create { viewModel ->
- when (viewModel) {
- is DeveloperOptionsModifyLessonProgressViewModel -> {
- viewModel.itemIndex.set(0)
- ViewType.VIEW_TYPE_MODIFY_LESSON_PROGRESS
- }
- is DeveloperOptionsViewLogsViewModel -> {
- viewModel.itemIndex.set(1)
- ViewType.VIEW_TYPE_VIEW_LOGS
- }
- is DeveloperOptionsOverrideAppBehaviorsViewModel -> {
- viewModel.itemIndex.set(2)
- ViewType.VIEW_TYPE_OVERRIDE_APP_BEHAVIORS
- }
- is DeveloperOptionsTestParsersViewModel -> {
- viewModel.itemIndex.set(3)
- ViewType.VIEW_TYPE_TEST_PARSERS
- }
- else -> throw IllegalArgumentException("Encountered unexpected view model: $viewModel")
- }
+ private enum class ViewType {
+ VIEW_TYPE_MODIFY_LESSON_PROGRESS,
+ VIEW_TYPE_VIEW_LOGS,
+ VIEW_TYPE_OVERRIDE_APP_BEHAVIORS,
+ VIEW_TYPE_TEST_PARSERS,
}
- .registerViewDataBinder(
- viewType = ViewType.VIEW_TYPE_MODIFY_LESSON_PROGRESS,
- inflateDataBinding = DeveloperOptionsModifyLessonProgressViewBinding::inflate,
- setViewModel = DeveloperOptionsModifyLessonProgressViewBinding::setViewModel,
- transformViewModel = { it as DeveloperOptionsModifyLessonProgressViewModel }
- )
- .registerViewDataBinder(
- viewType = ViewType.VIEW_TYPE_VIEW_LOGS,
- inflateDataBinding = DeveloperOptionsViewLogsViewBinding::inflate,
- setViewModel = DeveloperOptionsViewLogsViewBinding::setViewModel,
- transformViewModel = { it as DeveloperOptionsViewLogsViewModel }
- )
- .registerViewDataBinder(
- viewType = ViewType.VIEW_TYPE_OVERRIDE_APP_BEHAVIORS,
- inflateDataBinding = DeveloperOptionsOverrideAppBehaviorsViewBinding::inflate,
- setViewModel = DeveloperOptionsOverrideAppBehaviorsViewBinding::setViewModel,
- transformViewModel = { it as DeveloperOptionsOverrideAppBehaviorsViewModel }
- )
- .registerViewDataBinder(
- viewType = ViewType.VIEW_TYPE_TEST_PARSERS,
- inflateDataBinding = DeveloperOptionsTestParsersViewBinding::inflate,
- setViewModel = DeveloperOptionsTestParsersViewBinding::setViewModel,
- transformViewModel = { it as DeveloperOptionsTestParsersViewModel }
- )
- .build()
- }
-
- private enum class ViewType {
- VIEW_TYPE_MODIFY_LESSON_PROGRESS,
- VIEW_TYPE_VIEW_LOGS,
- VIEW_TYPE_OVERRIDE_APP_BEHAVIORS,
- VIEW_TYPE_TEST_PARSERS
}
-}
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 6a1f5aadeb7..cad35013974 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
@@ -6,5 +6,8 @@ import org.oppia.android.app.model.ProfileId
/** Interface to create intent for [DeveloperOptionsActivity]. */
interface DeveloperOptionsStarter {
- fun createIntent(context: Context, profileId: ProfileId): 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 5c18f62a718..84e84f09687 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
@@ -6,7 +6,11 @@ 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, profileId: ProfileId): Intent =
- DeveloperOptionsActivity.createDeveloperOptionsActivityIntent(context, profileId)
-}
+class DeveloperOptionsStarterImpl
+ @Inject
+ constructor() : DeveloperOptionsStarter {
+ override fun createIntent(
+ context: Context,
+ profileId: ProfileId,
+ ): Intent = DeveloperOptionsActivity.createDeveloperOptionsActivityIntent(context, profileId)
+ }
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterModule.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterModule.kt
index 71145d4b806..f9227eefc82 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterModule.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsStarterModule.kt
@@ -7,7 +7,5 @@ import dagger.Module
@Module
interface DeveloperOptionsStarterModule {
@Binds
- fun bindsDeveloperOptionsStarter(
- impl: DeveloperOptionsStarterImpl
- ): DeveloperOptionsStarter
+ fun bindsDeveloperOptionsStarter(impl: DeveloperOptionsStarterImpl): DeveloperOptionsStarter
}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsViewModel.kt
index a6ee237ec55..f5e7dfd471a 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsViewModel.kt
@@ -16,44 +16,45 @@ import javax.inject.Inject
* [DeveloperOptionsItemViewModel] which in turn implement corresponding functionalities.
*/
@FragmentScope
-class DeveloperOptionsViewModel @Inject constructor(
- activity: AppCompatActivity,
- private val showAllHintsAndSolutionController: ShowAllHintsAndSolutionController
-) {
- private val forceCrashButtonClickListener = activity as ForceCrashButtonClickListener
- private val routeToMarkChaptersCompletedListener =
- activity as RouteToMarkChaptersCompletedListener
- private val routeToMarkStoriesCompletedListener =
- activity as RouteToMarkStoriesCompletedListener
- private val routeToMarkTopicsCompletedListener =
- activity as RouteToMarkTopicsCompletedListener
- private val routeToViewEventLogsListener = activity as RouteToViewEventLogsListener
- private val routeToForceNetworkTypeListener = activity as RouteToForceNetworkTypeListener
- private val routeToMathExpressionParserTestListener =
- activity as RouteToMathExpressionParserTestListener
+class DeveloperOptionsViewModel
+ @Inject
+ constructor(
+ activity: AppCompatActivity,
+ private val showAllHintsAndSolutionController: ShowAllHintsAndSolutionController,
+ ) {
+ private val forceCrashButtonClickListener = activity as ForceCrashButtonClickListener
+ private val routeToMarkChaptersCompletedListener =
+ activity as RouteToMarkChaptersCompletedListener
+ private val routeToMarkStoriesCompletedListener =
+ activity as RouteToMarkStoriesCompletedListener
+ private val routeToMarkTopicsCompletedListener =
+ activity as RouteToMarkTopicsCompletedListener
+ private val routeToViewEventLogsListener = activity as RouteToViewEventLogsListener
+ private val routeToForceNetworkTypeListener = activity as RouteToForceNetworkTypeListener
+ private val routeToMathExpressionParserTestListener =
+ activity as RouteToMathExpressionParserTestListener
- /**
- * List of [DeveloperOptionsItemViewModel] used to populate recyclerview of
- * [DeveloperOptionsFragment] to enable corresponding functionalities.
- */
- val developerOptionsList: List by lazy {
- processDeveloperOptionsList()
- }
+ /**
+ * List of [DeveloperOptionsItemViewModel] used to populate recyclerview of
+ * [DeveloperOptionsFragment] to enable corresponding functionalities.
+ */
+ val developerOptionsList: List by lazy {
+ processDeveloperOptionsList()
+ }
- private fun processDeveloperOptionsList(): List {
- return listOf(
- DeveloperOptionsModifyLessonProgressViewModel(
- routeToMarkChaptersCompletedListener,
- routeToMarkStoriesCompletedListener,
- routeToMarkTopicsCompletedListener
- ),
- DeveloperOptionsViewLogsViewModel(routeToViewEventLogsListener),
- DeveloperOptionsOverrideAppBehaviorsViewModel(
- forceCrashButtonClickListener,
- routeToForceNetworkTypeListener,
- showAllHintsAndSolutionController
- ),
- DeveloperOptionsTestParsersViewModel(routeToMathExpressionParserTestListener)
- )
+ private fun processDeveloperOptionsList(): List =
+ listOf(
+ DeveloperOptionsModifyLessonProgressViewModel(
+ routeToMarkChaptersCompletedListener,
+ routeToMarkStoriesCompletedListener,
+ routeToMarkTopicsCompletedListener,
+ ),
+ DeveloperOptionsViewLogsViewModel(routeToViewEventLogsListener),
+ DeveloperOptionsOverrideAppBehaviorsViewModel(
+ forceCrashButtonClickListener,
+ routeToForceNetworkTypeListener,
+ showAllHintsAndSolutionController,
+ ),
+ DeveloperOptionsTestParsersViewModel(routeToMathExpressionParserTestListener),
+ )
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsModifyLessonProgressViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsModifyLessonProgressViewModel.kt
index 6672f488f2c..2b3da404741 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsModifyLessonProgressViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsModifyLessonProgressViewModel.kt
@@ -11,9 +11,8 @@ import org.oppia.android.app.devoptions.RouteToMarkTopicsCompletedListener
class DeveloperOptionsModifyLessonProgressViewModel(
private val routeToMarkChaptersCompletedListener: RouteToMarkChaptersCompletedListener,
private val routeToMarkStoriesCompletedListener: RouteToMarkStoriesCompletedListener,
- private val routeToMarkTopicsCompletedListener: RouteToMarkTopicsCompletedListener
+ private val routeToMarkTopicsCompletedListener: RouteToMarkTopicsCompletedListener,
) : DeveloperOptionsItemViewModel() {
-
/** Routes user to [MarkChaptersCompletedActivity] screen for modifying the progress of chapters. */
fun onMarkChaptersCompletedClicked() {
routeToMarkChaptersCompletedListener.routeToMarkChaptersCompleted()
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsOverrideAppBehaviorsViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsOverrideAppBehaviorsViewModel.kt
index 273733e3f3d..70c4bf86915 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsOverrideAppBehaviorsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsOverrideAppBehaviorsViewModel.kt
@@ -12,9 +12,8 @@ import org.oppia.android.domain.devoptions.ShowAllHintsAndSolutionController
class DeveloperOptionsOverrideAppBehaviorsViewModel(
private val forceCrashButtonClickListener: ForceCrashButtonClickListener,
private val forceNetworkTypeListener: RouteToForceNetworkTypeListener,
- private val showAllHintsAndSolutionController: ShowAllHintsAndSolutionController
+ private val showAllHintsAndSolutionController: ShowAllHintsAndSolutionController,
) : DeveloperOptionsItemViewModel() {
-
/** Identifies whether the feature to show all hints and solution is enabled or disabled. */
val isShowAllHintsAndSolutionEnabled =
ObservableField(showAllHintsAndSolutionController.getShowAllHintsAndSolution())
@@ -35,10 +34,10 @@ class DeveloperOptionsOverrideAppBehaviorsViewModel(
*/
fun onShowAllHintsAndSolutionClicked() {
showAllHintsAndSolutionController.setShowAllHintsAndSolution(
- !(showAllHintsAndSolutionController.getShowAllHintsAndSolution())
+ !(showAllHintsAndSolutionController.getShowAllHintsAndSolution()),
)
isShowAllHintsAndSolutionEnabled.set(
- showAllHintsAndSolutionController.getShowAllHintsAndSolution()
+ showAllHintsAndSolutionController.getShowAllHintsAndSolution(),
)
}
}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsTestParsersViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsTestParsersViewModel.kt
index 00e7edf8f56..939e47e96cc 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsTestParsersViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsTestParsersViewModel.kt
@@ -7,7 +7,7 @@ import org.oppia.android.app.devoptions.RouteToMathExpressionParserTestListener
* equations.
*/
class DeveloperOptionsTestParsersViewModel(
- private val routeToMathExpressionParserTestListener: RouteToMathExpressionParserTestListener
+ private val routeToMathExpressionParserTestListener: RouteToMathExpressionParserTestListener,
) : DeveloperOptionsItemViewModel() {
/** Routes the user to an activity for testing math expressions & equations. */
fun onMathExpressionsClicked() {
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsViewLogsViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsViewLogsViewModel.kt
index 750c0abe580..a58c123ebef 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsViewLogsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsViewLogsViewModel.kt
@@ -4,7 +4,7 @@ import org.oppia.android.app.devoptions.RouteToViewEventLogsListener
/** [DeveloperOptionsItemViewModel] to provide features to view logs such as analytic event logs. */
class DeveloperOptionsViewLogsViewModel(
- private val routeToViewEventLogsListener: RouteToViewEventLogsListener
+ private val routeToViewEventLogsListener: RouteToViewEventLogsListener,
) : DeveloperOptionsItemViewModel() {
/** Routes the user to [ViewEventLogsActivity] for displaying the event logs. */
fun onEventLogsClicked() {
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivity.kt
index 78d07c9e7f4..93bff5e7b87 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivity.kt
@@ -28,10 +28,9 @@ class ForceNetworkTypeActivity : InjectableAutoLocalizedAppCompatActivity() {
companion object {
/** Returns [Intent] for [ForceNetworkTypeActivity]. */
- fun createForceNetworkTypeActivityIntent(context: Context): Intent {
- return Intent(context, ForceNetworkTypeActivity::class.java).apply {
+ fun createForceNetworkTypeActivityIntent(context: Context): Intent =
+ Intent(context, ForceNetworkTypeActivity::class.java).apply {
decorateWithScreenName(FORCE_NETWORK_TYPE_ACTIVITY)
}
- }
}
}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt
index f868bfbf267..db29daafc7b 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt
@@ -7,27 +7,29 @@ import javax.inject.Inject
/** The presenter for [ForceNetworkTypeActivity]. */
@ActivityScope
-class ForceNetworkTypeActivityPresenter @Inject constructor(
- private val activity: AppCompatActivity
-) {
+class ForceNetworkTypeActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ ) {
+ /** Called when [ForceNetworkTypeActivity] is created. Handles UI for the activity. */
+ fun handleOnCreate() {
+ activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp)
+ activity.setContentView(R.layout.force_network_type_activity)
- /** Called when [ForceNetworkTypeActivity] is created. Handles UI for the activity. */
- fun handleOnCreate() {
- activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
- activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp)
- activity.setContentView(R.layout.force_network_type_activity)
-
- if (getForceNetworkTypeFragment() == null) {
- val forceNetworkTypeFragment = ForceNetworkTypeFragment.newInstance()
- activity.supportFragmentManager.beginTransaction().add(
- R.id.force_network_type_container,
- forceNetworkTypeFragment
- ).commitNow()
+ if (getForceNetworkTypeFragment() == null) {
+ val forceNetworkTypeFragment = ForceNetworkTypeFragment.newInstance()
+ activity.supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.force_network_type_container,
+ forceNetworkTypeFragment,
+ ).commitNow()
+ }
}
- }
- private fun getForceNetworkTypeFragment(): ForceNetworkTypeFragment? {
- return activity.supportFragmentManager
- .findFragmentById(R.id.force_network_type_container) as ForceNetworkTypeFragment?
+ private fun getForceNetworkTypeFragment(): ForceNetworkTypeFragment? =
+ activity.supportFragmentManager
+ .findFragmentById(R.id.force_network_type_container) as ForceNetworkTypeFragment?
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragment.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragment.kt
index f96f58da352..2a6cadcec4b 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragment.kt
@@ -27,8 +27,6 @@ class ForceNetworkTypeFragment : InjectableFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return forceNetworkTypeFragmentPresenter.handleCreateView(inflater, container)
- }
+ savedInstanceState: Bundle?,
+ ): View? = forceNetworkTypeFragmentPresenter.handleCreateView(inflater, container)
}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt
index 54db449bc89..fdfc66fb3b0 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt
@@ -16,70 +16,72 @@ import javax.inject.Inject
/** The presenter for [ForceNetworkTypeFragment]. */
@FragmentScope
-class ForceNetworkTypeFragmentPresenter @Inject constructor(
- private val activity: AppCompatActivity,
- private val fragment: Fragment,
- private val networkConnectionUtil: Optional,
- private val forceNetworkTypeViewModel: ForceNetworkTypeViewModel,
- private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory
-) {
+class ForceNetworkTypeFragmentPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val fragment: Fragment,
+ private val networkConnectionUtil: Optional,
+ private val forceNetworkTypeViewModel: ForceNetworkTypeViewModel,
+ private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory,
+ ) {
+ private lateinit var binding: ForceNetworkTypeFragmentBinding
+ private lateinit var linearLayoutManager: LinearLayoutManager
+ private lateinit var bindingAdapter: BindableAdapter
- private lateinit var binding: ForceNetworkTypeFragmentBinding
- private lateinit var linearLayoutManager: LinearLayoutManager
- private lateinit var bindingAdapter: BindableAdapter
+ /** Called when [ForceNetworkTypeFragment] is created. Handles UI for the fragment. */
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ ): View? {
+ binding =
+ ForceNetworkTypeFragmentBinding.inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
- /** Called when [ForceNetworkTypeFragment] is created. Handles UI for the fragment. */
- fun handleCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?
- ): View? {
- binding = ForceNetworkTypeFragmentBinding.inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
+ binding.forceNetworkTypeToolbar.setNavigationOnClickListener {
+ (activity as ForceNetworkTypeActivity).finish()
+ }
- binding.forceNetworkTypeToolbar.setNavigationOnClickListener {
- (activity as ForceNetworkTypeActivity).finish()
- }
+ binding.apply {
+ this.lifecycleOwner = fragment
+ this.viewModel = forceNetworkTypeViewModel
+ }
- binding.apply {
- this.lifecycleOwner = fragment
- this.viewModel = forceNetworkTypeViewModel
- }
+ linearLayoutManager = LinearLayoutManager(activity.applicationContext)
- linearLayoutManager = LinearLayoutManager(activity.applicationContext)
+ bindingAdapter = createRecyclerViewAdapter()
+ binding.forceNetworkTypeRecyclerView.apply {
+ layoutManager = linearLayoutManager
+ adapter = bindingAdapter
+ }
- bindingAdapter = createRecyclerViewAdapter()
- binding.forceNetworkTypeRecyclerView.apply {
- layoutManager = linearLayoutManager
- adapter = bindingAdapter
+ return binding.root
}
- return binding.root
- }
+ private fun createRecyclerViewAdapter(): BindableAdapter =
+ singleTypeBuilderFactory
+ .create()
+ .registerViewDataBinderWithSameModelType(
+ inflateDataBinding = ForceNetworkTypeNetworkItemViewBinding::inflate,
+ setViewModel = this::bindNetworkItemView,
+ ).build()
- private fun createRecyclerViewAdapter(): BindableAdapter {
- return singleTypeBuilderFactory.create()
- .registerViewDataBinderWithSameModelType(
- inflateDataBinding = ForceNetworkTypeNetworkItemViewBinding::inflate,
- setViewModel = this::bindNetworkItemView
- )
- .build()
- }
-
- private fun bindNetworkItemView(
- binding: ForceNetworkTypeNetworkItemViewBinding,
- model: NetworkTypeItemViewModel
- ) {
- binding.viewModel = model
- if (networkConnectionUtil.isPresent) {
- binding.isNetworkSelected =
- networkConnectionUtil.get().getForcedConnectionStatus() == model.networkType
- binding.networkTypeLayout.setOnClickListener {
- networkConnectionUtil.get().setCurrentConnectionStatus(model.networkType)
- bindingAdapter.notifyDataSetChanged()
+ private fun bindNetworkItemView(
+ binding: ForceNetworkTypeNetworkItemViewBinding,
+ model: NetworkTypeItemViewModel,
+ ) {
+ binding.viewModel = model
+ if (networkConnectionUtil.isPresent) {
+ binding.isNetworkSelected =
+ networkConnectionUtil.get().getForcedConnectionStatus() == model.networkType
+ binding.networkTypeLayout.setOnClickListener {
+ networkConnectionUtil.get().setCurrentConnectionStatus(model.networkType)
+ bindingAdapter.notifyDataSetChanged()
+ }
}
}
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeViewModel.kt
index 638be858d52..b837b3c1bf6 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeViewModel.kt
@@ -13,36 +13,36 @@ import javax.inject.Inject
* [NetworkTypeItemViewModel] which in turn display the available network types.
*/
@FragmentScope
-class ForceNetworkTypeViewModel @Inject constructor(
- private val resourceHandler: AppLanguageResourceHandler
-) : ObservableViewModel() {
+class ForceNetworkTypeViewModel
+ @Inject
+ constructor(
+ private val resourceHandler: AppLanguageResourceHandler,
+ ) : ObservableViewModel() {
+ /**
+ * List of [NetworkTypeItemViewModel] used to populate recycler view of [ForceNetworkTypeFragment]
+ * to display the available network types.
+ */
+ val networkTypeList: List by lazy {
+ processNetworkTypeList()
+ }
- /**
- * List of [NetworkTypeItemViewModel] used to populate recycler view of [ForceNetworkTypeFragment]
- * to display the available network types.
- */
- val networkTypeList: List by lazy {
- processNetworkTypeList()
- }
-
- private fun processNetworkTypeList(): List {
- return listOf(
- NetworkTypeItemViewModel(
- NetworkConnectionDebugUtil.DebugConnectionStatus.DEFAULT,
- resourceHandler.getStringInLocale(R.string.force_network_type_default_network)
- ),
- NetworkTypeItemViewModel(
- NetworkConnectionUtil.ProdConnectionStatus.LOCAL,
- resourceHandler.getStringInLocale(R.string.force_network_type_wifi_network)
- ),
- NetworkTypeItemViewModel(
- NetworkConnectionUtil.ProdConnectionStatus.CELLULAR,
- resourceHandler.getStringInLocale(R.string.force_network_type_cellular_network)
- ),
- NetworkTypeItemViewModel(
- NetworkConnectionUtil.ProdConnectionStatus.NONE,
- resourceHandler.getStringInLocale(R.string.force_network_type_no_network)
+ private fun processNetworkTypeList(): List =
+ listOf(
+ NetworkTypeItemViewModel(
+ NetworkConnectionDebugUtil.DebugConnectionStatus.DEFAULT,
+ resourceHandler.getStringInLocale(R.string.force_network_type_default_network),
+ ),
+ NetworkTypeItemViewModel(
+ NetworkConnectionUtil.ProdConnectionStatus.LOCAL,
+ resourceHandler.getStringInLocale(R.string.force_network_type_wifi_network),
+ ),
+ NetworkTypeItemViewModel(
+ NetworkConnectionUtil.ProdConnectionStatus.CELLULAR,
+ resourceHandler.getStringInLocale(R.string.force_network_type_cellular_network),
+ ),
+ NetworkTypeItemViewModel(
+ NetworkConnectionUtil.ProdConnectionStatus.NONE,
+ resourceHandler.getStringInLocale(R.string.force_network_type_no_network),
+ ),
)
- )
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/NetworkTypeItemViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/NetworkTypeItemViewModel.kt
index 6d1b06f50c5..da7b44adcf5 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/NetworkTypeItemViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/NetworkTypeItemViewModel.kt
@@ -5,7 +5,9 @@ import org.oppia.android.util.networking.ConnectionStatus
import javax.inject.Inject
/** [ViewModel] for displaying a network type for the recycler view in [ForceNetworkTypeFragment]. */
-class NetworkTypeItemViewModel @Inject constructor(
- val networkType: ConnectionStatus,
- val networkTypeString: String
-) : ObservableViewModel()
+class NetworkTypeItemViewModel
+ @Inject
+ constructor(
+ val networkType: ConnectionStatus,
+ val networkTypeString: String,
+ ) : ObservableViewModel()
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/testing/ForceNetworkTypeTestActivity.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/testing/ForceNetworkTypeTestActivity.kt
index c2dcbe1546a..4a599add8ab 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/testing/ForceNetworkTypeTestActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/testing/ForceNetworkTypeTestActivity.kt
@@ -8,7 +8,6 @@ import org.oppia.android.app.devoptions.forcenetworktype.ForceNetworkTypeFragmen
/** Activity for testing [ForceNetworkTypeFragment]. */
class ForceNetworkTypeTestActivity : InjectableAutoLocalizedAppCompatActivity() {
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activityComponent as ActivityComponentImpl).inject(this)
@@ -17,15 +16,16 @@ class ForceNetworkTypeTestActivity : InjectableAutoLocalizedAppCompatActivity()
setContentView(R.layout.force_network_type_activity)
if (getForceNetworkTypeFragment() == null) {
val forceNetworkTypeFragment = ForceNetworkTypeFragment.newInstance()
- supportFragmentManager.beginTransaction().add(
- R.id.force_network_type_container,
- forceNetworkTypeFragment
- ).commitNow()
+ supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.force_network_type_container,
+ forceNetworkTypeFragment,
+ ).commitNow()
}
}
- private fun getForceNetworkTypeFragment(): ForceNetworkTypeFragment? {
- return supportFragmentManager
+ private fun getForceNetworkTypeFragment(): ForceNetworkTypeFragment? =
+ supportFragmentManager
.findFragmentById(R.id.force_network_type_container) as ForceNetworkTypeFragment?
- }
}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/ChapterSummaryViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/ChapterSummaryViewModel.kt
index 6b9dd7a112e..1ce7958173e 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/ChapterSummaryViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/ChapterSummaryViewModel.kt
@@ -11,7 +11,7 @@ class ChapterSummaryViewModel(
val nextStoryIndex: Int,
val storyId: String,
val topicId: String,
- translationController: TranslationController
+ translationController: TranslationController,
) : MarkChaptersCompletedItemViewModel() {
/** The summary of the chapter being displayed. */
val chapterSummary = ephemeralChapterSummary.chapterSummary
@@ -19,11 +19,11 @@ class ChapterSummaryViewModel(
/** The localized title of the chapter being displayed. */
val chapterTitle by lazy {
translationController.extractString(
- chapterSummary.title, ephemeralChapterSummary.writtenTranslationContext
+ chapterSummary.title,
+ ephemeralChapterSummary.writtenTranslationContext,
)
}
/** Returns whether the chapter represented by the current view model is completed. */
- fun checkIfChapterIsCompleted(): Boolean =
- chapterSummary.chapterPlayState == ChapterPlayState.COMPLETED
+ fun checkIfChapterIsCompleted(): Boolean = chapterSummary.chapterPlayState == ChapterPlayState.COMPLETED
}
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 cc1a7897150..f8915892681 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
@@ -18,7 +18,6 @@ import javax.inject.Inject
/** Activity for Mark Chapters Completed. */
class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity() {
-
@Inject
lateinit var markChaptersCompletedActivityPresenter: MarkChaptersCompletedActivityPresenter
@@ -29,10 +28,11 @@ class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity()
super.onCreate(savedInstanceState)
(activityComponent as ActivityComponentImpl).inject(this)
- val args = intent.getProtoExtra(
- MARK_CHAPTERS_COMPLETED_ACTIVITY_PARAMS,
- MarkChaptersCompletedActivityParams.getDefaultInstance()
- )
+ val args =
+ intent.getProtoExtra(
+ MARK_CHAPTERS_COMPLETED_ACTIVITY_PARAMS,
+ MarkChaptersCompletedActivityParams.getDefaultInstance(),
+ )
val internalProfileId = args?.internalProfileId ?: -1
val showConfirmationNotice = args?.showConfirmationNotice ?: false
@@ -45,7 +45,7 @@ class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity()
override fun handleOnBackPressed() {
finish()
}
- }
+ },
)
}
@@ -65,15 +65,17 @@ class MarkChaptersCompletedActivity : InjectableAutoLocalizedAppCompatActivity()
fun createMarkChaptersCompletedIntent(
context: Context,
internalProfileId: Int,
- showConfirmationNotice: Boolean
+ showConfirmationNotice: Boolean,
): Intent {
val intent = Intent(context, MarkChaptersCompletedActivity::class.java)
- val args = MarkChaptersCompletedActivityParams.newBuilder().apply {
- this.internalProfileId = internalProfileId
- this.showConfirmationNotice = showConfirmationNotice
- }
- .build()
+ 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)
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt
index 6605456b86e..fcee13cb8e4 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt
@@ -7,27 +7,32 @@ import javax.inject.Inject
/** The presenter for [MarkChaptersCompletedActivity]. */
@ActivityScope
-class MarkChaptersCompletedActivityPresenter @Inject constructor(
- private val activity: AppCompatActivity
-) {
+class MarkChaptersCompletedActivityPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ ) {
+ fun handleOnCreate(
+ internalProfileId: Int,
+ showConfirmationNotice: Boolean,
+ ) {
+ activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp)
+ activity.setContentView(R.layout.mark_chapters_completed_activity)
- fun handleOnCreate(internalProfileId: Int, showConfirmationNotice: Boolean) {
- activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
- activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp)
- activity.setContentView(R.layout.mark_chapters_completed_activity)
-
- if (getMarkChaptersCompletedFragment() == null) {
- val markChaptersCompletedFragment =
- MarkChaptersCompletedFragment.newInstance(internalProfileId, showConfirmationNotice)
- activity.supportFragmentManager.beginTransaction().add(
- R.id.mark_chapters_completed_container,
- markChaptersCompletedFragment
- ).commitNow()
+ if (getMarkChaptersCompletedFragment() == null) {
+ val markChaptersCompletedFragment =
+ MarkChaptersCompletedFragment.newInstance(internalProfileId, showConfirmationNotice)
+ activity.supportFragmentManager
+ .beginTransaction()
+ .add(
+ R.id.mark_chapters_completed_container,
+ markChaptersCompletedFragment,
+ ).commitNow()
+ }
}
- }
- private fun getMarkChaptersCompletedFragment(): MarkChaptersCompletedFragment? {
- return activity.supportFragmentManager
- .findFragmentById(R.id.mark_chapters_completed_container) as MarkChaptersCompletedFragment?
+ private fun getMarkChaptersCompletedFragment(): MarkChaptersCompletedFragment? =
+ activity.supportFragmentManager
+ .findFragmentById(R.id.mark_chapters_completed_container) as MarkChaptersCompletedFragment?
}
-}
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 fb34bf79cea..99e8762feca 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
@@ -27,17 +27,20 @@ class MarkChaptersCompletedFragment : InjectableFragment() {
/** Returns a new [MarkChaptersCompletedFragment]. */
fun newInstance(
internalProfileId: Int,
- showConfirmationNotice: Boolean
+ showConfirmationNotice: Boolean,
): MarkChaptersCompletedFragment {
- val args = MarkChaptersCompletedFragmentArguments.newBuilder().apply {
- this.internalProfileId = internalProfileId
- this.showConfirmationNotice = showConfirmationNotice
- }
- .build()
+ 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)
- }
+ arguments =
+ Bundle().apply {
+ putProto(MARK_CHAPTERS_COMPLETED_FRAGMENT_ARGUMENTS_KEY, args)
+ }
}
}
}
@@ -50,28 +53,30 @@ class MarkChaptersCompletedFragment : InjectableFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
+ savedInstanceState: Bundle?,
): View? {
val arguments =
checkNotNull(arguments) { "Expected arguments to be passed to MarkChaptersCompletedFragment" }
- val args = arguments.getProto(
- MARK_CHAPTERS_COMPLETED_FRAGMENT_ARGUMENTS_KEY,
- MarkChaptersCompletedFragmentArguments.getDefaultInstance()
- )
+ 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()
- )
+ val savedStateArgs =
+ savedInstanceState?.getProto(
+ MARK_CHAPTERS_COMPLETED_FRAGMENT_STATE_KEY,
+ MarkChaptersCompletedFragmentStateBundle.getDefaultInstance(),
+ )
return markChaptersCompletedFragmentPresenter.handleCreateView(
inflater,
container,
internalProfileId,
showConfirmationNotice,
savedStateArgs?.explorationIdsList ?: listOf(),
- savedStateArgs?.explorationTitlesList ?: listOf()
+ savedStateArgs?.explorationTitlesList ?: listOf(),
)
}
@@ -79,15 +84,17 @@ class MarkChaptersCompletedFragment : InjectableFragment() {
super.onSaveInstanceState(outState)
outState.apply {
- val args = MarkChaptersCompletedFragmentStateBundle.newBuilder().apply {
- addAllExplorationIds(
- markChaptersCompletedFragmentPresenter.serializableSelectedExplorationIds
- )
- addAllExplorationTitles(
- markChaptersCompletedFragmentPresenter.serializableSelectedExplorationTitles
- )
- }
- .build()
+ 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/MarkChaptersCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt
index 98a5d8f35fc..f8cf71dedbc 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt
@@ -21,247 +21,267 @@ import javax.inject.Inject
/** The presenter for [MarkChaptersCompletedFragment]. */
@FragmentScope
-class MarkChaptersCompletedFragmentPresenter @Inject constructor(
- private val activity: AppCompatActivity,
- private val fragment: Fragment,
- private val viewModel: MarkChaptersCompletedViewModel,
- private val modifyLessonProgressController: ModifyLessonProgressController,
- private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory,
- private val resourceHandler: AppLanguageResourceHandler
-) {
- private lateinit var binding: MarkChaptersCompletedFragmentBinding
- private lateinit var linearLayoutManager: LinearLayoutManager
- private lateinit var bindingAdapter: BindableAdapter
- private lateinit var profileId: ProfileId
- private lateinit var alertDialog: AlertDialog
- private val selectedExplorationIds = mutableListOf()
- private val selectedExplorationTitles = mutableListOf()
+class MarkChaptersCompletedFragmentPresenter
+ @Inject
+ constructor(
+ private val activity: AppCompatActivity,
+ private val fragment: Fragment,
+ private val viewModel: MarkChaptersCompletedViewModel,
+ private val modifyLessonProgressController: ModifyLessonProgressController,
+ private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory,
+ private val resourceHandler: AppLanguageResourceHandler,
+ ) {
+ private lateinit var binding: MarkChaptersCompletedFragmentBinding
+ private lateinit var linearLayoutManager: LinearLayoutManager
+ private lateinit var bindingAdapter: BindableAdapter
+ private lateinit var profileId: ProfileId
+ private lateinit var alertDialog: AlertDialog
+ private val selectedExplorationIds = mutableListOf()
+ private val selectedExplorationTitles = mutableListOf()
- val serializableSelectedExplorationIds: ArrayList
- get() = ArrayList(selectedExplorationIds)
- val serializableSelectedExplorationTitles: ArrayList
- get() = ArrayList(selectedExplorationTitles)
+ val serializableSelectedExplorationIds: ArrayList
+ get() = ArrayList(selectedExplorationIds)
+ val serializableSelectedExplorationTitles: ArrayList
+ get() = ArrayList(selectedExplorationTitles)
- fun handleCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- internalProfileId: Int,
- showConfirmationNotice: Boolean,
- selectedExplorationIds: List,
- selectedExplorationTitles: List
- ): View? {
- binding = MarkChaptersCompletedFragmentBinding.inflate(
- inflater,
- container,
- /* attachToRoot= */ false
- )
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ internalProfileId: Int,
+ showConfirmationNotice: Boolean,
+ selectedExplorationIds: List,
+ selectedExplorationTitles: List,
+ ): View? {
+ binding =
+ MarkChaptersCompletedFragmentBinding.inflate(
+ inflater,
+ container,
+ // attachToRoot=
+ false,
+ )
- binding.markChaptersCompletedToolbar.setNavigationOnClickListener {
- (activity as MarkChaptersCompletedActivity).finish()
- }
+ binding.markChaptersCompletedToolbar.setNavigationOnClickListener {
+ (activity as MarkChaptersCompletedActivity).finish()
+ }
- binding.apply {
- this.lifecycleOwner = fragment
- this.viewModel = this@MarkChaptersCompletedFragmentPresenter.viewModel
- }
+ binding.apply {
+ this.lifecycleOwner = fragment
+ this.viewModel = this@MarkChaptersCompletedFragmentPresenter.viewModel
+ }
- this.selectedExplorationIds += selectedExplorationIds
- this.selectedExplorationTitles += selectedExplorationTitles
+ this.selectedExplorationIds += selectedExplorationIds
+ this.selectedExplorationTitles += selectedExplorationTitles
- profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build()
- viewModel.setProfileId(profileId)
+ profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build()
+ viewModel.setProfileId(profileId)
- linearLayoutManager = LinearLayoutManager(activity.applicationContext)
+ linearLayoutManager = LinearLayoutManager(activity.applicationContext)
- bindingAdapter = createRecyclerViewAdapter()
- binding.markChaptersCompletedRecyclerView.apply {
- layoutManager = linearLayoutManager
- adapter = bindingAdapter
- }
+ bindingAdapter = createRecyclerViewAdapter()
+ binding.markChaptersCompletedRecyclerView.apply {
+ layoutManager = linearLayoutManager
+ adapter = bindingAdapter
+ }
- binding.markChaptersCompletedAllCheckBoxContainer.setOnClickListener {
- if (binding.isAllChecked == null || binding.isAllChecked == false) {
- binding.isAllChecked = true
- viewModel.getItemList().forEach { viewModel ->
- if (viewModel is ChapterSummaryViewModel) {
- if (!viewModel.checkIfChapterIsCompleted())
- chapterSelected(
- viewModel.chapterIndex,
- viewModel.nextStoryIndex,
- viewModel.chapterSummary.explorationId,
- viewModel.chapterTitle
- )
+ binding.markChaptersCompletedAllCheckBoxContainer.setOnClickListener {
+ if (binding.isAllChecked == null || binding.isAllChecked == false) {
+ binding.isAllChecked = true
+ viewModel.getItemList().forEach { viewModel ->
+ if (viewModel is ChapterSummaryViewModel) {
+ if (!viewModel.checkIfChapterIsCompleted()) {
+ chapterSelected(
+ viewModel.chapterIndex,
+ viewModel.nextStoryIndex,
+ viewModel.chapterSummary.explorationId,
+ viewModel.chapterTitle,
+ )
+ }
+ }
}
- }
- } else if (binding.isAllChecked == true) {
- binding.isAllChecked = false
- viewModel.getItemList().forEach { viewModel ->
- if (viewModel is ChapterSummaryViewModel) {
- if (!viewModel.checkIfChapterIsCompleted()) {
- chapterUnselected(viewModel.chapterIndex, viewModel.nextStoryIndex)
+ } else if (binding.isAllChecked == true) {
+ binding.isAllChecked = false
+ viewModel.getItemList().forEach { viewModel ->
+ if (viewModel is ChapterSummaryViewModel) {
+ if (!viewModel.checkIfChapterIsCompleted()) {
+ chapterUnselected(viewModel.chapterIndex, viewModel.nextStoryIndex)
+ }
}
}
}
}
- }
- binding.markChaptersCompletedMarkCompletedTextView.setOnClickListener {
- if (showConfirmationNotice && this.selectedExplorationIds.isNotEmpty()) {
- showConfirmationDialog()
- } else markChaptersAsCompleted()
+ binding.markChaptersCompletedMarkCompletedTextView.setOnClickListener {
+ if (showConfirmationNotice && this.selectedExplorationIds.isNotEmpty()) {
+ showConfirmationDialog()
+ } else {
+ markChaptersAsCompleted()
+ }
+ }
+
+ return binding.root
}
- return binding.root
- }
+ private fun createRecyclerViewAdapter(): BindableAdapter =
+ multiTypeBuilderFactory
+ .create<
+ MarkChaptersCompletedItemViewModel,
+ ViewType,
+ > { viewModel ->
+ when (viewModel) {
+ is StorySummaryViewModel -> ViewType.VIEW_TYPE_STORY
+ is ChapterSummaryViewModel -> ViewType.VIEW_TYPE_CHAPTER
+ else -> throw IllegalArgumentException("Encountered unexpected view model: $viewModel")
+ }
+ }.registerViewDataBinder(
+ viewType = ViewType.VIEW_TYPE_STORY,
+ inflateDataBinding = MarkChaptersCompletedStorySummaryViewBinding::inflate,
+ setViewModel = MarkChaptersCompletedStorySummaryViewBinding::setViewModel,
+ transformViewModel = { it as StorySummaryViewModel },
+ ).registerViewDataBinder(
+ viewType = ViewType.VIEW_TYPE_CHAPTER,
+ inflateDataBinding = MarkChaptersCompletedChapterSummaryViewBinding::inflate,
+ setViewModel = this::bindChapterSummaryView,
+ transformViewModel = { it as ChapterSummaryViewModel },
+ ).build()
- private fun createRecyclerViewAdapter(): BindableAdapter {
- return multiTypeBuilderFactory.create { viewModel ->
- when (viewModel) {
- is StorySummaryViewModel -> ViewType.VIEW_TYPE_STORY
- is ChapterSummaryViewModel -> ViewType.VIEW_TYPE_CHAPTER
- else -> throw IllegalArgumentException("Encountered unexpected view model: $viewModel")
+ private fun bindChapterSummaryView(
+ binding: MarkChaptersCompletedChapterSummaryViewBinding,
+ model: ChapterSummaryViewModel,
+ ) {
+ binding.viewModel = model
+ val notCompletedChapterCount =
+ viewModel.getItemList().count {
+ it is ChapterSummaryViewModel && !it.checkIfChapterIsCompleted()
+ }
+ if (notCompletedChapterCount == 0) {
+ this.binding.isAllChecked = true
}
- }
- .registerViewDataBinder(
- viewType = ViewType.VIEW_TYPE_STORY,
- inflateDataBinding = MarkChaptersCompletedStorySummaryViewBinding::inflate,
- setViewModel = MarkChaptersCompletedStorySummaryViewBinding::setViewModel,
- transformViewModel = { it as StorySummaryViewModel }
- )
- .registerViewDataBinder(
- viewType = ViewType.VIEW_TYPE_CHAPTER,
- inflateDataBinding = MarkChaptersCompletedChapterSummaryViewBinding::inflate,
- setViewModel = this::bindChapterSummaryView,
- transformViewModel = { it as ChapterSummaryViewModel }
- )
- .build()
- }
+ if (model.checkIfChapterIsCompleted()) {
+ binding.isChapterChecked = true
+ binding.isChapterCheckboxEnabled = false
+ } else {
+ binding.isChapterChecked = model.chapterSummary.explorationId in selectedExplorationIds
- private fun bindChapterSummaryView(
- binding: MarkChaptersCompletedChapterSummaryViewBinding,
- model: ChapterSummaryViewModel
- ) {
- binding.viewModel = model
- val notCompletedChapterCount = viewModel.getItemList().count {
- it is ChapterSummaryViewModel && !it.checkIfChapterIsCompleted()
- }
- if (notCompletedChapterCount == 0) {
- this.binding.isAllChecked = true
- }
- if (model.checkIfChapterIsCompleted()) {
- binding.isChapterChecked = true
- binding.isChapterCheckboxEnabled = false
- } else {
- binding.isChapterChecked = model.chapterSummary.explorationId in selectedExplorationIds
+ binding.isChapterCheckboxEnabled = !model.chapterSummary.hasMissingPrerequisiteChapter() ||
+ model.chapterSummary.hasMissingPrerequisiteChapter() &&
+ model.chapterSummary.missingPrerequisiteChapter.explorationId in selectedExplorationIds
- binding.isChapterCheckboxEnabled = !model.chapterSummary.hasMissingPrerequisiteChapter() ||
- model.chapterSummary.hasMissingPrerequisiteChapter() &&
- model.chapterSummary.missingPrerequisiteChapter.explorationId in selectedExplorationIds
-
- binding.markChaptersCompletedChapterCheckBox.setOnCheckedChangeListener { _, isChecked ->
- if (isChecked) {
- chapterSelected(
- model.chapterIndex,
- model.nextStoryIndex,
- model.chapterSummary.explorationId,
- model.chapterTitle
- )
- } else {
- chapterUnselected(model.chapterIndex, model.nextStoryIndex)
+ binding.markChaptersCompletedChapterCheckBox.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked) {
+ chapterSelected(
+ model.chapterIndex,
+ model.nextStoryIndex,
+ model.chapterSummary.explorationId,
+ model.chapterTitle,
+ )
+ } else {
+ chapterUnselected(model.chapterIndex, model.nextStoryIndex)
+ }
}
}
}
- }
- private fun chapterSelected(chapterIdx: Int, nextStoryIdx: Int, expId: String, expTitle: String) {
- if (expId !in selectedExplorationIds) {
- selectedExplorationIds += expId
- selectedExplorationTitles += expTitle
- }
- if (selectedExplorationIds.size == viewModel.getItemList().countIncompleteChapters()) {
- binding.isAllChecked = true
- }
- if (!binding.markChaptersCompletedRecyclerView.isComputingLayout &&
- binding.markChaptersCompletedRecyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE
+ private fun chapterSelected(
+ chapterIdx: Int,
+ nextStoryIdx: Int,
+ expId: String,
+ expTitle: String,
) {
- bindingAdapter.notifyItemChanged(chapterIdx)
- if (chapterIdx + 1 < nextStoryIdx)
- bindingAdapter.notifyItemChanged(chapterIdx + 1)
+ if (expId !in selectedExplorationIds) {
+ selectedExplorationIds += expId
+ selectedExplorationTitles += expTitle
+ }
+ if (selectedExplorationIds.size == viewModel.getItemList().countIncompleteChapters()) {
+ binding.isAllChecked = true
+ }
+ if (!binding.markChaptersCompletedRecyclerView.isComputingLayout &&
+ binding.markChaptersCompletedRecyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE
+ ) {
+ bindingAdapter.notifyItemChanged(chapterIdx)
+ if (chapterIdx + 1 < nextStoryIdx) {
+ bindingAdapter.notifyItemChanged(chapterIdx + 1)
+ }
+ }
}
- }
- private fun chapterUnselected(chapterIndex: Int, nextStoryIndex: Int) {
- for (index in chapterIndex until nextStoryIndex) {
- val explorationId =
- (viewModel.getItemList()[index] as ChapterSummaryViewModel).chapterSummary.explorationId
- val expIndex = selectedExplorationIds.indexOf(explorationId)
- if (expIndex != -1) {
- selectedExplorationIds.removeAt(expIndex)
- selectedExplorationTitles.removeAt(expIndex)
+ private fun chapterUnselected(
+ chapterIndex: Int,
+ nextStoryIndex: Int,
+ ) {
+ for (index in chapterIndex until nextStoryIndex) {
+ val explorationId =
+ (viewModel.getItemList()[index] as ChapterSummaryViewModel).chapterSummary.explorationId
+ val expIndex = selectedExplorationIds.indexOf(explorationId)
+ if (expIndex != -1) {
+ selectedExplorationIds.removeAt(expIndex)
+ selectedExplorationTitles.removeAt(expIndex)
+ }
+ }
+ if (selectedExplorationIds.size != viewModel.getItemList().countIncompleteChapters()) {
+ binding.isAllChecked = false
+ }
+ if (!binding.markChaptersCompletedRecyclerView.isComputingLayout &&
+ binding.markChaptersCompletedRecyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE
+ ) {
+ bindingAdapter.notifyItemRangeChanged(
+ chapterIndex,
+ // itemCount =
+ nextStoryIndex - chapterIndex,
+ )
}
}
- if (selectedExplorationIds.size != viewModel.getItemList().countIncompleteChapters()) {
- binding.isAllChecked = false
- }
- if (!binding.markChaptersCompletedRecyclerView.isComputingLayout &&
- binding.markChaptersCompletedRecyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE
- ) {
- bindingAdapter.notifyItemRangeChanged(
- chapterIndex,
- /*itemCount = */ nextStoryIndex - chapterIndex
- )
+
+ private fun showConfirmationDialog() {
+ alertDialog =
+ AlertDialog
+ .Builder(activity, R.style.OppiaAlertDialogTheme)
+ .apply {
+ setTitle(R.string.mark_chapters_completed_confirm_setting_dialog_title)
+ setMessage(
+ resourceHandler.getStringInLocaleWithWrapping(
+ R.string.mark_chapters_completed_confirm_setting_dialog_message,
+ selectedExplorationTitles.joinToReadableString(),
+ ),
+ )
+ setNegativeButton(
+ R.string.mark_chapters_completed_confirm_setting_dialog_cancel_button_text,
+ ) { dialog, _ -> dialog.dismiss() }
+ setPositiveButton(
+ R.string.mark_chapters_completed_confirm_setting_dialog_confirm_button_text,
+ ) { dialog, _ ->
+ dialog.dismiss()
+ markChaptersAsCompleted()
+ }
+ }.create()
+ .also {
+ it.setCanceledOnTouchOutside(true)
+ it.show()
+ }
}
- }
- private fun showConfirmationDialog() {
- alertDialog = AlertDialog.Builder(activity, R.style.OppiaAlertDialogTheme).apply {
- setTitle(R.string.mark_chapters_completed_confirm_setting_dialog_title)
- setMessage(
- resourceHandler.getStringInLocaleWithWrapping(
- R.string.mark_chapters_completed_confirm_setting_dialog_message,
- selectedExplorationTitles.joinToReadableString()
- )
+ private fun markChaptersAsCompleted() {
+ modifyLessonProgressController.markMultipleChaptersCompleted(
+ profileId = profileId,
+ chapterMap = viewModel.getChapterMap().filterKeys { it in selectedExplorationIds },
)
- setNegativeButton(
- R.string.mark_chapters_completed_confirm_setting_dialog_cancel_button_text
- ) { dialog, _ -> dialog.dismiss() }
- setPositiveButton(
- R.string.mark_chapters_completed_confirm_setting_dialog_confirm_button_text
- ) { dialog, _ ->
- dialog.dismiss()
- markChaptersAsCompleted()
- }
- }.create().also {
- it.setCanceledOnTouchOutside(true)
- it.show()
+ activity.finish()
}
- }
- private fun markChaptersAsCompleted() {
- modifyLessonProgressController.markMultipleChaptersCompleted(
- profileId = profileId,
- chapterMap = viewModel.getChapterMap().filterKeys { it in selectedExplorationIds }
- )
- activity.finish()
- }
+ private enum class ViewType {
+ VIEW_TYPE_STORY,
+ VIEW_TYPE_CHAPTER,
+ }
- private enum class ViewType {
- VIEW_TYPE_STORY,
- VIEW_TYPE_CHAPTER
- }
+ private companion object {
+ private fun List.joinToReadableString(): String =
+ when (size) {
+ 0 -> ""
+ 1 -> single()
+ 2 -> "${this[0]} and ${this[1]}"
+ else -> "${asSequence().take(size - 1).joinToString()}, and ${last()}"
+ }
- private companion object {
- private fun List.joinToReadableString(): String {
- return when (size) {
- 0 -> ""
- 1 -> single()
- 2 -> "${this[0]} and ${this[1]}"
- else -> "${asSequence().take(size - 1).joinToString()}, and ${last()}"
- }
+ private fun List.countIncompleteChapters() =
+ filterIsInstance().count { !it.checkIfChapterIsCompleted() }
}
-
- private fun List.countIncompleteChapters() =
- filterIsInstance().count { !it.checkIfChapterIsCompleted() }
}
-}
diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedViewModel.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedViewModel.kt
index 3c7fe863f5e..22ab8189627 100644
--- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedViewModel.kt
@@ -18,97 +18,97 @@ import javax.inject.Inject
* [StorySummaryViewModel] which in turn display the story.
*/
@FragmentScope
-class MarkChaptersCompletedViewModel @Inject constructor(
- private val oppiaLogger: OppiaLogger,
- private val modifyLessonProgressController: ModifyLessonProgressController,
- private val translationController: TranslationController
-) : ObservableViewModel() {
+class MarkChaptersCompletedViewModel
+ @Inject
+ constructor(
+ private val oppiaLogger: OppiaLogger,
+ private val modifyLessonProgressController: ModifyLessonProgressController,
+ private val translationController: TranslationController,
+ ) : ObservableViewModel() {
+ private lateinit var profileId: ProfileId
- private lateinit var profileId: ProfileId
+ private val itemList = mutableListOf()
- private val itemList = mutableListOf()
-
- /**
- * List of [MarkChaptersCompletedItemViewModel] used to populate recyclerview of
- * [MarkChaptersCompletedFragment] to display stories and chapters.
- */
- val storySummaryLiveData: LiveData> by lazy {
- Transformations.map(storyMapLiveData, ::processStoryMap)
- }
-
- private val storyMapLiveData: LiveData