fix: take gravity
into consideration when calculating selection coordinates on Android
#749
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
📜 Description
Fixed wrong selection coordinates on Android.
💡 Motivation and Context
The main idea behind this PR is to make selection coordinates consistent between iOS and Android. On Android we can specify
textAlignVertical
asbottom
/top
/center
and it will affect position of the text inside input. Thus if input height is significantly bigger that text height, theny
coordinates will be wrong.To take
y
position into consideration I decided to use next code:In this case we take a value of
textAlignVertical
and we can decide which additional offset should be applied.While it works this change comes with new challenges - since we depend on new variables (such as
editTextHeight
andtextHeight
) and these variables can be updated asynchronously (textHeight
can have an updated value, buteditTextHeight
might be updated later), we need to take it into consideration and don't send possibly corrupted events. Below are approaches that I considered to use:1️⃣ Delaying gathering coords if layout changes its height
Theoretically I could add a condition like:
But turns out that for updating container layout sometimes it requires 2 frames to actually update layout 🤷♂️ And skipping two frames already gives a noticeable delay in terms of visual responsiveness.
2️⃣ Measuring editText to read actual height
The second approach I've been thinking of is using
measure
to actually get theheight
of container. While it works sometimesmeasure
returns wrong layout metrics. For example wheneditText
is very big it'll give a smallerheight
than it actually is. We could use something likemax(editText.height, measuredHeight)
, but when text input shrinks, then it will also produce incorrect metric and will result in incorrect coordinates.Note
To use measure without affecting layout I used next code
3️⃣ Use
OnPreDraw
listenerThe only one approach I've been thinking of was a continuation of using
FrameScheduler
and adding a new variable when we should re-execute handler (i. e. cacheeditText.height
and addlastEditTextHeight
). This approach works perfectly and catches a situation, when height is desynchronised and dispatches correct events afterwards. However we almost always will dispatch 2 events when layout changes its height.To overcome this problem I decided to use
OnPreDraw
listener instead ofFrameScheduler
(Choreographer). And it gives much better results (becauseOnPreDraw
runs after layout calculation, so we have better chances to read an actual value):Screen.Recording.2025-01-03.at.10.12.50.mov
Screen.Recording.2025-01-03.at.10.18.36.mov
However turns out, that Android may still schedule layout updates in two frames 🤯 So I couldn't get rid off
lastEditTextHeight
variable.OnPreDraw
gives much better result in terms of synchronization, butlastEditTextHeight
check still needed to work properly.📢 Changelog
E2E
getAttributes
seems to be not very stable on Android and caused flakiness.Android
OnPreDraw
listener instead ofFrameScheduler
to read more actual value ofheight
;gravity
into consideration;getLineBottom
/getLineTop
without math operations withascendant
/descendant
;editTextHeight
along withstart
/end
;layout === null
check before updating cached values.🤔 How Has This Been Tested?
Tested manually on Medium Phone API 35 and via e2e on API 28 and API 31.
📸 Screenshots (if appropriate):
📝 Checklist