From bcf252d3a6c6b324581ec1556937792ff8fa986e Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Sun, 25 Feb 2024 17:41:26 +0100 Subject: [PATCH] implement MotionView.eventTime and VelocityTracker This is needed to make androidx ViewPager work with touch input --- src/api-impl-jni/util.c | 2 +- src/api-impl-jni/views/android_view_View.c | 13 ++++---- src/api-impl/android/view/MotionEvent.java | 11 ++++--- .../android/view/VelocityTracker.java | 31 +++++++++++++++++-- .../android/view/ViewConfiguration.java | 13 +++++--- src/api-impl/android/widget/EdgeEffect.java | 1 + 6 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 727817fc..a12a2d19 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -83,7 +83,7 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.paint.getColor = _METHOD(handle_cache.paint.class, "getColor", "()I"); handle_cache.motion_event.class = _REF((*env)->FindClass(env, "android/view/MotionEvent")); - handle_cache.motion_event.constructor = _METHOD(handle_cache.motion_event.class, "", "(IIFF)V"); + handle_cache.motion_event.constructor = _METHOD(handle_cache.motion_event.class, "", "(IIFFJ)V"); handle_cache.canvas.class = _REF((*env)->FindClass(env, "android/graphics/Canvas")); handle_cache.canvas.constructor = _METHOD(handle_cache.canvas.class, "", "(JJ)V"); diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c index b9f054f7..db9b5da8 100644 --- a/src/api-impl-jni/views/android_view_View.c +++ b/src/api-impl-jni/views/android_view_View.c @@ -13,13 +13,13 @@ struct touch_callback_data { JavaVM *jvm; jobject this; jobject on_touch_listener; jclass on_touch_listener_class; bool intercepted; }; -static bool call_ontouch_callback(int action, double x, double y, struct touch_callback_data *d, GtkPropagationPhase phase) +static bool call_ontouch_callback(int action, double x, double y, struct touch_callback_data *d, GtkPropagationPhase phase, guint32 timestamp) { bool ret; JNIEnv *env; (*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6); - jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_TOUCHSCREEN, action, (float)x, (float)y); + jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_TOUCHSCREEN, action, (float)x, (float)y, (long)timestamp); if (phase == GTK_PHASE_CAPTURE && !d->intercepted) { d->intercepted = (*env)->CallBooleanMethod(env, d->this, handle_cache.view.onInterceptTouchEvent, motion_event); @@ -56,25 +56,26 @@ static gboolean on_event(GtkEventControllerLegacy *event_controller, GdkEvent *e GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(event_controller)); GtkPropagationPhase phase = gtk_event_controller_get_propagation_phase(GTK_EVENT_CONTROLLER(event_controller)); + guint32 timestamp = gdk_event_get_time(event); // TODO: this doesn't work for multitouch switch(gdk_event_get_event_type(event)) { case GDK_BUTTON_PRESS: case GDK_TOUCH_BEGIN: gdk_event_get_widget_relative_position(event, widget, &x, &y); - return call_ontouch_callback(MOTION_EVENT_ACTION_DOWN, x, y, d, phase); + return call_ontouch_callback(MOTION_EVENT_ACTION_DOWN, x, y, d, phase, timestamp); break; case GDK_BUTTON_RELEASE: case GDK_TOUCH_END: gdk_event_get_widget_relative_position(event, widget, &x, &y); - return call_ontouch_callback(MOTION_EVENT_ACTION_UP, x, y, d, phase); + return call_ontouch_callback(MOTION_EVENT_ACTION_UP, x, y, d, phase, timestamp); break; case GDK_MOTION_NOTIFY: if (!(gdk_event_get_modifier_state(event) & GDK_BUTTON1_MASK)) break; case GDK_TOUCH_UPDATE: gdk_event_get_widget_relative_position(event, widget, &x, &y); - return call_ontouch_callback(MOTION_EVENT_ACTION_MOVE, x, y, d, phase); + return call_ontouch_callback(MOTION_EVENT_ACTION_MOVE, x, y, d, phase, timestamp); break; default: break; @@ -107,7 +108,7 @@ static gboolean scroll_cb(GtkEventControllerScroll* self, gdouble dx, gdouble dy dx /= MAGIC_SCROLL_FACTOR; dy /= MAGIC_SCROLL_FACTOR; } - jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_CLASS_POINTER, MOTION_EVENT_ACTION_SCROLL, dx, -dy); + jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_CLASS_POINTER, MOTION_EVENT_ACTION_SCROLL, dx, -dy, (long)0); gboolean ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.onGenericMotionEvent, motion_event); if((*env)->ExceptionCheck(env)) diff --git a/src/api-impl/android/view/MotionEvent.java b/src/api-impl/android/view/MotionEvent.java index 68f565a5..ed7d08fc 100644 --- a/src/api-impl/android/view/MotionEvent.java +++ b/src/api-impl/android/view/MotionEvent.java @@ -1369,15 +1369,17 @@ public final class MotionEvent extends InputEvent { int action; float coord_x; float coord_y; + long eventTime; private MotionEvent() { } - public MotionEvent(int source, int action, float coord_x, float coord_y) { + public MotionEvent(int source, int action, float coord_x, float coord_y, long eventTime) { this.source = source; this.action = action; this.coord_x = coord_x; this.coord_y = coord_y; + this.eventTime = eventTime; } @Override @@ -1550,6 +1552,7 @@ public final class MotionEvent extends InputEvent { ev.action = action; ev.coord_x = x; ev.coord_y = y; + ev.eventTime = eventTime; return ev; } } @@ -1816,7 +1819,7 @@ public final class MotionEvent extends InputEvent { */ @Override public final long getEventTime() { - return System.currentTimeMillis(); + return eventTime; } /** @@ -1835,7 +1838,7 @@ public final class MotionEvent extends InputEvent { */ @Override public final long getEventTimeNano() { - return System.currentTimeMillis() * 1000; // FIXME + return eventTime * 1000; // FIXME } /** @@ -3030,7 +3033,7 @@ public final class MotionEvent extends InputEvent { msg.append(", edgeFlags=0x").append(/*Integer.toHexString(getEdgeFlags())*/ "FIXME"); msg.append(", pointerCount=").append(pointerCount); msg.append(", historySize=").append(/*getHistorySize()*/ "FIXME"); - msg.append(", eventTime=").append(/*getEventTime()*/ "FIXME"); + msg.append(", eventTime=").append(getEventTime()); msg.append(", downTime=").append(/*getDownTime()*/ "FIXME"); msg.append(", deviceId=").append(getDeviceId()); msg.append(", source=0x").append(Integer.toHexString(getSource())); diff --git a/src/api-impl/android/view/VelocityTracker.java b/src/api-impl/android/view/VelocityTracker.java index 08c677c5..54bb5848 100644 --- a/src/api-impl/android/view/VelocityTracker.java +++ b/src/api-impl/android/view/VelocityTracker.java @@ -6,15 +6,40 @@ public class VelocityTracker { return new VelocityTracker(); } - public void addMovement(MotionEvent event) {} + private float startX; + private float startY; + private long startEventTime; + private float currentX; + private float currentY; + private long currentEventTime; + + public void addMovement(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + startX = currentX = event.getX(); + startY = currentY = event.getY(); + startEventTime = currentEventTime = event.getEventTime(); + } else { + currentX = event.getX(); + currentY = event.getY(); + currentEventTime = event.getEventTime(); + } + } public void recycle() {} public void computeCurrentVelocity(int units, float maxVelocity) {} public void computeCurrentVelocity(int units) {} - public float getXVelocity(int id) {return 0.f;} - public float getYVelocity(int id) {return 0.f;} + public float getXVelocity(int id) { + if (currentEventTime == startEventTime) + return 0.f; + return (currentX - startX) / (currentEventTime - startEventTime) * 1000; + } + public float getYVelocity(int id) { + if (currentEventTime == startEventTime) + return 0.f; + return (currentY - startY) / (currentEventTime - startEventTime) * 1000; + } public void clear() {} } diff --git a/src/api-impl/android/view/ViewConfiguration.java b/src/api-impl/android/view/ViewConfiguration.java index b12e56f4..6a52ed30 100644 --- a/src/api-impl/android/view/ViewConfiguration.java +++ b/src/api-impl/android/view/ViewConfiguration.java @@ -2,6 +2,9 @@ package android.view; import android.content.Context; +/** + * default values are mainly based on AOSPs defaults. Does not account for scaling yet. + */ public class ViewConfiguration { public static ViewConfiguration get(Context context) { @@ -9,15 +12,15 @@ public class ViewConfiguration { } public int getScaledTouchSlop() { - return 0; + return 8; } public int getScaledMaximumFlingVelocity() { - return 0; + return 8000; } public int getScaledMinimumFlingVelocity() { - return 0; + return 50; } public static int getTapTimeout() { @@ -29,7 +32,7 @@ public class ViewConfiguration { } public int getScaledPagingTouchSlop(){ - return 0; + return 16; } public boolean hasPermanentMenuKey() { @@ -45,6 +48,6 @@ public class ViewConfiguration { } public static float getScrollFriction() { - return 0.f; + return 0.015f; } } diff --git a/src/api-impl/android/widget/EdgeEffect.java b/src/api-impl/android/widget/EdgeEffect.java index 2f6a8573..0ff74463 100644 --- a/src/api-impl/android/widget/EdgeEffect.java +++ b/src/api-impl/android/widget/EdgeEffect.java @@ -19,5 +19,6 @@ public class EdgeEffect extends View { public void onPull(float deltaDistance, float displacement) {} public boolean isFinished() {return true;} public void onRelease() {} + public void onAbsorb(int velocity) {} }