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 {