From b278b5e607544d2bdb4ce8645d61647ff5164e46 Mon Sep 17 00:00:00 2001
From: Adhiambo Peres <59600948+adhiamboperes@users.noreply.github.com>
Date: Sun, 30 Jun 2024 20:17:58 +0300
Subject: [PATCH] Fix Part of #4938: Introduce Onboarding profile type screen
(#5378)
## Explanation
Fixes Part of #4938: Introduce Onboarding profile type screen
Adds the profile type selection screen.
The new "I am a student" flow is not implemented, and tests have been
set up to fail when the flow has been implemented.
The "I am a parent/teacher" flow has been set up to route to the
existing profile screen.
## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If
this PR fixes part of an issue, prefix the title with "Fix part of
#bugnum: ...".)
- [x] Any changes to
[scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets)
files have their rationale included in the PR explanation.
- [x] The PR follows the [style
guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android
Studio
([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and
is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers
([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).
All tests pass on Espresso
![Screenshot 2024-05-23 at 18 06
46](https://github.com/oppia/oppia-android/assets/59600948/6d2414c9-6ddf-4340-a408-52a7a5c8acdc)
## For UI-specific PRs only
||||
| --- | --- | --- |
|| Portrait | Landscape |
| Mobile Light Mode
|![Screenshot_1719762068](https://github.com/oppia/oppia-android/assets/59600948/6adbff2d-6cc7-4884-bd31-8ddbc4f0d359)|![Screenshot_1719762228](https://github.com/oppia/oppia-android/assets/59600948/e9dcb7bc-9d1b-44e7-86ea-2f4d669fe5c6)|
| Mobile Dark Mode
|![Screenshot_1719761957](https://github.com/oppia/oppia-android/assets/59600948/c1248dda-d377-4586-9613-803517fd7d24)|![Screenshot_1719762205](https://github.com/oppia/oppia-android/assets/59600948/9cf78a42-8d7d-442b-9b4e-8db02d3c5bdf)|
| Tablet Light Mode
|![Screenshot_1719762364](https://github.com/oppia/oppia-android/assets/59600948/ee8d2de4-90e6-478c-b9e2-cfbba5b7b012)|![Screenshot_1719762334](https://github.com/oppia/oppia-android/assets/59600948/6e5d6f81-b7e1-4ac4-ab99-05832bb7a7a8)|
| Tablet Dark Mode
|![Screenshot_1719762388](https://github.com/oppia/oppia-android/assets/59600948/1e74ded0-4110-4995-a8f8-68feba04e769)|![Screenshot_1719762309](https://github.com/oppia/oppia-android/assets/59600948/8edef377-64aa-4828-aba4-42804a7fb029)|
---
app/src/main/AndroidManifest.xml | 4 +
.../app/activity/ActivityComponentImpl.kt | 2 +
.../app/fragment/FragmentComponentImpl.kt | 2 +
.../onboarding/OnboardingFragmentPresenter.kt | 8 +
.../OnboardingProfileTypeActivity.kt | 32 ++
.../OnboardingProfileTypeActivityPresenter.kt | 41 ++
.../OnboardingProfileTypeFragment.kt | 29 +
.../OnboardingProfileTypeFragmentPresenter.kt | 41 ++
app/src/main/res/drawable/learner_otter.xml | 542 ++++++++++++++++++
...nboarding_back_button_white_background.xml | 5 +
.../res/drawable/parent_teacher_otter.xml | 414 +++++++++++++
.../onboarding_profile_type_fragment.xml | 124 ++++
.../onboarding_profile_type_fragment.xml | 138 +++++
.../onboarding_profile_type_fragment.xml | 138 +++++
.../onboarding_profile_type_activity.xml | 10 +
.../onboarding_profile_type_fragment.xml | 137 +++++
.../main/res/values-night/color_palette.xml | 1 +
app/src/main/res/values/color_defs.xml | 5 +-
app/src/main/res/values/color_palette.xml | 5 +-
app/src/main/res/values/component_colors.xml | 4 +
app/src/main/res/values/dimens.xml | 2 +
app/src/main/res/values/strings.xml | 11 +
app/src/main/res/values/styles.xml | 43 ++
.../OnboardingProfileTypeActivityTest.kt | 215 +++++++
.../OnboardingProfileTypeFragmentTest.kt | 398 +++++++++++++
model/src/main/proto/screens.proto | 3 +
.../file_content_validation_checks.textproto | 2 +
scripts/assets/test_file_exemptions.textproto | 8 +
.../util/logging/EventBundleCreator.kt | 1 +
29 files changed, 2363 insertions(+), 2 deletions(-)
create mode 100644 app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivity.kt
create mode 100644 app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivityPresenter.kt
create mode 100644 app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragment.kt
create mode 100644 app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentPresenter.kt
create mode 100644 app/src/main/res/drawable/learner_otter.xml
create mode 100644 app/src/main/res/drawable/onboarding_back_button_white_background.xml
create mode 100644 app/src/main/res/drawable/parent_teacher_otter.xml
create mode 100644 app/src/main/res/layout-land/onboarding_profile_type_fragment.xml
create mode 100644 app/src/main/res/layout-sw600dp-land/onboarding_profile_type_fragment.xml
create mode 100644 app/src/main/res/layout-sw600dp-port/onboarding_profile_type_fragment.xml
create mode 100644 app/src/main/res/layout/onboarding_profile_type_activity.xml
create mode 100644 app/src/main/res/layout/onboarding_profile_type_fragment.xml
create mode 100644 app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivityTest.kt
create mode 100644 app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentTest.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 235bb4a479f..b25b8ccfc86 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -336,6 +336,10 @@
android:name=".app.classroom.ClassroomListActivity"
android:label="@string/classroom_list_activity_title"
android:theme="@style/OppiaThemeWithoutActionBar" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/onboarding_back_button_white_background.xml b/app/src/main/res/drawable/onboarding_back_button_white_background.xml
new file mode 100644
index 00000000000..47b0bf7daa1
--- /dev/null
+++ b/app/src/main/res/drawable/onboarding_back_button_white_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/parent_teacher_otter.xml b/app/src/main/res/drawable/parent_teacher_otter.xml
new file mode 100644
index 00000000000..abeec4882c4
--- /dev/null
+++ b/app/src/main/res/drawable/parent_teacher_otter.xml
@@ -0,0 +1,414 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-land/onboarding_profile_type_fragment.xml b/app/src/main/res/layout-land/onboarding_profile_type_fragment.xml
new file mode 100644
index 00000000000..40df0c99b39
--- /dev/null
+++ b/app/src/main/res/layout-land/onboarding_profile_type_fragment.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-sw600dp-land/onboarding_profile_type_fragment.xml b/app/src/main/res/layout-sw600dp-land/onboarding_profile_type_fragment.xml
new file mode 100644
index 00000000000..7dd593a18db
--- /dev/null
+++ b/app/src/main/res/layout-sw600dp-land/onboarding_profile_type_fragment.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-sw600dp-port/onboarding_profile_type_fragment.xml b/app/src/main/res/layout-sw600dp-port/onboarding_profile_type_fragment.xml
new file mode 100644
index 00000000000..932631d6119
--- /dev/null
+++ b/app/src/main/res/layout-sw600dp-port/onboarding_profile_type_fragment.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/onboarding_profile_type_activity.xml b/app/src/main/res/layout/onboarding_profile_type_activity.xml
new file mode 100644
index 00000000000..a7f78951061
--- /dev/null
+++ b/app/src/main/res/layout/onboarding_profile_type_activity.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/onboarding_profile_type_fragment.xml b/app/src/main/res/layout/onboarding_profile_type_fragment.xml
new file mode 100644
index 00000000000..d14485ee6d8
--- /dev/null
+++ b/app/src/main/res/layout/onboarding_profile_type_fragment.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-night/color_palette.xml b/app/src/main/res/values-night/color_palette.xml
index 7c9347d590a..901d95b33df 100644
--- a/app/src/main/res/values-night/color_palette.xml
+++ b/app/src/main/res/values-night/color_palette.xml
@@ -231,6 +231,7 @@
@color/color_def_dark_green
@color/color_def_accessible_grey
+ @color/color_def_dark_jade
@color/color_def_greenish_black
@color/color_def_white
diff --git a/app/src/main/res/values/color_defs.xml b/app/src/main/res/values/color_defs.xml
index d6144b1d5ed..af6a0ba28cd 100644
--- a/app/src/main/res/values/color_defs.xml
+++ b/app/src/main/res/values/color_defs.xml
@@ -141,9 +141,12 @@
#F6F6F6
#BDCCCC
- #E8E8E8
+ #E8E8E8
#E2F5F4
#25000000
#EDF6F5
#172B28
+ #8EBBB6
+ #64817E
+ #F8BF74
diff --git a/app/src/main/res/values/color_palette.xml b/app/src/main/res/values/color_palette.xml
index 649cc14c2d9..abeed016fca 100644
--- a/app/src/main/res/values/color_palette.xml
+++ b/app/src/main/res/values/color_palette.xml
@@ -261,7 +261,7 @@
@color/color_def_pale_green
@color/color_def_light_blue
@color/color_def_accessible_grey
- @color/color_def_survey_disabled_button_grey
+ @color/color_def_disabled_button_grey
@color/color_def_chooser_grey
@color/color_def_persian_green
@color/color_def_grey
@@ -271,6 +271,9 @@
@color/color_def_oppia_green
@color/color_def_accessible_grey
+ @color/color_def_jade
+ @color/color_def_oppia_brown
+ @color/color_def_light_orange
@color/color_def_greenish_white
@color/color_def_green
diff --git a/app/src/main/res/values/component_colors.xml b/app/src/main/res/values/component_colors.xml
index fd03df0ed14..94dd01bd602 100644
--- a/app/src/main/res/values/component_colors.xml
+++ b/app/src/main/res/values/component_colors.xml
@@ -304,9 +304,13 @@
@color/color_palette_button_text_color
@color/color_palette_edit_text_unselected_color
@color/color_palette_button_shadow_color
+
@color/color_palette_white_text_color
@color/color_palette_onboarding_primary_color
@color/color_palette_onboarding_primary_text_color
+ @color/color_palette_onboarding_profile_type_background_color
+ @color/color_palette_learner_profile_type_background_color
+ @color/color_palette_supervisor_profile_type_background_color
@color/color_palette_classroom_card_color
@color/color_palette_classroom_shared_text_color
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 2d53f641df9..1f521f91bfe 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -803,7 +803,9 @@
8dp
4dp
2dp
+
4dp
+ 8dp
12sp
14sp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 409c502e500..307e20dcc92 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -646,6 +646,17 @@
You can change your language selection anytime in the app settings
Let\'s go!
+
+ Select Profile Type
+ Tell us more about you!
+ I\'m a student and I want to learn new things!
+ I\'m the parent, teacher, or guardian of a student.
+ STEP 2 OF 5
+
Cute otter wearing glasses.
+ Cute otter with books.
+ Mama and baby otter.
+ Back
+ Continue
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 4ca3883d1fe..c9bf5fae5eb 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -688,4 +688,47 @@
- @dimen/onboarding_shared_text_size_small
- sans-serif
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivityTest.kt
new file mode 100644
index 00000000000..82c3d74ae11
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivityTest.kt
@@ -0,0 +1,215 @@
+package org.oppia.android.app.onboarding
+
+import android.app.Application
+import android.content.Context
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.intent.Intents
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.Component
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.R
+import org.oppia.android.app.activity.ActivityComponent
+import org.oppia.android.app.activity.ActivityComponentFactory
+import org.oppia.android.app.activity.route.ActivityRouterModule
+import org.oppia.android.app.application.ApplicationComponent
+import org.oppia.android.app.application.ApplicationInjector
+import org.oppia.android.app.application.ApplicationInjectorProvider
+import org.oppia.android.app.application.ApplicationModule
+import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
+import org.oppia.android.app.devoptions.DeveloperOptionsModule
+import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.ScreenName
+import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
+import org.oppia.android.app.shim.ViewBindingShimModule
+import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
+import org.oppia.android.data.backends.gae.NetworkConfigProdModule
+import org.oppia.android.data.backends.gae.NetworkModule
+import org.oppia.android.domain.classify.InteractionsModule
+import org.oppia.android.domain.classify.rules.algebraicexpressioninput.AlgebraicExpressionInputModule
+import org.oppia.android.domain.classify.rules.continueinteraction.ContinueModule
+import org.oppia.android.domain.classify.rules.dragAndDropSortInput.DragDropSortInputModule
+import org.oppia.android.domain.classify.rules.fractioninput.FractionInputModule
+import org.oppia.android.domain.classify.rules.imageClickInput.ImageClickInputModule
+import org.oppia.android.domain.classify.rules.itemselectioninput.ItemSelectionInputModule
+import org.oppia.android.domain.classify.rules.mathequationinput.MathEquationInputModule
+import org.oppia.android.domain.classify.rules.multiplechoiceinput.MultipleChoiceInputModule
+import org.oppia.android.domain.classify.rules.numberwithunits.NumberWithUnitsRuleModule
+import org.oppia.android.domain.classify.rules.numericexpressioninput.NumericExpressionInputModule
+import org.oppia.android.domain.classify.rules.numericinput.NumericInputRuleModule
+import org.oppia.android.domain.classify.rules.ratioinput.RatioInputModule
+import org.oppia.android.domain.classify.rules.textinput.TextInputRuleModule
+import org.oppia.android.domain.exploration.ExplorationProgressModule
+import org.oppia.android.domain.exploration.ExplorationStorageModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionConfigModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionProdModule
+import org.oppia.android.domain.onboarding.ExpirationMetaDataRetrieverModule
+import org.oppia.android.domain.oppialogger.LogStorageModule
+import org.oppia.android.domain.oppialogger.LoggingIdentifierModule
+import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule
+import org.oppia.android.domain.oppialogger.analytics.CpuPerformanceSnapshotterModule
+import org.oppia.android.domain.oppialogger.logscheduler.MetricLogSchedulerModule
+import org.oppia.android.domain.oppialogger.loguploader.LogReportWorkerModule
+import org.oppia.android.domain.platformparameter.PlatformParameterModule
+import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule
+import org.oppia.android.domain.question.QuestionModule
+import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.firebase.TestAuthenticationModule
+import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
+import org.oppia.android.testing.robolectric.RobolectricModule
+import org.oppia.android.testing.threading.TestCoroutineDispatchers
+import org.oppia.android.testing.threading.TestDispatcherModule
+import org.oppia.android.testing.time.FakeOppiaClockModule
+import org.oppia.android.util.accessibility.AccessibilityTestModule
+import org.oppia.android.util.caching.AssetModule
+import org.oppia.android.util.caching.testing.CachingTestModule
+import org.oppia.android.util.gcsresource.GcsResourceModule
+import org.oppia.android.util.locale.LocaleProdModule
+import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.extractCurrentAppScreenName
+import org.oppia.android.util.logging.EventLoggingConfigurationModule
+import org.oppia.android.util.logging.LoggerModule
+import org.oppia.android.util.logging.SyncStatusModule
+import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule
+import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule
+import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule
+import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule
+import org.oppia.android.util.parser.image.GlideImageLoaderModule
+import org.oppia.android.util.parser.image.ImageParsingModule
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [OnboardingProfileTypeActivity]. */
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(
+ application = OnboardingProfileTypeActivityTest.TestApplication::class,
+ qualifiers = "port-xxhdpi"
+)
+class OnboardingProfileTypeActivityTest {
+ @get:Rule
+ val initializeDefaultLocaleRule = InitializeDefaultLocaleRule()
+
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
+ @Inject
+ lateinit var context: Context
+
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ @Before
+ fun setUp() {
+ Intents.init()
+ setUpTestApplicationComponent()
+ }
+
+ @After
+ fun tearDown() {
+ Intents.release()
+ }
+
+ @Test
+ fun testActivity_createIntent_verifyScreenNameInIntent() {
+ val screenName =
+ OnboardingProfileTypeActivity.createOnboardingProfileTypeActivityIntent(context)
+ .extractCurrentAppScreenName()
+
+ assertThat(screenName).isEqualTo(ScreenName.ONBOARDING_PROFILE_TYPE_ACTIVITY)
+ }
+
+ @Test
+ fun testProfileTypeActivity_hasCorrectActivityLabel() {
+ launchOnboardingProfileTypeActivity().use { scenario ->
+ lateinit var title: CharSequence
+ scenario?.onActivity { activity -> title = activity.title }
+
+ // Verify that the activity label is correct as a proxy to verify TalkBack will announce the
+ // correct string when it's read out.
+ assertThat(title)
+ .isEqualTo(context.getString(R.string.onboarding_profile_type_activity_title))
+ }
+ }
+
+ private fun launchOnboardingProfileTypeActivity():
+ ActivityScenario? {
+ val scenario = ActivityScenario.launch(
+ OnboardingProfileTypeActivity.createOnboardingProfileTypeActivityIntent(context)
+ )
+ testCoroutineDispatchers.runCurrent()
+ return scenario
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
+ @Singleton
+ @Component(
+ modules = [
+ RobolectricModule::class,
+ PlatformParameterModule::class, PlatformParameterSingletonModule::class,
+ TestDispatcherModule::class, ApplicationModule::class,
+ LoggerModule::class, ContinueModule::class, FractionInputModule::class,
+ ItemSelectionInputModule::class, MultipleChoiceInputModule::class,
+ NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class,
+ DragDropSortInputModule::class, ImageClickInputModule::class, InteractionsModule::class,
+ GcsResourceModule::class, GlideImageLoaderModule::class, ImageParsingModule::class,
+ HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class,
+ AccessibilityTestModule::class, LogStorageModule::class, CachingTestModule::class,
+ ExpirationMetaDataRetrieverModule::class,
+ ViewBindingShimModule::class, RatioInputModule::class, WorkManagerConfigurationModule::class,
+ ApplicationStartupListenerModule::class, LogReportWorkerModule::class,
+ HintsAndSolutionConfigModule::class, HintsAndSolutionProdModule::class,
+ FirebaseLogUploaderModule::class, FakeOppiaClockModule::class,
+ DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class,
+ ExplorationStorageModule::class, NetworkModule::class, NetworkConfigProdModule::class,
+ NetworkConnectionUtilDebugModule::class, NetworkConnectionDebugUtilModule::class,
+ AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class,
+ NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
+ MathEquationInputModule::class, SplitScreenInteractionModule::class,
+ LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class,
+ EventLoggingConfigurationModule::class, ActivityRouterModule::class,
+ CpuPerformanceSnapshotterModule::class, ExplorationProgressModule::class,
+ TestAuthenticationModule::class
+ ]
+ )
+
+ interface TestApplicationComponent : ApplicationComponent {
+ @Component.Builder
+ interface Builder : ApplicationComponent.Builder
+
+ fun inject(onboardingProfileTypeActivityTest: OnboardingProfileTypeActivityTest)
+ }
+
+ class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider {
+ private val component: TestApplicationComponent by lazy {
+ DaggerOnboardingProfileTypeActivityTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build() as TestApplicationComponent
+ }
+
+ fun inject(onboardingProfileTypeActivityTest: OnboardingProfileTypeActivityTest) {
+ component.inject(onboardingProfileTypeActivityTest)
+ }
+
+ override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent {
+ return component.getActivityComponentBuilderProvider().get().setActivity(activity).build()
+ }
+
+ override fun getApplicationInjector(): ApplicationInjector = component
+ }
+}
diff --git a/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentTest.kt
new file mode 100644
index 00000000000..ad8f036e214
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentTest.kt
@@ -0,0 +1,398 @@
+package org.oppia.android.app.onboarding
+
+import android.app.Application
+import android.content.Context
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.intent.Intents
+import androidx.test.espresso.intent.Intents.intended
+import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
+import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.isRoot
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.Component
+import org.hamcrest.CoreMatchers.allOf
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.R
+import org.oppia.android.app.activity.ActivityComponent
+import org.oppia.android.app.activity.ActivityComponentFactory
+import org.oppia.android.app.activity.route.ActivityRouterModule
+import org.oppia.android.app.application.ApplicationComponent
+import org.oppia.android.app.application.ApplicationInjector
+import org.oppia.android.app.application.ApplicationInjectorProvider
+import org.oppia.android.app.application.ApplicationModule
+import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
+import org.oppia.android.app.devoptions.DeveloperOptionsModule
+import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
+import org.oppia.android.app.profile.ProfileChooserActivity
+import org.oppia.android.app.shim.ViewBindingShimModule
+import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
+import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape
+import org.oppia.android.data.backends.gae.NetworkConfigProdModule
+import org.oppia.android.data.backends.gae.NetworkModule
+import org.oppia.android.domain.classify.InteractionsModule
+import org.oppia.android.domain.classify.rules.algebraicexpressioninput.AlgebraicExpressionInputModule
+import org.oppia.android.domain.classify.rules.continueinteraction.ContinueModule
+import org.oppia.android.domain.classify.rules.dragAndDropSortInput.DragDropSortInputModule
+import org.oppia.android.domain.classify.rules.fractioninput.FractionInputModule
+import org.oppia.android.domain.classify.rules.imageClickInput.ImageClickInputModule
+import org.oppia.android.domain.classify.rules.itemselectioninput.ItemSelectionInputModule
+import org.oppia.android.domain.classify.rules.mathequationinput.MathEquationInputModule
+import org.oppia.android.domain.classify.rules.multiplechoiceinput.MultipleChoiceInputModule
+import org.oppia.android.domain.classify.rules.numberwithunits.NumberWithUnitsRuleModule
+import org.oppia.android.domain.classify.rules.numericexpressioninput.NumericExpressionInputModule
+import org.oppia.android.domain.classify.rules.numericinput.NumericInputRuleModule
+import org.oppia.android.domain.classify.rules.ratioinput.RatioInputModule
+import org.oppia.android.domain.classify.rules.textinput.TextInputRuleModule
+import org.oppia.android.domain.exploration.ExplorationProgressModule
+import org.oppia.android.domain.exploration.ExplorationStorageModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionConfigModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionProdModule
+import org.oppia.android.domain.onboarding.ExpirationMetaDataRetrieverModule
+import org.oppia.android.domain.oppialogger.LogStorageModule
+import org.oppia.android.domain.oppialogger.LoggingIdentifierModule
+import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule
+import org.oppia.android.domain.oppialogger.analytics.CpuPerformanceSnapshotterModule
+import org.oppia.android.domain.oppialogger.logscheduler.MetricLogSchedulerModule
+import org.oppia.android.domain.oppialogger.loguploader.LogReportWorkerModule
+import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule
+import org.oppia.android.domain.question.QuestionModule
+import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.firebase.TestAuthenticationModule
+import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
+import org.oppia.android.testing.platformparameter.TestPlatformParameterModule
+import org.oppia.android.testing.robolectric.RobolectricModule
+import org.oppia.android.testing.threading.TestCoroutineDispatchers
+import org.oppia.android.testing.threading.TestDispatcherModule
+import org.oppia.android.testing.time.FakeOppiaClockModule
+import org.oppia.android.util.accessibility.AccessibilityTestModule
+import org.oppia.android.util.caching.AssetModule
+import org.oppia.android.util.caching.testing.CachingTestModule
+import org.oppia.android.util.gcsresource.GcsResourceModule
+import org.oppia.android.util.locale.LocaleProdModule
+import org.oppia.android.util.locale.OppiaLocale
+import org.oppia.android.util.logging.EventLoggingConfigurationModule
+import org.oppia.android.util.logging.LoggerModule
+import org.oppia.android.util.logging.SyncStatusModule
+import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule
+import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule
+import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule
+import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule
+import org.oppia.android.util.parser.image.GlideImageLoaderModule
+import org.oppia.android.util.parser.image.ImageParsingModule
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [OnboardingProfileTypeFragment]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(
+ application = OnboardingProfileTypeFragmentTest.TestApplication::class,
+ qualifiers = "port-xxhdpi"
+)
+class OnboardingProfileTypeFragmentTest {
+ @get:Rule
+ val initializeDefaultLocaleRule = InitializeDefaultLocaleRule()
+
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ @Inject
+ lateinit var context: Context
+
+ @Inject
+ lateinit var machineLocale: OppiaLocale.MachineLocale
+
+ @Before
+ fun setUp() {
+ Intents.init()
+ setUpTestApplicationComponent()
+ testCoroutineDispatchers.registerIdlingResource()
+ }
+
+ @After
+ fun tearDown() {
+ testCoroutineDispatchers.unregisterIdlingResource()
+ Intents.release()
+ }
+
+ @Test
+ fun testFragment_portraitMode_headerTextIsDisplayed() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(withId(R.id.profile_type_title))
+ .check(
+ matches(
+ allOf(
+ isDisplayed(),
+ withText(
+ R.string.onboarding_profile_type_activity_header
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testFragment_landscapeMode_headerTextIsDisplayed() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.profile_type_title))
+ .check(
+ matches(
+ allOf(
+ isDisplayed(),
+ withText(
+ R.string.onboarding_profile_type_activity_header
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testFragment_portraitMode_navigationCardsAreDisplayed() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(withId(R.id.profile_type_learner_navigation_card))
+ .check(
+ matches(
+ allOf(
+ isDisplayed(),
+ hasDescendant(
+ withText(R.string.onboarding_profile_type_activity_student_text)
+ )
+ )
+ )
+ )
+
+ onView(withId(R.id.profile_type_supervisor_navigation_card))
+ .check(
+ matches(
+ allOf(
+ isDisplayed(),
+ hasDescendant(
+ withText(R.string.onboarding_profile_type_activity_parent_text)
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testFragment_landscapeMode_navigationCardsAreDisplayed() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.profile_type_learner_navigation_card))
+ .check(
+ matches(
+ allOf(
+ isDisplayed(),
+ hasDescendant(
+ withText(R.string.onboarding_profile_type_activity_student_text)
+ )
+ )
+ )
+ )
+
+ onView(withId(R.id.profile_type_supervisor_navigation_card))
+ .check(
+ matches(
+ allOf(
+ isDisplayed(),
+ hasDescendant(
+ withText(R.string.onboarding_profile_type_activity_parent_text)
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testFragment_portrait_stepCountTextIsDisplayed() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(withId(R.id.onboarding_steps_count))
+ .check(
+ matches(
+ allOf(
+ isDisplayed(),
+ withText(
+ R.string.onboarding_step_count_two
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testFragment_studentNavigationCardClicked_launchesCreateProfileScreen() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(withId(R.id.profile_type_learner_navigation_card)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ // Does nothing for now, but should fail once navigation is implemented in a future PR.
+ onView(withId(R.id.profile_type_learner_navigation_card))
+ .check(matches(isDisplayed()))
+
+ onView(withId(R.id.profile_type_supervisor_navigation_card))
+ .check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testFragment_orientationChange_studentNavigationCardClicked_launchesCreateProfileScreen() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.profile_type_learner_navigation_card)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ // Does nothing for now, but should fail once navigation is implemented in a future PR.
+ onView(withId(R.id.profile_type_learner_navigation_card))
+ .check(matches(isDisplayed()))
+
+ onView(withId(R.id.profile_type_supervisor_navigation_card))
+ .check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testFragment_supervisorNavigationCardClicked_launchesProfileChooserScreen() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(withId(R.id.profile_type_supervisor_navigation_card)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testFragment_orientationChange_supervisorCardClicked_launchesProfileChooserScreen() {
+ launchOnboardingProfileTypeActivity().use {
+ onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.profile_type_supervisor_navigation_card)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testFragment_backButtonPressed_currentScreenIsDestroyed() {
+ launchOnboardingProfileTypeActivity().use { scenario ->
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.onboarding_navigation_back)).perform(click())
+ scenario?.onActivity { activity ->
+ assertThat(activity.isFinishing).isTrue()
+ }
+ }
+ }
+
+ @Test
+ fun testFragment_landscapeMode_backButtonPressed_currentScreenIsDestroyed() {
+ launchOnboardingProfileTypeActivity().use { scenario ->
+ onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.onboarding_navigation_back)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ scenario?.onActivity { activity ->
+ assertThat(activity.isFinishing).isTrue()
+ }
+ }
+ }
+
+ private fun launchOnboardingProfileTypeActivity():
+ ActivityScenario? {
+ val scenario = ActivityScenario.launch(
+ OnboardingProfileTypeActivity.createOnboardingProfileTypeActivityIntent(context)
+ )
+ testCoroutineDispatchers.runCurrent()
+ return scenario
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
+ @Singleton
+ @Component(
+ modules = [
+ TestPlatformParameterModule::class, RobolectricModule::class,
+ TestDispatcherModule::class, ApplicationModule::class,
+ LoggerModule::class, ContinueModule::class, FractionInputModule::class,
+ ItemSelectionInputModule::class, MultipleChoiceInputModule::class,
+ NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class,
+ DragDropSortInputModule::class, ImageClickInputModule::class, InteractionsModule::class,
+ GcsResourceModule::class, GlideImageLoaderModule::class, ImageParsingModule::class,
+ HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class,
+ AccessibilityTestModule::class, LogStorageModule::class, CachingTestModule::class,
+ ExpirationMetaDataRetrieverModule::class,
+ ViewBindingShimModule::class, RatioInputModule::class, WorkManagerConfigurationModule::class,
+ ApplicationStartupListenerModule::class, LogReportWorkerModule::class,
+ HintsAndSolutionConfigModule::class, HintsAndSolutionProdModule::class,
+ FirebaseLogUploaderModule::class, FakeOppiaClockModule::class,
+ DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class,
+ ExplorationStorageModule::class, NetworkModule::class, NetworkConfigProdModule::class,
+ NetworkConnectionUtilDebugModule::class, NetworkConnectionDebugUtilModule::class,
+ AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class,
+ PlatformParameterSingletonModule::class,
+ NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
+ MathEquationInputModule::class, SplitScreenInteractionModule::class,
+ LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class,
+ EventLoggingConfigurationModule::class, ActivityRouterModule::class,
+ CpuPerformanceSnapshotterModule::class, ExplorationProgressModule::class,
+ TestAuthenticationModule::class
+ ]
+ )
+ interface TestApplicationComponent : ApplicationComponent {
+ @Component.Builder
+ interface Builder : ApplicationComponent.Builder
+
+ fun inject(onboardingProfileTypeFragmentTest: OnboardingProfileTypeFragmentTest)
+ }
+
+ class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider {
+ private val component: TestApplicationComponent by lazy {
+ DaggerOnboardingProfileTypeFragmentTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build() as TestApplicationComponent
+ }
+
+ fun inject(onboardingProfileTypeFragmentTest: OnboardingProfileTypeFragmentTest) {
+ component.inject(onboardingProfileTypeFragmentTest)
+ }
+
+ override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent {
+ return component.getActivityComponentBuilderProvider().get().setActivity(activity).build()
+ }
+
+ override fun getApplicationInjector(): ApplicationInjector = component
+ }
+}
diff --git a/model/src/main/proto/screens.proto b/model/src/main/proto/screens.proto
index 84518cbea6d..9e323e5aa72 100644
--- a/model/src/main/proto/screens.proto
+++ b/model/src/main/proto/screens.proto
@@ -161,6 +161,9 @@ enum ScreenName {
// Screen name value for the scenario when the classroom list activity is visible to the user.
CLASSROOM_LIST_ACTIVITY = 50;
+
+ // Screen name value for the scenario when the profile type activity is visible to the user.
+ ONBOARDING_PROFILE_TYPE_ACTIVITY = 51;
}
// Defines the current visible UI screen of the application.
diff --git a/scripts/assets/file_content_validation_checks.textproto b/scripts/assets/file_content_validation_checks.textproto
index 6a32f0c51ab..9a9918f2f64 100644
--- a/scripts/assets/file_content_validation_checks.textproto
+++ b/scripts/assets/file_content_validation_checks.textproto
@@ -358,7 +358,9 @@ file_content_checks {
failure_message: "Only colors from component_colors.xml may be used in drawables except vector assets."
exempted_file_patterns: "app/src/main/res/drawable.*?/(ic_|lesson_thumbnail_graphic_).+?\\.xml"
exempted_file_patterns: "app/src/main/res/drawable/full_oppia_logo.xml"
+ exempted_file_patterns: "app/src/main/res/drawable/learner_otter.xml"
exempted_file_patterns: "app/src/main/res/drawable/otter.xml"
+ exempted_file_patterns: "app/src/main/res/drawable/parent_teacher_otter.xml"
exempted_file_patterns: "app/src/main/res/drawable/profile_image_shadow.xml"
exempted_file_patterns: "app/src/main/res/drawable/rounded_white_background_with_shadow.xml"
exempted_file_patterns: "app/src/main/res/drawable/selected_region_background.xml"
diff --git a/scripts/assets/test_file_exemptions.textproto b/scripts/assets/test_file_exemptions.textproto
index 6721dcb339e..4f5600d8576 100644
--- a/scripts/assets/test_file_exemptions.textproto
+++ b/scripts/assets/test_file_exemptions.textproto
@@ -1066,6 +1066,14 @@ test_file_exemption {
exempted_file_path: "app/src/main/java/org/oppia/android/app/onboarding/ViewPagerSlide.kt"
test_file_not_required: true
}
+test_file_exemption {
+ exempted_file_path: "app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivityPresenter.kt"
+ test_file_not_required: true
+}
+test_file_exemption {
+ exempted_file_path: "app/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragmentPresenter.kt"
+ test_file_not_required: true
+}
test_file_exemption {
exempted_file_path: "app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicItemViewModel.kt"
test_file_not_required: true
diff --git a/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt b/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt
index ab091a7bba7..bc52286b4e7 100644
--- a/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt
+++ b/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt
@@ -855,6 +855,7 @@ class EventBundleCreator @Inject constructor(
ScreenName.FOREGROUND_SCREEN -> "foreground_screen"
ScreenName.SURVEY_ACTIVITY -> "survey_activity"
ScreenName.CLASSROOM_LIST_ACTIVITY -> "classroom_list_activity"
+ ScreenName.ONBOARDING_PROFILE_TYPE_ACTIVITY -> "onboarding_profile_type_activity"
}
private fun AppLanguageSelection.toAnalyticsText(): String {