diff --git a/src/api-impl-jni/generated_headers/android_view_ViewGroup.h b/src/api-impl-jni/generated_headers/android_view_ViewGroup.h
index c60d66f9..f5ac7150 100644
--- a/src/api-impl-jni/generated_headers/android_view_ViewGroup.h
+++ b/src/api-impl-jni/generated_headers/android_view_ViewGroup.h
@@ -231,6 +231,14 @@ JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1drawChildren
JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1drawChild
(JNIEnv *, jobject, jlong, jlong, jlong);
+/*
+ * Class: android_view_ViewGroup
+ * Method: native_dispatchTouchEvent
+ * Signature: (JLandroid/view/MotionEvent;DD)Z
+ */
+JNIEXPORT jboolean JNICALL Java_android_view_ViewGroup_native_1dispatchTouchEvent
+ (JNIEnv *, jobject, jlong, jobject, jdouble, jdouble);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c
index 8fa829df..ba1716be 100644
--- a/src/api-impl-jni/util.c
+++ b/src/api-impl-jni/util.c
@@ -132,6 +132,7 @@ void set_up_handle_cache(JNIEnv *env)
handle_cache.view.getScrollY = _METHOD(handle_cache.view.class, "getScrollY", "()I");
handle_cache.view.performClick = _METHOD(handle_cache.view.class, "performClick", "()Z");
handle_cache.view.onTouchEvent = _METHOD(handle_cache.view.class, "onTouchEvent", "(Landroid/view/MotionEvent;)Z");
+ handle_cache.view.onTouchEventInternal = _METHOD(handle_cache.view.class, "onTouchEventInternal", "(Landroid/view/MotionEvent;)Z");
handle_cache.view.dispatchTouchEvent = _METHOD(handle_cache.view.class, "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z");
handle_cache.view.onInterceptTouchEvent = _METHOD(handle_cache.view.class, "onInterceptTouchEvent", "(Landroid/view/MotionEvent;)Z");
handle_cache.view.layoutInternal = _METHOD(handle_cache.view.class, "layoutInternal", "(II)V");
@@ -143,6 +144,9 @@ void set_up_handle_cache(JNIEnv *env)
handle_cache.view.dispatchKeyEvent = _METHOD(handle_cache.view.class, "dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z");
handle_cache.view.onKeyDown = _METHOD(handle_cache.view.class, "onKeyDown", "(ILandroid/view/KeyEvent;)Z");
+ handle_cache.view_group.class = _REF((*env)->FindClass(env, "android/view/ViewGroup"));
+ handle_cache.view_group.dispatchTouchEvent = _METHOD(handle_cache.view_group.class, "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z");
+
handle_cache.asset_manager.class = _REF((*env)->FindClass(env, "android/content/res/AssetManager"));
handle_cache.asset_manager.extractFromAPK = _STATIC_METHOD(handle_cache.asset_manager.class, "extractFromAPK", "(Ljava/lang/String;Ljava/lang/String;)V");
diff --git a/src/api-impl-jni/util.h b/src/api-impl-jni/util.h
index a8f92048..5d343344 100644
--- a/src/api-impl-jni/util.h
+++ b/src/api-impl-jni/util.h
@@ -77,6 +77,7 @@ struct handle_cache {
jmethodID getScrollY;
jmethodID performClick;
jmethodID onTouchEvent;
+ jmethodID onTouchEventInternal;
jmethodID dispatchTouchEvent;
jmethodID onInterceptTouchEvent;
jmethodID layoutInternal;
@@ -88,6 +89,10 @@ struct handle_cache {
jmethodID dispatchKeyEvent;
jmethodID onKeyDown;
} view;
+ struct {
+ jclass class;
+ jmethodID dispatchTouchEvent;
+ } view_group;
struct {
jclass class;
jmethodID extractFromAPK;
diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c
index f6e4eea3..191167cc 100644
--- a/src/api-impl-jni/views/android_view_View.c
+++ b/src/api-impl-jni/views/android_view_View.c
@@ -46,11 +46,40 @@ static WrapperWidget *cancel_triggerer = NULL;
static struct pointer pointers[MAX_POINTERS] = {};
+bool view_dispatch_motionevent(JNIEnv *env, WrapperWidget *wrapper, GtkPropagationPhase phase, jobject motion_event, GdkEvent *event) {
+ int ret;
+
+ jobject this = wrapper->jobj;
+
+ if (wrapper->custom_dispatch_touch) {
+ ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.dispatchTouchEvent, motion_event);
+ } else if (phase == GTK_PHASE_CAPTURE && !wrapper->intercepting_touch) {
+ wrapper->intercepting_touch = (*env)->CallBooleanMethod(env, this, handle_cache.view.onInterceptTouchEvent, motion_event);
+ if (wrapper->intercepting_touch) {
+ if(event) {
+ // store the event that was canceled and let it propagate to the child widgets
+ canceled_event = event;
+ cancel_triggerer = wrapper;
+ } else {
+ /* this function is also called to synthesize an event, in which case there is no GdkEvent so not sure what to do */
+ fprintf(stderr, "view_dispatch_motionevent: onInterceptTouchEvent returned true but this is a synthesized event, please investigate\n");
+ }
+ }
+ ret = false;
+ } else {
+ ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.onTouchEventInternal, motion_event);
+ }
+
+ if((*env)->ExceptionCheck(env))
+ (*env)->ExceptionDescribe(env);
+
+ return ret;
+}
+
static bool call_ontouch_callback(WrapperWidget *wrapper, int action, struct pointer pointers[MAX_POINTERS], GPtrArray *pointer_indices, GtkPropagationPhase phase, guint32 timestamp, GdkEvent *event)
{
bool ret;
JNIEnv *env = get_jni_env();
- jobject this = wrapper->jobj;
int num_pointers = pointer_indices->len;
jintArray ids = (*env)->NewIntArray(env, num_pointers);
@@ -62,24 +91,9 @@ static bool call_ontouch_callback(WrapperWidget *wrapper, int action, struct poi
(*env)->SetFloatArrayRegion(env, coords, 4 * i, 4, &pointer->coord_x);
}
- jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_TOUCHSCREEN, action, (long)timestamp, ids, coords);
+ jobject motion_event = (*env)->NewObject(env, handle_cache.motion_event.class, handle_cache.motion_event.constructor, SOURCE_TOUCHSCREEN, action, timestamp, ids, coords);
- if (wrapper->custom_dispatch_touch) {
- ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.dispatchTouchEvent, motion_event);
- } else if (phase == GTK_PHASE_CAPTURE && !wrapper->intercepting_touch) {
- wrapper->intercepting_touch = (*env)->CallBooleanMethod(env, this, handle_cache.view.onInterceptTouchEvent, motion_event);
- if (wrapper->intercepting_touch) {
- // store the event that was canceled and let it propagate to the child widgets
- canceled_event = event;
- cancel_triggerer = wrapper;
- }
- ret = false;
- } else {
- ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.onTouchEvent, motion_event);
- }
-
- if((*env)->ExceptionCheck(env))
- (*env)->ExceptionDescribe(env);
+ ret = view_dispatch_motionevent(env, wrapper, phase, motion_event, event);
(*env)->DeleteLocalRef(env, motion_event);
diff --git a/src/api-impl-jni/views/android_view_ViewGroup.c b/src/api-impl-jni/views/android_view_ViewGroup.c
index 5cf6996c..d8be5d43 100644
--- a/src/api-impl-jni/views/android_view_ViewGroup.c
+++ b/src/api-impl-jni/views/android_view_ViewGroup.c
@@ -63,3 +63,103 @@ JNIEXPORT void JNICALL Java_android_view_ViewGroup_native_1drawChild(JNIEnv *env
gtk_widget_queue_draw(child); // FIXME: why didn't compose UI invalidate the child?
gtk_widget_snapshot_child(widget, child, snapshot);
}
+
+/* FIXME: put this in a header */
+G_DECLARE_FINAL_TYPE(JavaWidget, java_widget, JAVA, WIDGET, GtkWidget)
+bool view_dispatch_motionevent(JNIEnv *env, WrapperWidget *wrapper, GtkPropagationPhase phase, jobject motion_event, GdkEvent *event);
+
+static bool dispatch_motionevent_if_JavaWidget(GtkWidget *widget, GtkPropagationPhase phase, jobject motion_event)
+{
+ if(!JAVA_IS_WIDGET(widget))
+ return false;
+
+ return view_dispatch_motionevent(get_jni_env(), WRAPPER_WIDGET(gtk_widget_get_parent(widget)), phase, motion_event, NULL);
+}
+
+/* used by atl_propagate_synthetic_motionevent */
+#define GDK_ARRAY_ELEMENT_TYPE GtkWidget *
+#define GDK_ARRAY_TYPE_NAME GtkWidgetStack
+#define GDK_ARRAY_NAME gtk_widget_stack
+#define GDK_ARRAY_FREE_FUNC g_object_unref
+#define GDK_ARRAY_PREALLOC 16
+#include "gdkarrayimpl.c"
+
+/* based on gtk_propagate_event_internal © GTK Team */
+bool atl_propagate_synthetic_motionevent(GtkWidget *widget, jobject motionevent, GtkWidget *toplevel)
+{
+ int handled_event = false;
+ GtkWidgetStack widget_array;
+ int i;
+
+ /* First, propagate event down */
+ gtk_widget_stack_init(&widget_array);
+ gtk_widget_stack_append(&widget_array, g_object_ref(widget));
+
+ for (;;) {
+ widget = gtk_widget_get_parent(widget);
+ if (!widget)
+ break;
+
+ if (widget == toplevel)
+ break;
+
+ gtk_widget_stack_append(&widget_array, g_object_ref(widget));
+ }
+
+ i = gtk_widget_stack_get_size(&widget_array) - 1;
+ for (;;) {
+ widget = gtk_widget_stack_get(&widget_array, i);
+
+ if (!gtk_widget_is_sensitive(widget)) {
+ handled_event = true;
+ } else if (gtk_widget_get_realized(widget))
+ handled_event = dispatch_motionevent_if_JavaWidget(widget, GTK_PHASE_CAPTURE, motionevent);
+
+ handled_event |= !gtk_widget_get_realized(widget);
+
+ if (handled_event)
+ break;
+
+ if (i == 0)
+ break;
+
+ i--;
+ }
+
+ /* If not yet handled, also propagate back up */
+ if (!handled_event) {
+ /* Propagate event up the widget tree so that
+ * parents can see the button and motion
+ * events of the children.
+ */
+ for (i = 0; i < gtk_widget_stack_get_size(&widget_array); i++) {
+ widget = gtk_widget_stack_get(&widget_array, i);
+
+ /* Scroll events are special cased here because it
+ * feels wrong when scrolling a GtkViewport, say,
+ * to have children of the viewport eat the scroll
+ * event
+ */
+ if (!gtk_widget_is_sensitive(widget))
+ handled_event = true;
+ else if (gtk_widget_get_realized(widget))
+ handled_event = dispatch_motionevent_if_JavaWidget(widget, GTK_PHASE_BUBBLE, motionevent);
+
+ handled_event |= !gtk_widget_get_realized(widget);
+
+ if (handled_event)
+ break;
+ }
+ }
+
+ gtk_widget_stack_clear(&widget_array);
+ return handled_event;
+}
+
+JNIEXPORT jboolean JNICALL Java_android_view_ViewGroup_native_1dispatchTouchEvent(JNIEnv *env, jobject this, jlong widget_ptr, jobject motion_event, jdouble x, jdouble y)
+{
+ GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr));
+ GtkWidget *picked_child = gtk_widget_pick(widget, x, y, GTK_PICK_DEFAULT);
+
+ return atl_propagate_synthetic_motionevent(picked_child, motion_event, widget);
+}
diff --git a/src/api-impl-jni/views/gdkarrayimpl.c b/src/api-impl-jni/views/gdkarrayimpl.c
new file mode 100644
index 00000000..dfde3ce4
--- /dev/null
+++ b/src/api-impl-jni/views/gdkarrayimpl.c
@@ -0,0 +1,326 @@
+/* lifted from Gtk for gtk_propagate_event_internal derived function */
+/*
+ * Copyright © 2020 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors: Benjamin Otte
+ */
+
+#include
+
+G_BEGIN_DECLS
+
+#ifndef GDK_ARRAY_TYPE_NAME
+#define GDK_ARRAY_TYPE_NAME GdkArray
+#endif
+
+#ifndef GDK_ARRAY_NAME
+#define GDK_ARRAY_NAME gdk_array
+#endif
+
+#ifndef GDK_ARRAY_ELEMENT_TYPE
+#define GDK_ARRAY_ELEMENT_TYPE gpointer
+#endif
+
+#ifdef GDK_ARRAY_PREALLOC
+#if GDK_ARRAY_PREALLOC == 0
+#undef GDK_ARRAY_PREALLOC
+#endif
+#endif
+
+#ifdef GDK_ARRAY_NULL_TERMINATED
+#define GDK_ARRAY_REAL_SIZE(_size) ((_size) + 1)
+#define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof(_T_) - 1)
+#else
+#define GDK_ARRAY_REAL_SIZE(_size) (_size)
+#define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof(_T_))
+#endif
+
+/* make this readable */
+#define _T_ GDK_ARRAY_ELEMENT_TYPE
+#define GdkArray GDK_ARRAY_TYPE_NAME
+#define gdk_array_paste_more(GDK_ARRAY_NAME, func_name) GDK_ARRAY_NAME##_##func_name
+#define gdk_array_paste(GDK_ARRAY_NAME, func_name) gdk_array_paste_more(GDK_ARRAY_NAME, func_name)
+#define gdk_array(func_name) gdk_array_paste(GDK_ARRAY_NAME, func_name)
+
+typedef struct GdkArray GdkArray;
+
+struct GdkArray {
+ _T_ *start;
+ _T_ *end;
+ _T_ *end_allocation;
+#ifdef GDK_ARRAY_PREALLOC
+ _T_ preallocated[GDK_ARRAY_REAL_SIZE(GDK_ARRAY_PREALLOC)];
+#endif
+};
+
+/* no G_GNUC_UNUSED here, if you don't use an array type, remove it. */
+static inline void
+gdk_array(init)(GdkArray *self)
+{
+#ifdef GDK_ARRAY_PREALLOC
+ self->start = self->preallocated;
+ self->end = self->start;
+ self->end_allocation = self->start + GDK_ARRAY_PREALLOC;
+#ifdef GDK_ARRAY_NULL_TERMINATED
+ *self->start = *(_T_[1]){0};
+#endif
+#else
+ self->start = NULL;
+ self->end = NULL;
+ self->end_allocation = NULL;
+#endif
+}
+
+G_GNUC_UNUSED static inline gsize
+gdk_array(get_capacity)(const GdkArray *self)
+{
+ return self->end_allocation - self->start;
+}
+
+G_GNUC_UNUSED static inline gsize
+gdk_array(get_size)(const GdkArray *self)
+{
+ return self->end - self->start;
+}
+
+static inline void
+gdk_array(free_elements)(_T_ *start,
+ _T_ *end)
+{
+#ifdef GDK_ARRAY_FREE_FUNC
+ _T_ *e;
+ for (e = start; e < end; e++)
+#ifdef GDK_ARRAY_BY_VALUE
+ GDK_ARRAY_FREE_FUNC(e);
+#else
+ GDK_ARRAY_FREE_FUNC(*e);
+#endif
+#endif
+}
+
+/* no G_GNUC_UNUSED here */
+static inline void
+gdk_array(clear)(GdkArray *self)
+{
+ gdk_array(free_elements)(self->start, self->end);
+
+#ifdef GDK_ARRAY_PREALLOC
+ if (self->start != self->preallocated)
+#endif
+ g_free(self->start);
+ gdk_array(init)(self);
+}
+
+/*
+ * gdk_array_steal:
+ * @self: the array
+ *
+ * Steals all data in the array and clears the array.
+ *
+ * If you need to know the size of the data, you should query it
+ * beforehand.
+ *
+ * Returns: The array's data
+ **/
+G_GNUC_UNUSED static inline _T_ *
+gdk_array(steal)(GdkArray *self)
+{
+ _T_ *result;
+
+#ifdef GDK_ARRAY_PREALLOC
+ if (self->start == self->preallocated) {
+ gsize size = GDK_ARRAY_REAL_SIZE(gdk_array(get_size)(self));
+ result = g_new(_T_, size);
+ memcpy(result, self->preallocated, sizeof(_T_) * size);
+ } else
+#endif
+ result = self->start;
+
+ gdk_array(init)(self);
+
+ return result;
+}
+
+G_GNUC_UNUSED static inline _T_ *
+gdk_array(get_data)(const GdkArray *self)
+{
+ return self->start;
+}
+
+G_GNUC_UNUSED static inline _T_ *
+gdk_array(index)(const GdkArray *self,
+ gsize pos)
+{
+ return self->start + pos;
+}
+
+G_GNUC_UNUSED static inline gboolean
+gdk_array(is_empty)(const GdkArray *self)
+{
+ return self->end == self->start;
+}
+
+G_GNUC_UNUSED static inline void
+gdk_array(reserve)(GdkArray *self,
+ gsize n)
+{
+ gsize new_capacity, size, capacity;
+
+ if (G_UNLIKELY(n > GDK_ARRAY_MAX_SIZE))
+ g_error("requesting array size of %zu, but maximum size is %zu", n, GDK_ARRAY_MAX_SIZE);
+
+ capacity = gdk_array(get_capacity)(self);
+ if (n <= capacity)
+ return;
+
+ size = gdk_array(get_size)(self);
+ /* capacity * 2 can overflow, that's why we MAX() */
+ new_capacity = MAX(GDK_ARRAY_REAL_SIZE(n), capacity * 2);
+
+#ifdef GDK_ARRAY_PREALLOC
+ if (self->start == self->preallocated) {
+ self->start = g_new(_T_, new_capacity);
+ memcpy(self->start, self->preallocated, sizeof(_T_) * GDK_ARRAY_REAL_SIZE(size));
+ } else
+#endif
+#ifdef GDK_ARRAY_NULL_TERMINATED
+ if (self->start == NULL) {
+ self->start = g_new(_T_, new_capacity);
+ *self->start = *(_T_[1]){0};
+ } else
+#endif
+ self->start = g_renew(_T_, self->start, new_capacity);
+
+ self->end = self->start + size;
+ self->end_allocation = self->start + new_capacity;
+#ifdef GDK_ARRAY_NULL_TERMINATED
+ self->end_allocation--;
+#endif
+}
+
+G_GNUC_UNUSED static inline void
+gdk_array(splice)(GdkArray *self,
+ gsize pos,
+ gsize removed,
+ gboolean stolen,
+#ifdef GDK_ARRAY_BY_VALUE
+ const _T_ *additions,
+#else
+ _T_ *additions,
+#endif
+ gsize added)
+{
+ gsize size;
+ gsize remaining;
+
+ size = gdk_array(get_size)(self);
+ g_assert(pos + removed <= size);
+ remaining = size - pos - removed;
+
+ if (!stolen)
+ gdk_array(free_elements)(gdk_array(index)(self, pos),
+ gdk_array(index)(self, pos + removed));
+
+ gdk_array(reserve)(self, size - removed + added);
+
+ if (GDK_ARRAY_REAL_SIZE(remaining) && removed != added)
+ memmove(gdk_array(index)(self, pos + added),
+ gdk_array(index)(self, pos + removed),
+ GDK_ARRAY_REAL_SIZE(remaining) * sizeof(_T_));
+
+ if (added) {
+ if (additions)
+ memcpy(gdk_array(index)(self, pos),
+ additions,
+ added * sizeof(_T_));
+#ifndef GDK_ARRAY_NO_MEMSET
+ else
+ memset(gdk_array(index)(self, pos), 0, added * sizeof(_T_));
+#endif
+ }
+
+ /* might overflow, but does the right thing */
+ self->end += added - removed;
+}
+
+G_GNUC_UNUSED static void
+gdk_array(set_size)(GdkArray *self,
+ gsize new_size)
+{
+ gsize old_size = gdk_array(get_size)(self);
+ if (new_size > old_size)
+ gdk_array(splice)(self, old_size, 0, FALSE, NULL, new_size - old_size);
+ else
+ gdk_array(splice)(self, new_size, old_size - new_size, FALSE, NULL, 0);
+}
+
+G_GNUC_UNUSED static void
+gdk_array(append)(GdkArray *self,
+#ifdef GDK_ARRAY_BY_VALUE
+ _T_ *value)
+#else
+ _T_ value)
+#endif
+{
+ gdk_array(splice)(self,
+ gdk_array(get_size)(self),
+ 0,
+ FALSE,
+#ifdef GDK_ARRAY_BY_VALUE
+ value,
+#else
+ &value,
+#endif
+ 1);
+}
+
+#ifdef GDK_ARRAY_BY_VALUE
+G_GNUC_UNUSED static _T_ *
+gdk_array(get)(const GdkArray *self,
+ gsize pos)
+{
+ return gdk_array(index)(self, pos);
+}
+#else
+G_GNUC_UNUSED static _T_
+gdk_array(get)(const GdkArray *self,
+ gsize pos)
+{
+ return *gdk_array(index)(self, pos);
+}
+#endif
+
+#ifndef GDK_ARRAY_NO_UNDEF
+
+#undef _T_
+#undef GdkArray
+#undef gdk_array_paste_more
+#undef gdk_array_paste
+#undef gdk_array
+#undef GDK_ARRAY_REAL_SIZE
+#undef GDK_ARRAY_MAX_SIZE
+
+#undef GDK_ARRAY_BY_VALUE
+#undef GDK_ARRAY_ELEMENT_TYPE
+#undef GDK_ARRAY_FREE_FUNC
+#undef GDK_ARRAY_NAME
+#undef GDK_ARRAY_NULL_TERMINATED
+#undef GDK_ARRAY_PREALLOC
+#undef GDK_ARRAY_TYPE_NAME
+#undef GDK_ARRAY_NO_MEMSET
+#endif
+
+G_END_DECLS
diff --git a/src/api-impl-jni/widgets/WrapperWidget.c b/src/api-impl-jni/widgets/WrapperWidget.c
index 82858880..cb418cfb 100644
--- a/src/api-impl-jni/widgets/WrapperWidget.c
+++ b/src/api-impl-jni/widgets/WrapperWidget.c
@@ -23,7 +23,7 @@ static void wrapper_widget_set_property (GObject *object, guint property_id, con
}
}
-static void wrapper_widget_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+static void wrapper_widget_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
WrapperWidget *self = WRAPPER_WIDGET(object);
@@ -91,7 +91,7 @@ static void wrapper_widget_get_property (GObject *object, guint property_id, GVa
}
}
-static void wrapper_widget_init (WrapperWidget *wrapper_widget)
+static void wrapper_widget_init(WrapperWidget *wrapper_widget)
{
}
@@ -386,7 +386,7 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job
jmethodID ontouchevent_method = _METHOD(_CLASS(jobj), "onTouchEvent", "(Landroid/view/MotionEvent;)Z");
jmethodID dispatchtouchevent_method = _METHOD(_CLASS(jobj), "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z");
- wrapper->custom_dispatch_touch = dispatchtouchevent_method != handle_cache.view.dispatchTouchEvent;
+ wrapper->custom_dispatch_touch = (dispatchtouchevent_method != handle_cache.view.dispatchTouchEvent && dispatchtouchevent_method != handle_cache.view_group.dispatchTouchEvent);
if (ontouchevent_method != handle_cache.view.onTouchEvent || wrapper->custom_dispatch_touch) {
_setOnTouchListener(env, jobj, GTK_WIDGET(wrapper));
}
diff --git a/src/api-impl/android/view/View.java b/src/api-impl/android/view/View.java
index 6033818a..d502d818 100644
--- a/src/api-impl/android/view/View.java
+++ b/src/api-impl/android/view/View.java
@@ -1075,13 +1075,22 @@ public class View implements Drawable.Callback {
}
private OnTouchListener on_touch_listener = null;
- public boolean onTouchEvent(MotionEvent event) {
+
+ public boolean onTouchEventInternal(MotionEvent event) {
boolean handled = false;
if (on_touch_listener != null)
handled = on_touch_listener.onTouch(this, event);
+
+ if (!handled)
+ handled = onTouchEvent(event);
+
return handled;
}
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+
public void setOnTouchListener(OnTouchListener l) {
nativeSetOnTouchListener(widget);
on_touch_listener = l;
@@ -1206,12 +1215,21 @@ public class View implements Drawable.Callback {
public native void setBackgroundColor(int color);
public native void native_setVisibility(long widget, int visibility, float alpha);
+
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ }
+
+ protected void dispatchVisibilityChanged(View changedView, int visibility) {
+ onVisibilityChanged(changedView, visibility);
+ }
+
public void setVisibility(int visibility) {
native_setVisibility(widget, visibility, alpha);
if ((visibility == View.GONE) != (this.visibility == View.GONE)) {
requestLayout();
}
this.visibility = visibility;
+ dispatchVisibilityChanged(this, visibility);
}
public void setPadding(int left, int top, int right, int bottom) {
diff --git a/src/api-impl/android/view/ViewGroup.java b/src/api-impl/android/view/ViewGroup.java
index b8defa1d..e9b2b9bb 100644
--- a/src/api-impl/android/view/ViewGroup.java
+++ b/src/api-impl/android/view/ViewGroup.java
@@ -61,6 +61,10 @@ public class ViewGroup extends View implements ViewParent, ViewManager {
addView(child, params);
}
+ public void addView(View child, int index, LayoutParams params) {
+ addViewInternal(child, index, params);
+ }
+
protected void addViewInternal(View child, int index, LayoutParams params) {
if (child.parent == this)
return;
@@ -81,10 +85,13 @@ public class ViewGroup extends View implements ViewParent, ViewManager {
requestLayout();
}
- public void addView(View child, int index, LayoutParams params) {
- addViewInternal(child, index, params);
+ /* We never call this ourselves */
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ return native_dispatchTouchEvent(widget, event, event.getX(), event.getY());
}
+
protected boolean addViewInLayout(View child, int index, LayoutParams params) {
addViewInternal(child, index, params);
return true;
@@ -169,6 +176,16 @@ public class ViewGroup extends View implements ViewParent, ViewManager {
}
}
+ @Override
+ protected void dispatchVisibilityChanged(View changedView, int visibility) {
+ if (children == null) // happens if this gets called during super constructor
+ return;
+
+ for (View child: children) {
+ child.dispatchVisibilityChanged(changedView, visibility);
+ }
+ }
+
protected native void native_addView(long widget, long child, int index, LayoutParams params);
protected native void native_removeView(long widget, long child);
@Override
@@ -636,4 +653,6 @@ public class ViewGroup extends View implements ViewParent, ViewManager {
}
public void requestChildFocus(View child, View focused) {}
+
+ public native boolean native_dispatchTouchEvent(long widget, MotionEvent event, double x, double y);
}