Skip to content

Fix Part of #4938: Create Admin PIN (4/12) - #5880

Open
adhiamboperes wants to merge 159 commits into
developfrom
create-pin-setup
Open

Fix Part of #4938: Create Admin PIN (4/12)#5880
adhiamboperes wants to merge 159 commits into
developfrom
create-pin-setup

Conversation

@adhiamboperes

@adhiamboperes adhiamboperes commented Jul 4, 2025

Copy link
Copy Markdown
Contributor

Explanation

Fixes part of #4938

Introduces a new screen for creating an admin PIN during admin onboarding. This is the last step of admin onboarding.

The PR ensures there are validations to enforce correct PINs that are 5 digits long and that the user inputs and appropriate error messages are retained even on configuration changes.

There are additional changes that clean up the onboarding flow placeholders, and tests have been added where necessary.

Essential Checklist

  • 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: ...".)
  • Any changes to scripts/assets files have their rationale included in the PR explanation.
  • The PR follows the style guide.
  • The PR does not contain any unnecessary code changes from Android Studio (reference).
  • The PR is made from a branch that's not called "develop" and is up-to-date with "develop".
  • The PR is assigned to the appropriate reviewers (reference).

For UI-specific PRs only

If your PR includes UI-related changes, then:

  • Add screenshots for portrait/landscape for both a tablet & phone of the before & after UI changes
  • For the screenshots above, include both English and pseudo-localized (RTL) screenshots (see RTL guide)
  • Add a video showing the full UX flow with a screen reader enabled (see accessibility guide)
  • For PRs introducing new UI elements or color changes, both light and dark mode screenshots must be included
  • Add a screenshot demonstrating that you ran affected Espresso tests locally & that they're passing

@github-actions

github-actions Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

@adhiamboperes this PR is being marked as draft because the PR description must contain 'Fixes #' or 'Fixes part of #' for each issue the PR is changing, and each one on its own line with no other text.

@github-actions
github-actions Bot marked this pull request as draft April 1, 2026 22:43
@adhiamboperes

Copy link
Copy Markdown
Contributor Author

Also the issue assignment workflow is still failing despite this branch being up to date with develop.

@adhiamboperes
adhiamboperes marked this pull request as ready for review April 1, 2026 22:54
@BenHenning

Copy link
Copy Markdown
Member

Also the issue assignment workflow is still failing despite this branch being up to date with develop.

@adhiamboperes it seems the regex check is case sensitive. Perhaps it will pass now?

@BenHenning BenHenning left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @adhiamboperes! Took a full pass, PTAL. I'll also respond to my earlier long comment (but can't as part of this review due to a GitHub bug).


@Test
fun testFragment_onLaunch_allTextViewsHaveCorrectContent() {
launch(CreateAdminPinActivity::class.java).use {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: it's ideal to test fragments in the context of a different activity. If it's easy I suggest using TestActivity for this purpose.

That might potentially help with figuring out the timeout, too, since it looks like (per CI) both the fragment and activity classes are hanging.

Comment on lines +137 to +138
delay(100)
keyboardController?.show()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the delay and explicit show actually necessary? Gemini seems to suggest that the delay is likely what's causing the deadlock with our coroutine dispatcher testing barrier which would cause the hanging. It seems to assume that these lines may be unnecessary because focusing the text view ought to be sufficient to open the keyboard automatically.

However, if this is called too soon it may need to be wrapped in a onGloballyPositioned so that focus happens after layout.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, They keyboard shows correctly without the delay, so I have removed it, and there was no need to use onGloballyPositioned

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the delay does not fix the hanging test issue. I had previously spent a lot of time investigating this issue, and the closest I got was pinpointing a platform parameter load issue:

at org.oppia.android.testing.platformparameter.TestPlatformParameterModule$providePlatformParameterController$1.loadParametersAsync
at org.oppia.android.app.activity.InjectableAppCompatActivity$ensurePlatformParametersAreLoaded$1.invokeSuspend

Comment on lines +259 to +262
{
focusManager.clearFocus()
onDone?.invoke()
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary extra curly braces?

@adhiamboperes adhiamboperes Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are needed. The function expectes a unit:

Error: type mismatch: inferred type is Unit? but (KeyboardActionScope.() -> Unit)? was expected
          onDone = if (imeAction == ImeAction.Done) {
                   ^

else -> PinValidationResult(
isValid = false,
errorMessage = resourceHandler.getStringInLocaleWithWrapping(
R.string.create_admin_pin_activity_mismatch_error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some repeat references to these strings. Is it possible to cleanly refactor to avoid repeating these such that each string is only referenced once?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added these to the companion object and refactored the references.

Comment on lines +486 to +489
private data class PinValidationResult(
val isValid: Boolean,
val errorMessage: String = ""
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, but you could refine this a bit by leveraging a sealed class and also using a StringRes so that the string lookup only needs to happen in one place for the error:

private sealed class PinValidationResult {
  object Valid: PinValidationResult()
  data class Invalid(@StringRes val errorMessageId: Int): PinValidationResult()
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, the code readability has improved a bit.

private val resourceHandler: AppLanguageResourceHandler,
private val profileManagementController: ProfileManagementController
) {
private lateinit var binding: CreateAdminPinFragmentBinding

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this actually need to use databinding? It seems unnecessary looking at the layout file (and probably better to avoid it since I'm not exactly sure how well Compose and databinding will interoperate...).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made the ComposeView the root element in the layout, and refactored its reference directly:

val composeView = inflater.inflate(
  R.layout.create_admin_pin_fragment, container, /* attachToRoot= */ false
) as ComposeView

}
}

fun testFragment_supervisorOnboardingFlow_stepCountThreeText_isDisplayed() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing @Test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

testCoroutineDispatchers.runCurrent()

composeRule
.onNodeWithText(context.getString(R.string.create_admin_pin_activity_blank_error))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm looking at these tests, I acknowledge they are quite clean however one issue I see is that there's a bit of a risk here. I think it's theoretically possible to show every related string to the new screen at one time and all of the tests will pass (or most will, there are a few assert not visible).

This is actually why I think it's important to validate specific views but I know Compose makes that tricky. I think we do need a solution for this but I'm not precisely sure what it is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized that I didn't quite understand your intent here.

If you mean that we should assert something about the structure or context of those strings, then we could try to ensure we assert for a few conditions at a time, e.g. all other errors hidden + button disabled + only expected error shown. The downside is that the tests can be overly verbose.

But this doesn't look unique to Compose, since the problem could still occur with Android Views.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or did you mean:

composeTestRule
    .onNode(matcher).assert(hasText("Button") or hasText("Button2"))

Comment on lines +207 to +208
// The submit-time error message to display when show_error is true.
string error_message = 4;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this cause any problems with the user changing their system language while the activity is open?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it won't. I tested this and the string language updates correctly for all errors on activity recreation.

Screen_recording_20260421_160640.webm

Comment on lines +2032 to +2033
exempted_file_path: "app/src/main/java/org/oppia/android/app/profile/CreateAdminPinActivityPresenter.kt"
test_file_not_required: true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: missing spacing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file does not use spacing between each item.

@oppiabot

oppiabot Bot commented Apr 3, 2026

Copy link
Copy Markdown

Unassigning @BenHenning since the review is done.

@oppiabot

oppiabot Bot commented Apr 3, 2026

Copy link
Copy Markdown

Hi @adhiamboperes, it looks like some changes were requested on this pull request by @BenHenning. PTAL. Thanks!

@oppiabot

This comment was marked as outdated.

@oppiabot oppiabot Bot added the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Apr 10, 2026
@adhiamboperes adhiamboperes removed the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Apr 14, 2026
@oppiabot

This comment was marked as outdated.

@oppiabot oppiabot Bot added the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Apr 28, 2026
@oppiabot oppiabot Bot closed this May 5, 2026
@adhiamboperes adhiamboperes reopened this May 11, 2026
@oppiabot

This comment was marked as outdated.

@github-actions

This comment was marked as outdated.

@oppiabot

This comment was marked as outdated.

1 similar comment
@oppiabot

This comment was marked as outdated.

# Conflicts:
#	app/src/main/java/org/oppia/android/app/onboarding/AdminIntroFragmentPresenter.kt
#	app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt
#	model/src/main/proto/screens.proto
#	utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt
@oppiabot

oppiabot Bot commented Sep 7, 2026

Copy link
Copy Markdown

Hi @adhiamboperes, I'm going to mark this PR as stale because it hasn't had any updates for 7 days. If no further activity occurs within 7 days, it will be automatically closed so that others can take up the issue.
If you are still working on this PR, please make a follow-up commit within 3 days (and submit it for review, if applicable). Please also let us know if you are stuck so we can help you! If you're unsure how to reassign this PR to a reviewer, please make sure to review the wiki page that details the Guidance on submitting PRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale Corresponds to items that haven't seen a recent update and may be automatically closed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants