Skip to content

Commit 5f3e75a

Browse files
Fix #5736, Fix #5721, Fix 5954, Fix 5636 and Fix part of #5800: 0.16 beta bug fixes (#5831)
<!-- READ ME FIRST: Please fill in the explanation section below and check off every point from the Essential Checklist! --> ## Explanation Fix #5736: The last character in the list is truncated because https://github.com/oppia/oppia-android/blob/cda4f44e36222fae5fc8672c774cc8c281f97156/utility/src/main/java/org/oppia/android/util/parser/html/HtmlParser.kt#L179 deletes 2 characters from the end if the text ends with a single `\n`, which is too aggressive. This means it deletes the last character (4) and the \n, when it should only delete the \n. The safest solution is to trim all leading and trailing `\n` characters, not just one. That way, whether there’s 1, 2, or 10 newlines at the edges, they’ll be removed without touching non-\n characters. |Before|After| |--|--| |![Image](https://github.com/user-attachments/assets/c43a8089-ff53-486f-94d6-0fc07ae9b37b)|![image](https://github.com/user-attachments/assets/7cca97e7-9496-44da-9333-854954975fd9)| Fix #5721: Per @subhajitxyz's proposed solution in #5721 (comment), We need to calculate the width of the text outside of `drawLeadingMargin()`, so that `getLeadingMargin()` can return the correct computedLeadingMargin independently. We do this by passing a reference of the TextView to the tag handler so that we can use it to construct a matching `TextPaint` object and dynamically calculate the prefix width (e.g., "1.", "10.") using its font, text size, and other styling. This will allow us to decouple `getLeadingMargin()` from `drawLeadingMargin()` and compute the margin correctly during layout. ||| |--|--| |![Image](https://github.com/user-attachments/assets/f0e98f58-301c-4eef-898d-93062604baf1)| ![Screenshot_2025-09-04-23-43-44-057_org oppia android](https://github.com/user-attachments/assets/a9c4e003-c494-4302-842c-b9edf1e52144)| |![Image](https://github.com/user-attachments/assets/7c4682a2-2542-403f-aeaa-c2cc1a87765a)|![Screenshot_2025-09-04-23-40-19-274_org oppia android](https://github.com/user-attachments/assets/20453581-77de-4201-8944-c91acd23c61a)| Fix #5636 The developer options and admin controls menu item faailed to reopen because of respective variables in `NavigationDrawerFragmentPresenter` being set to true to indicate that they are already open, as well as forcing the drawer to close. Unfortunately, tests for repeated opening are blocked by known issue: #2535. Fix #5954 This bug is caused by the survey being shown AFTER the exploration has been stopped, so attempting to play the exploration results into the frozen state. The user is still able to exit the exploration normally by clicking the close or return to topic buttons again, but this creates an inconsistent UX. The bug is fixed by navigating the user away from the ExplorationActivity if they decline the survey. Some minor chnages have been made to the survey code to remove some redundancies. |Before|After| |--|--| |https://github.com/user-attachments/assets/5bed7d7b-6d78-427a-a105-86fc0f15fd08|https://github.com/user-attachments/assets/cb6f042f-3e2c-4ae7-b972-75cf08a2f137| Fix part of #5800 The survey gets dismissed due to the gating provider updating as a result of the combined profile DataProvider updates. Immediately the survey popup is show, the `surveyLastShownTimestampMs` field of a profile gets update, and this updates downstream providers that are subscribed to the profile provider. This has been fixed by removing the observer to the gating provider once it has returned the first time.vThis happened only on clicking "return to question". This was not possible to test in `StateFragmentTest` because of issues with dragAndDrop on robolectric, preventing playing through and exploration. I spent a considerable amount of time trying to set up the tst using the Ratios exploration, but experienced difficulty with item selection in state 19 -- the images are large and scrolling is needed to see and click all the options, but I was not able to make this work at this time. So keeping the issue open to track adding tests either buy fixing the ratios playthrough, or the drag and drop issue. |Before|After| |--|--| |. |https://github.com/user-attachments/assets/cb6f042f-3e2c-4ae7-b972-75cf08a2f137| The PR also bumps versions for the release. ## Essential Checklist <!-- Please tick the relevant boxes by putting an "x" in them. --> - [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)).
1 parent d4d99d4 commit 5f3e75a

File tree

17 files changed

+344
-93
lines changed

17 files changed

+344
-93
lines changed

app/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ LISTENERS = [
145145
"src/main/java/org/oppia/android/app/recyclerview/OnItemDragListener.kt",
146146
"src/main/java/org/oppia/android/app/settings/profile/LoadProfileEditDeletionDialogListener.kt",
147147
"src/main/java/org/oppia/android/app/settings/profile/RouteToProfileEditListener.kt",
148+
"src/main/java/org/oppia/android/app/survey/DismissSurveyListener.kt",
148149
"src/main/java/org/oppia/android/app/survey/SelectedAnswerAvailabilityReceiver.kt",
149150
"src/main/java/org/oppia/android/app/topic/RouteToRevisionCardListener.kt",
150151
"src/main/java/org/oppia/android/app/topic/lessons/ChapterSummarySelector.kt",

app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,6 @@ class NavigationDrawerFragmentPresenter @Inject constructor(
106106
return@setOnClickListener
107107
}
108108
uncheckAllMenuItemsWhenAdministratorControlsOrDeveloperOptionsIsSelected()
109-
drawerLayout.closeDrawers()
110-
footerViewModel.isDeveloperOptionsSelected.set(true)
111109
val intent = starter.createIntent(activity, profileId)
112110
fragment.requireActivity().startActivity(intent)
113111
if (previousMenuItemId == 0) fragment.requireActivity().finish()
@@ -141,8 +139,6 @@ class NavigationDrawerFragmentPresenter @Inject constructor(
141139

142140
uncheckAllMenuItemsWhenAdministratorControlsOrDeveloperOptionsIsSelected()
143141

144-
drawerLayout.closeDrawers()
145-
footerViewModel.isAdministratorControlsSelected.set(true)
146142
val intent =
147143
AdministratorControlsActivity.createAdministratorControlsActivityIntent(
148144
activity,

app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import org.oppia.android.app.player.state.listener.FlashbackToolbarListener
2424
import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListener
2525
import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener
2626
import org.oppia.android.app.player.stopplaying.StopStatePlayingSessionWithSavedProgressListener
27+
import org.oppia.android.app.survey.DismissSurveyListener
28+
import org.oppia.android.app.survey.TAG_SURVEY_WELCOME_DIALOG
2729
import org.oppia.android.app.topic.conceptcard.ConceptCardListener
2830
import org.oppia.android.util.extensions.getProtoExtra
2931
import org.oppia.android.util.extensions.putProtoExtra
@@ -49,7 +51,8 @@ class ExplorationActivity :
4951
ConceptCardListener,
5052
BottomSheetOptionsMenuItemClickListener,
5153
RequestVoiceOverIconSpotlightListener,
52-
FlashbackToolbarListener {
54+
FlashbackToolbarListener,
55+
DismissSurveyListener {
5356

5457
@Inject lateinit var explorationActivityPresenter: ExplorationActivityPresenter
5558
private lateinit var state: State
@@ -220,4 +223,14 @@ class ExplorationActivity :
220223
override fun showFlashbackToolbar() {
221224
explorationActivityPresenter.showFlashbackToolbar()
222225
}
226+
227+
override fun dismissSurvey() {
228+
val previousFragment = supportFragmentManager
229+
.findFragmentByTag(TAG_SURVEY_WELCOME_DIALOG)
230+
231+
if (previousFragment != null) {
232+
supportFragmentManager.beginTransaction().remove(previousFragment).commitNow()
233+
explorationActivityPresenter.backPressActivitySelector()
234+
}
235+
}
223236
}

app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ class ExplorationActivityPresenter @Inject constructor(
393393
}
394394
}
395395

396-
private fun backPressActivitySelector() {
396+
/** Selects the appropriate way to close the activity based on the parent screen. */
397+
fun backPressActivitySelector() {
397398
when (parentScreen) {
398399
ExplorationActivityParams.ParentScreen.TOPIC_SCREEN_LESSONS_TAB,
399400
ExplorationActivityParams.ParentScreen.STORY_SCREEN -> activity.finish()
@@ -576,20 +577,23 @@ class ExplorationActivityPresenter @Inject constructor(
576577
}
577578
is AsyncResult.Success -> {
578579
if (gatingResult.value) {
580+
oppiaLogger.d(
581+
"ExplorationActivity", "Successfully retrieved gating decision"
582+
)
579583
val dialogFragment =
580584
SurveyWelcomeDialogFragment.newInstance(
581585
profileId,
582586
topicId,
583587
explorationId,
584588
SURVEY_QUESTIONS
585589
)
586-
val transaction = activity.supportFragmentManager.beginTransaction()
587-
transaction
590+
activity.supportFragmentManager
591+
.beginTransaction()
588592
.add(dialogFragment, TAG_SURVEY_WELCOME_DIALOG)
589-
.addToBackStack(null)
590-
.commit()
593+
.commitNow()
591594

592-
// Changes to underlying DataProviders will update the gating result.
595+
// Changes to underlying DataProviders will update the gating result,
596+
// which can interrupt the survey dialog.
593597
liveData.removeObserver(this)
594598
} else {
595599
backPressActivitySelector()

app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -576,39 +576,49 @@ class StateFragmentPresenter @Inject constructor(
576576
}
577577

578578
private fun maybeShowSurveyDialog(profileId: ProfileId, topicId: String) {
579-
surveyGatingController.maybeShowSurvey(profileId, topicId).toLiveData().observe(
579+
val liveData = surveyGatingController.maybeShowSurvey(profileId, topicId).toLiveData()
580+
liveData.observe(
580581
activity,
581-
{ gatingResult ->
582-
when (gatingResult) {
583-
is AsyncResult.Pending -> {
584-
oppiaLogger.d("StateFragment", "A gating decision is pending")
585-
}
586-
is AsyncResult.Failure -> {
587-
oppiaLogger.e(
588-
"StateFragment",
589-
"Failed to retrieve gating decision",
590-
gatingResult.error
591-
)
592-
(activity as StopStatePlayingSessionWithSavedProgressListener)
593-
.deleteCurrentProgressAndStopSession(isCompletion = true)
594-
}
595-
is AsyncResult.Success -> {
596-
if (gatingResult.value) {
597-
val dialogFragment =
598-
SurveyWelcomeDialogFragment.newInstance(
599-
profileId,
600-
topicId,
601-
explorationId,
602-
SURVEY_QUESTIONS
603-
)
604-
val transaction = activity.supportFragmentManager.beginTransaction()
605-
transaction
606-
.add(dialogFragment, TAG_SURVEY_WELCOME_DIALOG)
607-
.commitNow()
608-
} else {
582+
object : Observer<AsyncResult<Boolean>> {
583+
override fun onChanged(gatingResult: AsyncResult<Boolean>?) {
584+
when (gatingResult) {
585+
null, is AsyncResult.Pending -> {
586+
oppiaLogger.d("StateFragment", "A gating decision is pending")
587+
}
588+
589+
is AsyncResult.Failure -> {
590+
oppiaLogger.e(
591+
"StateFragment",
592+
"Failed to retrieve gating decision",
593+
gatingResult.error
594+
)
609595
(activity as StopStatePlayingSessionWithSavedProgressListener)
610596
.deleteCurrentProgressAndStopSession(isCompletion = true)
611597
}
598+
599+
is AsyncResult.Success -> {
600+
oppiaLogger.d("StateFragment", "Successfully retrieved gating decision")
601+
if (gatingResult.value) {
602+
val dialogFragment =
603+
SurveyWelcomeDialogFragment.newInstance(
604+
profileId,
605+
topicId,
606+
explorationId,
607+
SURVEY_QUESTIONS
608+
)
609+
val transaction = activity.supportFragmentManager.beginTransaction()
610+
transaction
611+
.add(dialogFragment, TAG_SURVEY_WELCOME_DIALOG)
612+
.commitNow()
613+
614+
// Changes to underlying DataProviders will update the gating result,
615+
// which can interrupt the survey dialog.
616+
liveData.removeObserver(this)
617+
} else {
618+
(activity as StopStatePlayingSessionWithSavedProgressListener)
619+
.deleteCurrentProgressAndStopSession(isCompletion = true)
620+
}
621+
}
612622
}
613623
}
614624
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.oppia.android.app.survey
2+
3+
/** Listener for when a user clicks the Maybe Later button in a survey popup. */
4+
interface DismissSurveyListener {
5+
/** Called when the user clicks the Maybe Later button in a survey popup. */
6+
fun dismissSurvey()
7+
}

app/src/main/java/org/oppia/android/app/survey/ExitSurveyConfirmationDialogFragment.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,4 @@ class ExitSurveyConfirmationDialogFragment : InjectableDialogFragment() {
5555
dialog?.setCancelable(false)
5656
return exitSurveyConfirmationDialogFragmentPresenter.handleCreateView(inflater, container)
5757
}
58-
59-
override fun onStart() {
60-
super.onStart()
61-
dialog?.window?.setWindowAnimations(R.style.ExitSurveyConfirmationDialogStyle)
62-
}
6358
}

app/src/main/java/org/oppia/android/app/survey/SurveyWelcomeDialogFragment.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ class SurveyWelcomeDialogFragment : InjectableDialogFragment() {
8888
val explorationId = args.explorationId!!
8989
val surveyQuestions = getQuestions(args.questionsList)
9090

91+
dialog?.setCanceledOnTouchOutside(false)
92+
dialog?.setCancelable(false)
93+
9194
return surveyWelcomeDialogFragmentPresenter.handleCreateView(
9295
inflater,
9396
container,
@@ -101,9 +104,4 @@ class SurveyWelcomeDialogFragment : InjectableDialogFragment() {
101104
private fun getQuestions(questionNumberList: List<Int>): List<SurveyQuestionName> {
102105
return questionNumberList.map { number -> SurveyQuestionName.forNumber(number) }
103106
}
104-
105-
override fun onStart() {
106-
super.onStart()
107-
dialog?.window?.setWindowAnimations(R.style.SurveyOnboardingDialogStyle)
108-
}
109107
}

app/src/main/java/org/oppia/android/app/survey/SurveyWelcomeDialogFragmentPresenter.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class SurveyWelcomeDialogFragmentPresenter @Inject constructor(
3030
private val profileManagementController: ProfileManagementController
3131
) {
3232
private lateinit var explorationId: String
33+
private val dismissSurveyListener = activity as DismissSurveyListener
3334

3435
/** Sets up data binding. */
3536
fun handleCreateView(
@@ -51,9 +52,7 @@ class SurveyWelcomeDialogFragmentPresenter @Inject constructor(
5152
}
5253

5354
binding.maybeLaterButton.setOnClickListener {
54-
activity.supportFragmentManager.beginTransaction()
55-
.remove(fragment)
56-
.commitNow()
55+
dismissSurveyListener.dismissSurvey()
5756
}
5857

5958
profileManagementController.updateSurveyLastShownTimestamp(profileId)
@@ -90,7 +89,7 @@ class SurveyWelcomeDialogFragmentPresenter @Inject constructor(
9089
fragment.startActivity(intent)
9190
activity.finish()
9291
val transaction = activity.supportFragmentManager.beginTransaction()
93-
transaction.remove(fragment).commitAllowingStateLoss()
92+
transaction.remove(fragment).commitNow()
9493
}
9594
}
9695
}

0 commit comments

Comments
 (0)