Skip to content
Open
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f3e8961
New layout for auth
adhiamboperes Mar 18, 2025
471e1a6
Merge remote-tracking branch 'upstream/develop' into redesign-pin-input
adhiamboperes Apr 26, 2025
14296af
Add compose livedata dependency
adhiamboperes May 2, 2025
cb7936f
Create color and string resources
adhiamboperes May 2, 2025
b10ae81
Create xml layouts
adhiamboperes May 2, 2025
5a366f1
Create ProfileLoginActivity
adhiamboperes May 2, 2025
83f3762
Create ProfileLoginFragment
adhiamboperes May 2, 2025
c384747
Refactor login intent to new ProfileLoginActivity
adhiamboperes May 2, 2025
a691304
Add todos to track cleanup
adhiamboperes May 2, 2025
eec9434
Cleanup unused items
adhiamboperes May 2, 2025
99e1a19
Add admin reset pin flow
adhiamboperes May 4, 2025
a93542a
Add route to non-admin reset pin flow
adhiamboperes May 4, 2025
1ea182a
Add non-admin pin reset flow
adhiamboperes May 4, 2025
69973d1
wip commit
adhiamboperes May 5, 2025
b393870
Merge branch 'develop' of github.com:oppia/oppia-android into redesig…
adhiamboperes May 5, 2025
9ef0501
Add activity tests
adhiamboperes May 15, 2025
734c9ab
Add fragment tests
adhiamboperes May 21, 2025
d894646
Break down large composables
adhiamboperes May 22, 2025
a0ca9de
Add tests for login flow
adhiamboperes May 22, 2025
0524005
Move dialogs to own files
adhiamboperes May 22, 2025
3c7e3ff
Add tests for dialog flows: wip for non-admin
adhiamboperes May 22, 2025
a83c10b
Add todo to ensure an admin profile is created when all profile data …
adhiamboperes May 22, 2025
09bfe8c
Fix lint issues
adhiamboperes May 22, 2025
a4e9208
Fix static analysis checks failures
adhiamboperes May 23, 2025
e0a697e
Fix ProfileChooserFragmentTest failures
adhiamboperes May 23, 2025
6680a9c
Finish dialog tests
adhiamboperes May 23, 2025
7523db0
Merge branch 'develop' of github.com:oppia/oppia-android into redesig…
adhiamboperes May 23, 2025
828e042
Format dagger modules
adhiamboperes May 23, 2025
c1f6619
Fix missing dagger module
adhiamboperes May 23, 2025
432f3d7
fix trim spannable bug
adhiamboperes May 27, 2025
4e4315f
Merge branch 'develop' of github.com:oppia/oppia-android into develop
adhiamboperes Jun 6, 2025
bff3c08
Merge branch 'develop' into redesign-pin-input
adhiamboperes Jun 6, 2025
7d781b4
Merge branch 'develop' into redesign-pin-input
adhiamboperes Jun 30, 2025
98372c9
Generate a new maven_install due to conflicts in the old one.
adhiamboperes Jun 30, 2025
d6cc784
regenerate maven_install.json
adhiamboperes Jun 30, 2025
8dbe1ec
address reviewer comment.
adhiamboperes Jul 1, 2025
92efbaf
Revert unrelated changed
adhiamboperes Jul 1, 2025
99eeafb
fix tests
adhiamboperes Jul 1, 2025
7971ea4
Merge branch 'develop' into redesign-pin-input
adhiamboperes Jul 1, 2025
54304b3
Merge branch 'develop' into redesign-pin-input
adhiamboperes Jul 1, 2025
f6c3ce4
Repin dependencies
adhiamboperes Jul 1, 2025
d3be083
Merge branch 'redesign-pin-input' of github.com:oppia/oppia-android i…
adhiamboperes Jul 1, 2025
ef109b7
Merge branch 'develop' into redesign-pin-input
adhiamboperes Jul 3, 2025
4b42e64
Exempt files from coverage.
adhiamboperes Jul 3, 2025
da39e88
Address reviewer comments
adhiamboperes Jul 3, 2025
d9cd074
Fix coverage failures
adhiamboperes Jul 3, 2025
a0e3293
Merge branch 'develop' into redesign-pin-input
adhiamboperes Jul 8, 2025
b745269
Merge branch 'develop' into redesign-pin-input
adhiamboperes Jul 15, 2025
696089e
Merge branch 'develop' of github.com:oppia/oppia-android into redesig…
adhiamboperes Aug 16, 2025
cf03541
Merge branch 'develop' into redesign-pin-input
adhiamboperes Aug 22, 2025
7a2928a
Use use {} block for scenario
adhiamboperes Aug 25, 2025
cc2b32f
Remove test tags
adhiamboperes Aug 25, 2025
8b2e18b
Merge branch 'develop' of github.com:oppia/oppia-android into redesig…
adhiamboperes Aug 25, 2025
82e0ba2
Merge branch 'redesign-pin-input' of github.com:oppia/oppia-android i…
adhiamboperes Aug 25, 2025
3ff1286
Add lint exemption
adhiamboperes Aug 25, 2025
4589f9e
Merge branch 'develop' of github.com:oppia/oppia-android into redesig…
adhiamboperes Sep 1, 2025
50f62cd
Add comments explaining test tag usage
adhiamboperes Sep 1, 2025
f8670b9
fix kdoc formatting
adhiamboperes Sep 1, 2025
99ef626
add comment
adhiamboperes Sep 7, 2025
252a05b
resolve merge conflicts from develop
adhiamboperes Sep 7, 2025
1373660
Remove duplicate tests
adhiamboperes Sep 8, 2025
1a03d92
Merge remote-tracking branch 'upstream/develop' into redesign-pin-input
adhiamboperes Sep 8, 2025
6b161f5
Merge branch 'develop' into redesign-pin-input
adhiamboperes Sep 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ kt_android_library(
"//third_party:androidx_compose_foundation_foundation-layout",
"//third_party:androidx_compose_material_material",
"//third_party:androidx_compose_runtime_runtime",
"//third_party:androidx_compose_runtime_runtime-livedata",
"//third_party:androidx_compose_ui_ui",
"//third_party:androidx_core_core-ktx",
"//third_party:androidx_databinding_databinding-adapters",
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@
android:name=".app.onboarding.IntroActivity"
android:label="@string/onboarding_learner_intro_activity_title"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.profile.ProfileLoginActivity"
android:label="@string/profile_login_activity_title"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.testing.TextInputLayoutBindingAdaptersTestActivity"
android:theme="@style/OppiaThemeWithoutActionBar" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import org.oppia.android.app.profile.AdminAuthActivity
import org.oppia.android.app.profile.AdminPinActivity
import org.oppia.android.app.profile.PinPasswordActivity
import org.oppia.android.app.profile.ProfileChooserActivity
import org.oppia.android.app.profile.ProfileLoginActivity
import org.oppia.android.app.profileprogress.ProfilePictureActivity
import org.oppia.android.app.profileprogress.ProfileProgressActivity
import org.oppia.android.app.resumelesson.ResumeLessonActivity
Expand Down Expand Up @@ -230,4 +231,5 @@ interface ActivityComponentImpl :
fun inject(onboardingProfileTypeActivity: OnboardingProfileTypeActivity)
fun inject(createProfileActivity: CreateProfileActivity)
fun inject(introActivity: IntroActivity)
fun inject(profileLoginActivity: ProfileLoginActivity)
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ class ClassroomListFragmentPresenter @Inject constructor(
private lateinit var binding: ClassroomListFragmentBinding
private lateinit var classroomListViewModel: ClassroomListViewModel
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? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import org.oppia.android.app.player.stopplaying.UnsavedExplorationDialogFragment
import org.oppia.android.app.policies.PoliciesFragment
import org.oppia.android.app.profile.AdminSettingsDialogFragment
import org.oppia.android.app.profile.ProfileChooserFragment
import org.oppia.android.app.profile.ProfileLoginFragment
import org.oppia.android.app.profile.ResetPinDialogFragment
import org.oppia.android.app.profileprogress.ProfilePictureEditDialogFragment
import org.oppia.android.app.profileprogress.ProfileProgressFragment
Expand Down Expand Up @@ -210,4 +211,5 @@ interface FragmentComponentImpl : FragmentComponent, ViewComponentBuilderInjecto
fun inject(createProfileFragment: CreateProfileFragment)
fun inject(introFragment: IntroFragment)
fun inject(flashbackConfirmationDialogFragment: FlashbackConfirmationDialogFragment)
fun inject(profileLoginFragment: ProfileLoginFragment)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.oppia.android.app.profile

import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.window.DialogProperties
import org.oppia.android.app.ui.R

/** Composable that represents the admin's forgot pin dialog. */
@Composable
fun AdminPinRecoveryDialog(
onDismissRequest: () -> Unit,
onConfirmation: () -> Unit
) {
val appName = stringResource(R.string.app_name)

AlertDialog(
title = {
Text(stringResource(R.string.profile_login_forgot_pin_dialog_title))
},
text = {
Text(stringResource(R.string.profile_login_forgot_pin_dialog_message, appName))
},
properties = DialogProperties(
dismissOnClickOutside = false,
dismissOnBackPress = false
),
onDismissRequest = { onDismissRequest() },
dismissButton = {
TextButton(
onClick = { onDismissRequest() }
) {
Text(stringResource(R.string.profile_login_forgot_pin_dialog_cancel_button))
}
},
confirmButton = {
TextButton(
onClick = { onConfirmation() }
) {
Text(stringResource(R.string.profile_login_forgot_pin_dialog_reset_button, appName))
}
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,31 @@ import android.os.Bundle
import org.oppia.android.app.fragment.FragmentComponentImpl
import org.oppia.android.app.fragment.InjectableDialogFragment
import org.oppia.android.app.model.AdminSettingsDialogFragmentArguments
import org.oppia.android.app.model.ProfileId
import org.oppia.android.util.extensions.getProto
import org.oppia.android.util.extensions.putProto
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId
import javax.inject.Inject

/** DialogFragment that allows user to input admin PIN. */
class AdminSettingsDialogFragment : InjectableDialogFragment() {
companion object {
/** Arguments key for AdminSettingsDialogFragment. */
const val ADMIN_SETTINGS_DIALOG_FRAGMENT_ARGUMENTS_KEY = "AdminSettingsDialogFragment.arguments"
fun newInstance(adminPin: String): AdminSettingsDialogFragment {
val args = AdminSettingsDialogFragmentArguments.newBuilder().setAdminPin(adminPin).build()
fun newInstance(
adminPin: String,
profileId: ProfileId,
profileName: String
): AdminSettingsDialogFragment {
val args = AdminSettingsDialogFragmentArguments.newBuilder()
.setAdminPin(adminPin)
.setCurrentProfileName(profileName)
.build()
return AdminSettingsDialogFragment().apply {
arguments = Bundle().apply {
putProto(ADMIN_SETTINGS_DIALOG_FRAGMENT_ARGUMENTS_KEY, args)
decorateWithUserProfileId(profileId)
}
}
}
Expand All @@ -39,10 +50,16 @@ class AdminSettingsDialogFragment : InjectableDialogFragment() {
AdminSettingsDialogFragmentArguments.getDefaultInstance()
)
val adminPin = args?.adminPin
val profileName = args?.currentProfileName ?: ""
val profileId = arguments?.extractCurrentUserProfileId() ?: ProfileId.getDefaultInstance()

checkNotNull(adminPin) { "Admin Pin must not be null" }

return adminSettingsDialogFragmentPresenter.handleOnCreateDialog(
activity as ProfileRouteDialogInterface,
adminPin
adminPin,
profileId,
profileName
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import org.oppia.android.app.databinding.databinding.AdminSettingsDialogBinding
import org.oppia.android.app.fragment.FragmentScope
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.app.ui.R
import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged
Expand All @@ -24,7 +25,9 @@ class AdminSettingsDialogFragmentPresenter @Inject constructor(
) {
fun handleOnCreateDialog(
routeDialogInterface: ProfileRouteDialogInterface,
adminPin: String?
adminPin: String?,
profileId: ProfileId,
profileName: String
): Dialog {
val binding: AdminSettingsDialogBinding =
DataBindingUtil.inflate(
Expand Down Expand Up @@ -83,7 +86,7 @@ class AdminSettingsDialogFragmentPresenter @Inject constructor(
return@setOnClickListener
}
if (binding.adminSettingsInputPinEditText.text.toString() == adminPin) {
routeDialogInterface.routeToResetPinDialog()
routeDialogInterface.routeToResetPinDialog(profileId, profileName)
} else {
adminViewModel.errorMessage.set(
resourceHandler.getStringInLocale(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.oppia.android.app.profile

import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.window.DialogProperties
import org.oppia.android.app.ui.R

/** Composable that represents the admin's reset app data confirmation dialog. */
@Composable
fun DataResetConfirmationDialog(
onDismissRequest: () -> Unit,
deleteAppData: () -> Unit
) {
val appName = stringResource(R.string.app_name)

AlertDialog(
title = {
Text(
stringResource(R.string.admin_confirm_app_wipe_title, appName)
)
},
text = {
Text(stringResource(R.string.admin_confirm_app_wipe_message, appName))
},
properties = DialogProperties(
dismissOnClickOutside = false,
dismissOnBackPress = false
),
onDismissRequest = { onDismissRequest() },
dismissButton = {
TextButton(
onClick = { onDismissRequest() }
) {
Text(stringResource(R.string.admin_confirm_app_wipe_negative_button_text))
}
},
confirmButton = {
TextButton(
onClick = { deleteAppData() }
) {
Text(
stringResource(
R.string.admin_confirm_app_wipe_positive_button_text,
appName
)
)
}
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import android.os.Bundle
import org.oppia.android.app.activity.ActivityComponentImpl
import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity
import org.oppia.android.app.model.PinPasswordActivityParams
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.model.ScreenName.PIN_PASSWORD_ACTIVITY
import org.oppia.android.util.extensions.putProtoExtra
import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName
import javax.inject.Inject

// TODO(#5817): Remove when v2 onboarding flow has stabilized.
/** Activity that allows user to input his or her PIN. */
class PinPasswordActivity :
InjectableAutoLocalizedAppCompatActivity(),
Expand Down Expand Up @@ -43,7 +45,7 @@ class PinPasswordActivity :
pinPasswordActivityPresenter.handleOnCreate()
}

override fun routeToResetPinDialog() {
override fun routeToResetPinDialog(profileId: ProfileId, profileName: String) {
pinPasswordActivityPresenter.handleRouteToResetPinDialog()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,32 @@ class PinPasswordActivityPresenter @Inject constructor(
}

binding.forgotPin.setOnClickListener {
if (pinViewModel.isAdmin.get()!!) {
val isAdmin = pinViewModel.isAdmin.get() ?: false
if (isAdmin) {
showAdminForgotPin()
} else {
val previousFrag =
activity.supportFragmentManager.findFragmentByTag(TAG_ADMIN_SETTINGS_DIALOG)
if (previousFrag != null) {
activity.supportFragmentManager.beginTransaction().remove(previousFrag).commitNow()
activity.supportFragmentManager.beginTransaction()
.remove(previousFrag)
.commitNow()
}

val nameValue = pinViewModel.name.get()

if (adminPin != null && nameValue != null) {
val dialogFragment = AdminSettingsDialogFragment
.newInstance(adminPin, profileId, nameValue)
dialogFragment.showNow(
activity.supportFragmentManager,
TAG_ADMIN_SETTINGS_DIALOG
)
}
val dialogFragment = AdminSettingsDialogFragment
.newInstance(adminPin!!)
dialogFragment.showNow(activity.supportFragmentManager, TAG_ADMIN_SETTINGS_DIALOG)
}
}

if (pinViewModel.showAdminPinForgotPasswordPopUp.get()!!) {
if (pinViewModel.showAdminPinForgotPasswordPopUp.get() == true) {
showAdminForgotPin()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.oppia.android.util.data.AsyncResult
import org.oppia.android.util.data.DataProviders.Companion.toLiveData
import javax.inject.Inject

// TODO(#5817): Remove along with PinPasswordActivity when v2 onboarding flow has stabilized.
/** The ViewModel for [PinPasswordActivity]. */
@ActivityScope
class PinPasswordViewModel @Inject constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ProfileChooserActivityPresenter @Inject constructor(
/** Adds [ProfileChooserFragment] to view. */
fun handleOnCreate(profileId: ProfileId, profileType: ProfileType) {
if (enableOnboardingFlowV2.value) {
// TODO(#4938): Ensure default profile is present when the admin resets the app data.
profileManagementController.updateNewProfileDetails(
profileId = profileId,
profileType = profileType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,11 @@ class ProfileChooserFragmentPresenter @Inject constructor(
}
}
} else {
val pinPasswordIntent = PinPasswordActivity.createPinPasswordActivityIntent(
activity,
chooserViewModel.adminPin,
profile.id.internalId
val profileLoginIntent = ProfileLoginActivity.createProfileLoginActivityIntent(
context,
profile.id
)
activity.startActivity(pinPasswordIntent)
activity.startActivity(profileLoginIntent)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.oppia.android.app.profile

import android.content.Context
import android.content.Intent
import android.os.Bundle
import org.oppia.android.app.activity.ActivityComponentImpl
import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.model.ScreenName
import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId
import javax.inject.Inject

/** Activity that allows user to log in to their profile by inputting their PIN. */
class ProfileLoginActivity :
InjectableAutoLocalizedAppCompatActivity(),
ProfileRouteDialogInterface {

@Inject
lateinit var profileLoginActivityPresenter: ProfileLoginActivityPresenter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activityComponent as ActivityComponentImpl).inject(this)

val profileId = intent.extractCurrentUserProfileId()

profileLoginActivityPresenter.handleOnCreate(profileId)
}

companion object {
/** Creates and returns an Intent to open a new [ProfileLoginActivity]. */
fun createProfileLoginActivityIntent(
context: Context,
profileId: ProfileId
): Intent {
return Intent(context, ProfileLoginActivity::class.java).apply {
decorateWithUserProfileId(profileId)
decorateWithScreenName(ScreenName.PROFILE_LOGIN_ACTIVITY)
}
}
}

override fun routeToResetPinDialog(profileId: ProfileId, profileName: String) {
profileLoginActivityPresenter.handleRouteToResetPinDialog(profileId, profileName)
}

override fun routeToSuccessDialog() {
profileLoginActivityPresenter.handleRouteToSuccessDialog()
}
}
Loading
Loading