-
Notifications
You must be signed in to change notification settings - Fork 305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix cursor moving back and missing characters #2537
Conversation
This, however, has a side effect (which existed earlier as well but was overlooked because of cursor resetting) of missing some characters when typing too fast. Solving this will require more effort. I can think of a solution which will involve detecting "user has stopped typing". Something like this - stackoverflow. Let me know if you want to fix this as well. Will raise a separate issue for this. Alternative: Use RxBindings or do some backpressure solutioning to handle all character inputs from user. But this will need a more complex handling - like "not updating the EditText until upstream processing (handleInput) is complete". |
@jingtang10 what are your thoughts on this? |
I made an attempt at solving the "side effect" as well. Seems good to me but Idk if this behaviour - "detect user has stopped typing" can have any other side effects. Only difficulty is to test this as racing conditions are always a pain to test. Can you try testing this PR ? |
Thanks for all your work on this @MJ1998. @FikriMilano will test the PR tomorrow and we'll get back to you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @MJ1998, I like your first idea and it's working, in a nutshell -> only apply new answer to questionnaireViewItem after user stops typing for 0.5 second, if there are more inputs coming in, the answer won't be applied since the timer is canceled.
Not sure if it's worth it to introduce RxBinding right now.
override fun afterTextChanged(editable: Editable?) {
timer?.cancel()
// If editable has become empty or this is the first character then invoke afterTextChanged
// instantly else start a timer for user to finish typing
if (editable?.toString().isNullOrEmpty() || firstCharacter) {
Log.d("FIKRI1", "$editable")
afterTextChanged(editable?.toString())
} else {
// countDownInterval is simply kept greater than delay as we don't need onTick
timer =
object : CountDownTimer(delay, delay * 2) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
Log.d("FIKRI2", "$editable")
afterTextChanged(editable?.toString())
}
}
.start()
}
}
2024-05-09 11:24:26.372 2316-2316 FIKRI1 com.google.android.fhir.catalog D
2024-05-09 11:24:27.912 2316-2316 FIKRI1 com.google.android.fhir.catalog D a
2024-05-09 11:24:30.037 2316-2316 FIKRI2 com.google.android.fhir.catalog D aa
2024-05-09 11:24:35.997 2316-2316 FIKRI2 com.google.android.fhir.catalog D aaqwertqwertqwertqwertqwert
qwerty.webm
Just FYI an elaboration on how I am testing this:- You can observe some characters being missed before the last commit if you test like this. @FikriMilano In actual case the upstream function can take more time to finish (for example, resolving fhir query) - which makes characters missing clearly visible. |
Regarding your question on RxBinding, I think its a much bigger effort as we dont use simple TextView or EditText views, rather its a EditTextInputLayout which offers more features like validation, header views along with it. So moving from EditTextInputLayout to RxBinding views will be difficult. |
@MJ1998 I agree, we can keep it this way for now |
@MJ1998 this fix you shared is working great. Thank you for prioritising this and getting it resolved so quickly! |
@MJ1998 upon further testing, the issue of the missing characters is significantly affecting the data entry. Missing.letters.bug.webmCan we please go back to the drawing board on this? |
Is this issue happening on this PR? |
@MJ1998 yes it is. |
Gotcha. I had a feeling this will introduce more side effects. |
@MJ1998 there's none, it's just a normal text field |
Can you try increasing the DELAY TIME to 1000 or 2000 milli seconds and see if this is happening ? |
@MJ1998 after investigation from my end, the cursor issue only happens in one of our project where it has some additional features that the Android FHIR SDK doesn't have yet (I do plan to push them to this repo, with finalization). This PR's change seems to not work well with those additional features, so the fix will come from my end. After reverting some of our additional features, the cursor issue is gone. Plus, when I try the catalog app, the cursor issue doesn't happen. Your solution is likely already correct. @f-odhiambo @ndegwamartin for the main FHIRCore branch, feel free to pull this change, the issue should not happen since main doesn't have additional feature which I refer before. |
@MJ1998 but yeah, we will try it in our main FHIRCore branch then get back to you on the result. |
- With unmerged PR #9 - WUP PR google#2178 - WUP PR google#2511 - WUP PR google#2537
@MJ1998 in the main branch, the solution works on my end, but it's showing a glitch in @f-odhiambo device. screen-20240528-152054.mp4 |
@MJ1998 could you try my other solution? (a different one, this won't break calculatedExpression) It's a rough solution, but the idea is to prevent unwanted re-binding by only bind the text from questionnaireViewItem, if the text is different compared to before the re-binding. Highlight of the change:
More details:
bug-fix.mov |
Can you try this solution in @f-odhiambo device and confirm if it works ? |
Also NVM about that, I was testing it again with more random words, and it failed the tests... |
Context: User input is sent back to QuestionnaireViewModel for validation, updating dependent items and then building the RecyclerView list -> which then comes back to the EditViewHolderFactory. |
yay, awesome, thank you @MJ1998 🎉 ! |
- With unmerged PR #9 - WUP PR google#2178 - WUP PR google#2511 - WUP PR google#2537 - WUP PR google#2511
@MJ1998 awesome work! 🚀 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approach taken to fix the issue looks fine and the changes look good as well.
Just a comment to add Unit test to make sure that the code keeps working as expected.
...c/main/java/com/google/android/fhir/datacapture/views/factories/EditTextViewHolderFactory.kt
Show resolved
Hide resolved
thanks jajoo for this! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks @MJ1998 !
IMPORTANT: All PRs must be linked to an issue (except for extremely trivial and straightforward changes).
Fixes #1914
Cause of the issue: Sometimes processing of input is slower than the typing speed. This results into inconsistency between the answer stored in the QuestionnaireViewItem and the current text in the EditText. The UI is updated everytime the QuestionnaireViewItem is bound. So here we see the inconsistency where we use EditText#setText which does 2 things: Sets the EditText content to the answer in QuestionnaireViewItem (leading to missing character) AND also sets the cursor to beginning of the input.
Solution:-
The EditText temporarily contains an extra character. The TextWatcher gets removed (here) before it can process this extra character, leaving the saved text state (in QuestionnaireViewItem) inconsistent with the actual EditText content. This inconsistency triggers a UI update using EditText#setText (here), which unintentionally resets the cursor position to the beginning.Read comments in rest of the PR for more details.Description
Clear and concise code change description.
Alternative(s) considered
Have you considered any alternatives? And if so, why have you chosen the approach in this PR?
Type
Choose one: (Bug fix | Feature | Documentation | Testing | Code health | Builds | Releases | Other)
Screenshots (if applicable)
Checklist
./gradlew spotlessApply
and./gradlew spotlessCheck
to check my code follows the style guide of this project../gradlew check
and./gradlew connectedCheck
to test my changes locally.