-
Notifications
You must be signed in to change notification settings - Fork 6k
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 non-vd android platform view input event offsets #52532
Changes from all commits
e51a149
3358956
dd40623
e09eeed
d1208fa
e560f49
d263563
00801ed
b91c1cd
dab6725
ad39109
616e8bf
356adf2
5e936b6
f118c61
e190144
90c65d9
3f6970b
4fec506
26ab85b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,6 @@ | |
|
||
package io.flutter.plugin.platform; | ||
|
||
import static android.view.MotionEvent.PointerCoords; | ||
import static android.view.MotionEvent.PointerProperties; | ||
import static io.flutter.Build.API_LEVELS; | ||
|
||
import android.annotation.TargetApi; | ||
|
@@ -668,32 +666,53 @@ public long configureForTextureLayerComposition( | |
return textureId; | ||
} | ||
|
||
/** | ||
* Translates an original touch event to have the same locations as the ones that Flutter | ||
* calculates (because original + flutter's - original = flutter's). | ||
* | ||
* @param originalEvent The saved original input event. | ||
* @param pointerCoords The coordinates that Flutter thinks the touch is happening at. | ||
*/ | ||
private static void translateMotionEvent( | ||
MotionEvent originalEvent, PointerCoords[] pointerCoords) { | ||
if (pointerCoords.length < 1) { | ||
return; | ||
} | ||
|
||
float xOffset = pointerCoords[0].x - originalEvent.getX(); | ||
float yOffset = pointerCoords[0].y - originalEvent.getY(); | ||
|
||
originalEvent.offsetLocation(xOffset, yOffset); | ||
} | ||
|
||
@VisibleForTesting | ||
public MotionEvent toMotionEvent( | ||
float density, PlatformViewsChannel.PlatformViewTouch touch, boolean usingVirtualDiplay) { | ||
MotionEventTracker.MotionEventId motionEventId = | ||
MotionEventTracker.MotionEventId.from(touch.motionEventId); | ||
MotionEvent trackedEvent = motionEventTracker.pop(motionEventId); | ||
|
||
// Pointer coordinates in the tracked events are global to FlutterView | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a deeper question about this change: If the original MotionEvent had view-local coordinates why can't we just deliver the original MotionEvent here? Let me explain below: When someone touches a Platform View the following happens:
My intuition would be that in step (1) the events in O have the correct coordinates. And then in (4) we deliver O to the view. In what cases do we see a non-zero offset? Help me understand intuitively what's happening here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is something weird going on here for sure. The reporting issue has a repro, which includes an example of a temporary fix that forces the platform view to refresh. In investigating why this temporarily fixes the issue, I printed out the original motion events and our calculated locations of platform view touches (and some more locations from framework code). It turns out refreshing the webview changes the location of the original motion event that we store. Still need to investigate further as to why this would be the case, but it wasn't what I was originally expecting at all. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like probably the reason why we can't deliver the motion events as is in (4) is that the Platform view in question believes it is at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I think I actually know the answer here now. When we deliver the motion event in (4), we are then delivering it to an inner view, and as such need to make it relative to the inner view, which is why we need to offset. Does that make sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Talked with @johnmccutchan offline and we still aren't sure this reasoning is 100% correct, but landing regardless as the fix works better than what we have) |
||
// The framework converts them to be local to a widget, given that | ||
// motion events operate on local coords, we need to replace these in the tracked | ||
// event with their local counterparts. | ||
// Compute this early so it can be used as input to translateNonVirtualDisplayMotionEvent. | ||
PointerCoords[] pointerCoords = | ||
parsePointerCoordsList(touch.rawPointerCoords, density) | ||
.toArray(new PointerCoords[touch.pointerCount]); | ||
|
||
if (!usingVirtualDiplay && trackedEvent != null) { | ||
// We have the original event, deliver it as it will pass the verifiable | ||
// We have the original event, deliver it after offsetting as it will pass the verifiable | ||
// input check. | ||
translateMotionEvent(trackedEvent, pointerCoords); | ||
return trackedEvent; | ||
} | ||
// We are in virtual display mode or don't have a reference to the original MotionEvent. | ||
// In this case we manually recreate a MotionEvent to be delivered. This MotionEvent | ||
// will fail the verifiable input check. | ||
|
||
// Pointer coordinates in the tracked events are global to FlutterView | ||
// framework converts them to be local to a widget, given that | ||
// motion events operate on local coords, we need to replace these in the tracked | ||
// event with their local counterparts. | ||
PointerProperties[] pointerProperties = | ||
parsePointerPropertiesList(touch.rawPointerPropertiesList) | ||
.toArray(new PointerProperties[touch.pointerCount]); | ||
PointerCoords[] pointerCoords = | ||
parsePointerCoordsList(touch.rawPointerCoords, density) | ||
.toArray(new PointerCoords[touch.pointerCount]); | ||
|
||
// TODO (kaushikiska) : warn that we are potentially using an untracked | ||
// event in the platform views. | ||
|
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.
Could we add a roboelectric test programmatically invokes translateNonVirtualDisplayMotionEvent and toMotionEvent and checks their return values?
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.
I wrote a test that invokes toMotionEvent in both the vd and non-vd case, and expects that they have equivalent x and y coords. This isn't exactly what you asked for, but does it make sense to you?