prevent reference cycles between Java and native objects

This commit is contained in:
Julian Winkler
2024-07-26 21:47:08 +02:00
parent 45801d8f17
commit e3c0931714
30 changed files with 257 additions and 181 deletions

View File

@@ -45,7 +45,6 @@ JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1bitmap_1from_1path(
attach_sk_image(pixbuf);
g_object_ref(pixbuf);
return _INTPTR(pixbuf);
}
/* new empty bitmap */

View File

@@ -115,3 +115,9 @@ JNIEXPORT void JNICALL Java_android_graphics_Canvas_native_1drawPath(JNIEnv *env
sk_canvas_draw_path(canvas, path, paint);
}
JNIEXPORT void JNICALL Java_android_graphics_Canvas_native_1destroy_1canvas(JNIEnv *env, jclass class, jlong skia_canvas)
{
sk_canvas_t *canvas = (sk_canvas_t *)_PTR(skia_canvas);
sk_canvas_destroy(canvas);
}

View File

@@ -14,6 +14,8 @@
#define _INTPTR(ptr)((jlong)(intptr_t)(ptr))
#define _REF(obj)((*env)->NewGlobalRef(env, obj))
#define _UNREF(obj)((*env)->DeleteGlobalRef(env, obj))
#define _WEAK_REF(obj)((*env)->NewWeakGlobalRef(env, obj))
#define _WEAK_UNREF(obj)((*env)->DeleteWeakGlobalRef(env, obj))
#define _CLASS(object) ((*env)->GetObjectClass(env, object))
#define _SUPER(object) ((*env)->GetSuperclass(env, object))
#define _METHOD(class, method, attrs) ((*env)->GetMethodID(env, class, method, attrs))

View File

@@ -89,6 +89,14 @@ JNIEXPORT void JNICALL Java_android_graphics_Canvas_native_1rotate_1and_1transla
JNIEXPORT void JNICALL Java_android_graphics_Canvas_native_1drawPath
(JNIEnv *, jclass, jlong, jlong, jlong);
/*
* Class: android_graphics_Canvas
* Method: native_destroy_canvas
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_android_graphics_Canvas_native_1destroy_1canvas
(JNIEnv *, jclass, jlong);
#ifdef __cplusplus
}
#endif

View File

@@ -201,19 +201,19 @@ extern "C" {
#define android_view_View_TEXT_DIRECTION_RTL 4L
/*
* Class: android_view_View
* Method: setOnTouchListener
* Signature: (Landroid/view/View/OnTouchListener;)V
* Method: nativeSetOnTouchListener
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_android_view_View_setOnTouchListener
(JNIEnv *, jobject, jobject);
JNIEXPORT void JNICALL Java_android_view_View_nativeSetOnTouchListener
(JNIEnv *, jobject, jlong);
/*
* Class: android_view_View
* Method: setOnClickListener
* Signature: (Landroid/view/View/OnClickListener;)V
* Method: nativeSetOnClickListener
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_android_view_View_setOnClickListener
(JNIEnv *, jobject, jobject);
JNIEXPORT void JNICALL Java_android_view_View_nativeSetOnClickListener
(JNIEnv *, jobject, jlong);
/*
* Class: android_view_View
@@ -337,11 +337,11 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1setVisibility
/*
* Class: android_view_View
* Method: setOnLongClickListener
* Signature: (Landroid/view/View/OnLongClickListener;)V
* Method: nativeSetOnLongClickListener
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_android_view_View_setOnLongClickListener
(JNIEnv *, jobject, jobject);
JNIEXPORT void JNICALL Java_android_view_View_nativeSetOnLongClickListener
(JNIEnv *, jobject, jlong);
/*
* Class: android_view_View

View File

@@ -217,11 +217,11 @@ JNIEXPORT void JNICALL Java_android_widget_Button_native_1setText
/*
* Class: android_widget_Button
* Method: native_setOnClickListener
* Signature: (JLandroid/view/View/OnClickListener;)V
* Method: nativeSetOnClickListener
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_android_widget_Button_native_1setOnClickListener
(JNIEnv *, jobject, jlong, jobject);
JNIEXPORT void JNICALL Java_android_widget_Button_nativeSetOnClickListener
(JNIEnv *, jobject, jlong);
#ifdef __cplusplus
}

View File

@@ -225,11 +225,11 @@ JNIEXPORT void JNICALL Java_android_widget_ImageButton_native_1setDrawable
/*
* Class: android_widget_ImageButton
* Method: native_setOnClickListener
* Signature: (JLandroid/view/View/OnClickListener;)V
* Method: nativeSetOnClickListener
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_android_widget_ImageButton_native_1setOnClickListener
(JNIEnv *, jobject, jlong, jobject);
JNIEXPORT void JNICALL Java_android_widget_ImageButton_nativeSetOnClickListener
(JNIEnv *, jobject, jlong);
#ifdef __cplusplus
}

View File

@@ -81,7 +81,7 @@ JNIEXPORT jlong JNICALL Java_android_graphics_drawable_Drawable_native_1construc
JavaPaintable *paintable = NULL;
if (handle_cache.drawable.draw != _METHOD(_CLASS(this), "draw", "(Landroid/graphics/Canvas;)V")) {
paintable = g_object_new(java_paintable_get_type(), NULL);
paintable->drawable = _REF(this);
paintable->drawable = _WEAK_REF(this);
}
return _INTPTR(paintable);
}

View File

@@ -133,6 +133,7 @@ void set_up_handle_cache(JNIEnv *env)
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");
handle_cache.view.measure = _METHOD(handle_cache.view.class, "measure", "(II)V");
handle_cache.view.performLongClick = _METHOD(handle_cache.view.class, "performLongClick", "(FF)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");

View File

@@ -79,6 +79,7 @@ struct handle_cache {
jmethodID onInterceptTouchEvent;
jmethodID layoutInternal;
jmethodID measure;
jmethodID performLongClick;
} view;
struct {
jclass class;

View File

@@ -87,11 +87,20 @@ static GtkSizeRequestMode android_layout_get_request_mode(GtkLayoutManager *layo
}
}
static void android_layout_dispose(GObject *layout_manager)
{
AndroidLayout *layout = ATL_ANDROID_LAYOUT(layout_manager);
JNIEnv *env = get_jni_env();
_WEAK_UNREF(layout->view);
}
static void android_layout_class_init(AndroidLayoutClass *klass)
{
klass->parent_class.measure = android_layout_measure;
klass->parent_class.allocate = android_layout_allocate;
klass->parent_class.get_request_mode = android_layout_get_request_mode;
klass->parent_class.parent_class.dispose = android_layout_dispose;
}
static void android_layout_init(AndroidLayout *self) {}
@@ -101,7 +110,8 @@ G_DEFINE_TYPE(AndroidLayout, android_layout, GTK_TYPE_LAYOUT_MANAGER)
GtkLayoutManager *android_layout_new(jobject view)
{
AndroidLayout *layout = g_object_new(android_layout_get_type(), NULL);
layout->view = view;
JNIEnv *env = get_jni_env();
layout->view = _WEAK_REF(view);
layout->width = MATCH_PARENT;
layout->height = MATCH_PARENT;
return &layout->parent_instance;

View File

@@ -13,34 +13,31 @@
#define SOURCE_TOUCHSCREEN 0x1002
struct touch_callback_data { JavaVM *jvm; jobject this; jobject on_touch_listener; jclass on_touch_listener_class; bool intercepted; };
static GdkEvent *canceled_event = NULL;
static struct touch_callback_data *cancel_triggerer = NULL;
static WrapperWidget *cancel_triggerer = NULL;
static bool call_ontouch_callback(int action, double x, double y, struct touch_callback_data *d, GtkPropagationPhase phase, guint32 timestamp, GdkEvent *event)
static bool call_ontouch_callback(WrapperWidget *wrapper, int action, double x, double y, GtkPropagationPhase phase, guint32 timestamp, GdkEvent *event)
{
bool ret;
double raw_x;
double raw_y;
JNIEnv *env;
(*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6);
JNIEnv *env = get_jni_env();
jobject this = wrapper->jobj;
gdk_event_get_position(event, &raw_x, &raw_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, (float)raw_x, (float)raw_y);
if (phase == GTK_PHASE_CAPTURE && !d->intercepted) {
d->intercepted = (*env)->CallBooleanMethod(env, d->this, handle_cache.view.onInterceptTouchEvent, motion_event);
if (d->intercepted) {
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 = d;
cancel_triggerer = wrapper;
}
ret = false;
} else if(d->on_touch_listener) /* NULL listener means the callback was registered for onTouchEvent */
ret = (*env)->CallBooleanMethod(env, d->on_touch_listener, _METHOD(d->on_touch_listener_class, "onTouch", "(Landroid/view/View;Landroid/view/MotionEvent;)Z"), d->this, motion_event);
else
ret = (*env)->CallBooleanMethod(env, d->this, _METHOD(d->on_touch_listener_class, "onTouchEvent", "(Landroid/view/MotionEvent;)Z"), motion_event);
} else {
ret = (*env)->CallBooleanMethod(env, this, handle_cache.view.onTouchEvent, motion_event);
}
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
@@ -48,7 +45,7 @@ static bool call_ontouch_callback(int action, double x, double y, struct touch_c
(*env)->DeleteLocalRef(env, motion_event);
if (action == MOTION_EVENT_ACTION_UP)
d->intercepted = false;
wrapper->intercepting_touch = false;
return ret;
}
static void gdk_event_get_widget_relative_position(GdkEvent *event, GtkWidget *widget, double *x, double *y)
@@ -70,41 +67,42 @@ static void gdk_event_get_widget_relative_position(GdkEvent *event, GtkWidget *w
}
// TODO: find a way to reconcile this with libandroid/input.c?
static gboolean on_event(GtkEventControllerLegacy *event_controller, GdkEvent *event, struct touch_callback_data *d)
static gboolean on_event(GtkEventControllerLegacy *event_controller, GdkEvent *event, gpointer user_data)
{
double x;
double y;
GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(event_controller));
WrapperWidget *wrapper = WRAPPER_WIDGET(widget);
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
if (cancel_triggerer == d) { // cancel done
if (cancel_triggerer == wrapper) { // cancel done
canceled_event = NULL;
cancel_triggerer = NULL;
} else if (event == canceled_event) {
gdk_event_get_widget_relative_position(event, widget, &x, &y);
call_ontouch_callback(MOTION_EVENT_ACTION_CANCEL, x, y, d, phase, timestamp, event);
call_ontouch_callback(wrapper, MOTION_EVENT_ACTION_CANCEL, x, y, phase, timestamp, event);
return false;
}
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, timestamp, event);
return call_ontouch_callback(wrapper, MOTION_EVENT_ACTION_DOWN, x, y, phase, timestamp, event);
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, timestamp, event);
return call_ontouch_callback(wrapper, MOTION_EVENT_ACTION_UP, x, y, phase, timestamp, event);
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, timestamp, event);
return call_ontouch_callback(wrapper, MOTION_EVENT_ACTION_MOVE, x, y, phase, timestamp, event);
break;
default:
break;
@@ -113,12 +111,13 @@ static gboolean on_event(GtkEventControllerLegacy *event_controller, GdkEvent *e
return false;
}
static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, struct touch_callback_data *d)
static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data)
{
JNIEnv *env;
(*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6);
JNIEnv *env = get_jni_env();
GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
WrapperWidget *wrapper = WRAPPER_WIDGET(gtk_widget_get_parent(widget));
(*env)->CallVoidMethod(env, d->on_touch_listener, _METHOD(d->on_touch_listener_class, "onClick", "(Landroid/view/View;)V"), d->this);
(*env)->CallBooleanMethod(env, wrapper->jobj, handle_cache.view.performClick);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
@@ -146,25 +145,16 @@ static gboolean scroll_cb(GtkEventControllerScroll* self, gdouble dx, gdouble dy
return ret;
}
void _setOnTouchListener(JNIEnv *env, jobject this, GtkWidget *widget, jobject on_touch_listener)
void _setOnTouchListener(JNIEnv *env, jobject this, GtkWidget *widget)
{
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
struct touch_callback_data *callback_data = malloc(sizeof(struct touch_callback_data));
callback_data->jvm = jvm;
callback_data->this = _REF(this);
callback_data->on_touch_listener = on_touch_listener ? _REF(on_touch_listener) : NULL;
callback_data->on_touch_listener_class = on_touch_listener ? _REF(_CLASS(callback_data->on_touch_listener)) : _REF(_CLASS(callback_data->this));
GtkEventController *old_controller = g_object_get_data(G_OBJECT(widget), "on_touch_listener");
if(old_controller)
gtk_widget_remove_controller(widget, old_controller);
return;
GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_event_controller_legacy_new());
gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_BUBBLE);
g_signal_connect(controller, "event", G_CALLBACK(on_event), callback_data);
g_signal_connect(controller, "event", G_CALLBACK(on_event), NULL);
gtk_widget_add_controller(widget, controller);
g_object_set_data(G_OBJECT(widget), "on_touch_listener", controller);
@@ -177,7 +167,7 @@ void _setOnTouchListener(JNIEnv *env, jobject this, GtkWidget *widget, jobject o
GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_event_controller_legacy_new());
gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_CAPTURE);
g_signal_connect(controller, "event", G_CALLBACK(on_event), callback_data);
g_signal_connect(controller, "event", G_CALLBACK(on_event), NULL);
gtk_widget_add_controller(widget, controller);
g_object_set_data(G_OBJECT(widget), "on_intercept_touch_listener", controller);
}
@@ -188,35 +178,24 @@ void _setOnTouchListener(JNIEnv *env, jobject this, GtkWidget *widget, jobject o
gtk_widget_set_overflow(GTK_WIDGET(wrapper), GTK_OVERFLOW_HIDDEN);
}
JNIEXPORT void JNICALL Java_android_view_View_setOnTouchListener(JNIEnv *env, jobject this, jobject on_touch_listener)
JNIEXPORT void JNICALL Java_android_view_View_nativeSetOnTouchListener(JNIEnv *env, jobject this, jlong widget_ptr)
{
GtkWidget *widget = gtk_widget_get_parent(GTK_WIDGET(_PTR(_GET_LONG_FIELD(this, "widget"))));
GtkWidget *widget = gtk_widget_get_parent(GTK_WIDGET(_PTR(widget_ptr)));
_setOnTouchListener(env, this, widget, on_touch_listener);
_setOnTouchListener(env, this, widget);
}
JNIEXPORT void JNICALL Java_android_view_View_setOnClickListener(JNIEnv *env, jobject this, jobject on_click_listener)
JNIEXPORT void JNICALL Java_android_view_View_nativeSetOnClickListener(JNIEnv *env, jobject this, jlong widget_ptr)
{
GtkWidget *widget = GTK_WIDGET(_PTR(_GET_LONG_FIELD(this, "widget")));
if (!on_click_listener)
return;
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
struct touch_callback_data *callback_data = malloc(sizeof(struct touch_callback_data));
callback_data->jvm = jvm;
callback_data->this = _REF(this);
callback_data->on_touch_listener = _REF(on_click_listener);
callback_data->on_touch_listener_class = _REF(_CLASS(callback_data->on_touch_listener));
GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr));
GtkEventController *old_controller = g_object_get_data(G_OBJECT(widget), "on_click_listener");
if(old_controller)
gtk_widget_remove_controller(widget, old_controller);
return;
GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new());
g_signal_connect(controller, "released", G_CALLBACK(on_click), callback_data); // the release completes the click, I guess?
g_signal_connect(controller, "released", G_CALLBACK(on_click), NULL); // the release completes the click, I guess?
gtk_widget_add_controller(widget, controller);
g_object_set_data(G_OBJECT(widget), "on_click_listener", controller);
widget_set_needs_allocation(widget);
@@ -351,15 +330,27 @@ JNIEXPORT void JNICALL Java_android_view_View_native_1setVisibility(JNIEnv *env,
*/
struct _JavaWidget {GtkWidget parent_instance;};
G_DECLARE_FINAL_TYPE(JavaWidget, java_widget, JAVA, WIDGET, GtkWidget)
static void java_widget_dispose(GObject *java_widget)
{
GtkWidget *widget = GTK_WIDGET(java_widget);
GtkWidget *child = gtk_widget_get_first_child(widget);
while (child) {
GtkWidget *_child = gtk_widget_get_next_sibling(child);
gtk_widget_unparent(child);
child = _child;
}
}
static void java_widget_init(JavaWidget *java_widget) {}
static void java_widget_class_init(JavaWidgetClass *class) {}
static void java_widget_class_init(JavaWidgetClass *class) {
G_OBJECT_CLASS(class)->dispose = java_widget_dispose;
}
G_DEFINE_TYPE(JavaWidget, java_widget, GTK_TYPE_WIDGET)
JNIEXPORT jlong JNICALL Java_android_view_View_native_1constructor(JNIEnv *env, jobject this, jobject context, jobject attrs)
{
GtkWidget *wrapper = g_object_ref(wrapper_widget_new());
WrapperWidget *wrapper = g_object_ref(WRAPPER_WIDGET(wrapper_widget_new()));
GtkWidget *widget = g_object_new(java_widget_get_type(), NULL);
gtk_widget_set_layout_manager(widget, android_layout_new(_REF(this)));
gtk_widget_set_layout_manager(widget, android_layout_new(this));
wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), widget);
wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this);
@@ -373,7 +364,7 @@ JNIEXPORT jlong JNICALL Java_android_view_View_native_1constructor(JNIEnv *env,
if (_METHOD(_CLASS(this), "onGenericMotionEvent", "(Landroid/view/MotionEvent;)Z") != handle_cache.view.onGenericMotionEvent) {
GtkEventController *controller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
g_signal_connect(controller, "scroll", G_CALLBACK(scroll_cb), _REF(this));
g_signal_connect(controller, "scroll", G_CALLBACK(scroll_cb), wrapper->jobj);
gtk_widget_add_controller(widget, controller);
}
@@ -522,12 +513,13 @@ JNIEXPORT jboolean JNICALL Java_android_view_View_native_1getGlobalVisibleRect(J
return true;
}
static void on_long_click(GtkGestureLongPress *gesture, double x, double y, struct touch_callback_data *d)
static void on_long_click(GtkGestureLongPress *gesture, double x, double y, gpointer user_data)
{
JNIEnv *env;
(*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6);
JNIEnv *env = get_jni_env();
GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
WrapperWidget *wrapper = WRAPPER_WIDGET(gtk_widget_get_parent(widget));
bool ret =(*env)->CallBooleanMethod(env, d->on_touch_listener, _METHOD(d->on_touch_listener_class, "onLongClick", "(Landroid/view/View;)Z"), d->this);
bool ret =(*env)->CallBooleanMethod(env, wrapper->jobj, handle_cache.view.performLongClick, x, y);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
@@ -536,7 +528,7 @@ static void on_long_click(GtkGestureLongPress *gesture, double x, double y, stru
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_CLAIMED);
}
static void on_long_click_update(GtkGesture *gesture, GdkEventSequence* sequence, struct touch_callback_data *d)
static void on_long_click_update(GtkGesture *gesture, GdkEventSequence* sequence, gpointer user_data)
{
GdkEvent *event = gtk_gesture_get_last_event(gesture, sequence);
if (event == canceled_event) {
@@ -544,29 +536,17 @@ static void on_long_click_update(GtkGesture *gesture, GdkEventSequence* sequence
}
}
JNIEXPORT void JNICALL Java_android_view_View_setOnLongClickListener(JNIEnv *env, jobject this, jobject on_long_click_listener)
JNIEXPORT void JNICALL Java_android_view_View_nativeSetOnLongClickListener(JNIEnv *env, jobject this, jlong widget_ptr)
{
GtkWidget *widget = GTK_WIDGET(_PTR(_GET_LONG_FIELD(this, "widget")));
if (!on_long_click_listener)
return;
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
struct touch_callback_data *callback_data = malloc(sizeof(struct touch_callback_data));
callback_data->jvm = jvm;
callback_data->this = _REF(this);
callback_data->on_touch_listener = _REF(on_long_click_listener);
callback_data->on_touch_listener_class = _REF(_CLASS(callback_data->on_touch_listener));
GtkWidget *widget = GTK_WIDGET(_PTR(widget_ptr));
GtkEventController *old_controller = g_object_get_data(G_OBJECT(widget), "on_long_click_listener");
if(old_controller)
gtk_widget_remove_controller(widget, old_controller);
return;
GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_gesture_long_press_new());
g_signal_connect(controller, "pressed", G_CALLBACK(on_long_click), callback_data);
g_signal_connect(controller, "update", G_CALLBACK(on_long_click_update), callback_data);
g_signal_connect(controller, "pressed", G_CALLBACK(on_long_click), NULL);
g_signal_connect(controller, "update", G_CALLBACK(on_long_click_update), NULL);
gtk_widget_add_controller(widget, controller);
g_object_set_data(G_OBJECT(widget), "on_long_click_listener", controller);
}

View File

@@ -17,13 +17,19 @@ static void wrapper_widget_init (WrapperWidget *wrapper_widget)
static void wrapper_widget_dispose(GObject *wrapper_widget)
{
gtk_widget_unparent(gtk_widget_get_first_child(GTK_WIDGET(wrapper_widget)));
GtkWidget *widget = GTK_WIDGET(wrapper_widget);
GtkWidget *child = gtk_widget_get_first_child(widget);
while (child) {
GtkWidget *_child = gtk_widget_get_next_sibling(child);
gtk_widget_unparent(child);
child = _child;
}
WrapperWidget *wrapper = WRAPPER_WIDGET(wrapper_widget);
if (wrapper->jvm) {
JNIEnv *env;
(*wrapper->jvm)->GetEnv(wrapper->jvm, (void**)&env, JNI_VERSION_1_6);
if (wrapper->jobj)
_UNREF(wrapper->jobj);
_WEAK_UNREF(wrapper->jobj);
if (wrapper->canvas)
_UNREF(wrapper->canvas);
}
@@ -179,7 +185,7 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
wrapper->jvm = jvm;
wrapper->jobj = _REF(jobj);
wrapper->jobj = _WEAK_REF(jobj);
jmethodID on_draw_method = _METHOD(_CLASS(jobj), "onDraw", "(Landroid/graphics/Canvas;)V");
jmethodID draw_method = _METHOD(_CLASS(jobj), "draw", "(Landroid/graphics/Canvas;)V");
if (on_draw_method != handle_cache.view.onDraw || draw_method != handle_cache.view.draw) {
@@ -192,7 +198,7 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job
jmethodID ontouchevent_method = _METHOD(_CLASS(jobj), "onTouchEvent", "(Landroid/view/MotionEvent;)Z");
if (ontouchevent_method != handle_cache.view.onTouchEvent) {
_setOnTouchListener(env, jobj, GTK_WIDGET(wrapper), NULL);
_setOnTouchListener(env, jobj, GTK_WIDGET(wrapper));
}
jmethodID computeScroll_method = _METHOD(_CLASS(jobj), "computeScroll", "()V");
@@ -204,7 +210,7 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job
if (performClick_method != handle_cache.view.performClick) {
GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new());
g_signal_connect(controller, "released", G_CALLBACK(on_click), _REF(jobj));
g_signal_connect(controller, "released", G_CALLBACK(on_click), wrapper->jobj);
gtk_widget_add_controller(wrapper->child, controller);
widget_set_needs_allocation(wrapper->child);
}

View File

@@ -22,6 +22,7 @@ struct _WrapperWidget
int real_width;
int real_height;
gboolean needs_allocation;
gboolean intercepting_touch;
};
struct _WrapperWidgetClass
@@ -37,6 +38,6 @@ void wrapper_widget_set_layout_params(WrapperWidget *wrapper, int width, int hei
void wrapper_widget_set_background(WrapperWidget *wrapper, GdkPaintable *paintable);
void wrapper_widget_consume_touch_events(WrapperWidget *wrapper);
void _setOnTouchListener(JNIEnv *env, jobject this, GtkWidget *widget, jobject on_touch_listener);
void _setOnTouchListener(JNIEnv *env, jobject this, GtkWidget *widget);
#endif

View File

@@ -107,7 +107,7 @@ JNIEXPORT jlong JNICALL Java_android_widget_AbsListView_native_1constructor(JNIE
RangeListModel *model = g_object_new(range_list_model_get_type(), NULL);
GtkWidget *list_view = gtk_list_view_new(GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(model))), factory);
model->list_view = list_view;
model->jobject = _REF(this);
model->jobject = _WEAK_REF(this);
GtkWidget *scrolled_window = gtk_scrolled_window_new();
gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(scrolled_window), TRUE);
gtk_scrolled_window_set_propagate_natural_width(GTK_SCROLLED_WINDOW(scrolled_window), TRUE);

View File

@@ -36,7 +36,7 @@ JNIEXPORT jlong JNICALL Java_android_widget_AbsSpinner_native_1constructor(JNIEn
GtkWidget *dropdown = gtk_drop_down_new(G_LIST_MODEL(model), NULL);
gtk_drop_down_set_factory(GTK_DROP_DOWN(dropdown), factory);
model->list_view = dropdown;
model->jobject = _REF(this);
model->jobject = _WEAK_REF(this);
wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), dropdown);
gtk_widget_set_name(dropdown, "Spinner");
return _INTPTR(dropdown);

View File

@@ -15,6 +15,7 @@ JNIEXPORT jlong JNICALL Java_android_widget_Button_native_1constructor(JNIEnv *e
GtkWidget *label = gtk_button_new_with_label(text);
wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), label);
wrapper_widget_consume_touch_events(WRAPPER_WIDGET(wrapper)); // Android button consumes touch events
wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this);
return _INTPTR(label);
}
@@ -27,38 +28,20 @@ JNIEXPORT void JNICALL Java_android_widget_Button_native_1setText(JNIEnv *env, j
((*env)->ReleaseStringUTFChars(env, text, nativeText));
}
struct touch_callback_data {
JavaVM *jvm;
jobject this;
jobject listener;
jmethodID listener_method;
};
static void clicked_cb(GtkWidget *button, gpointer user_data) {
JNIEnv *env = get_jni_env();
WrapperWidget *wrapper = WRAPPER_WIDGET(gtk_widget_get_parent(button));
static void clicked_cb(GtkWidget *button, struct touch_callback_data *d) {
JNIEnv *env;
(*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6);
(*env)->CallVoidMethod(env, d->listener, d->listener_method, d->this);
(*env)->CallBooleanMethod(env, wrapper->jobj, handle_cache.view.performClick);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
}
JNIEXPORT void JNICALL Java_android_widget_Button_native_1setOnClickListener(JNIEnv *env, jobject this, jlong widget_ptr, jobject on_click_listener)
JNIEXPORT void JNICALL Java_android_widget_Button_nativeSetOnClickListener(JNIEnv *env, jobject this, jlong widget_ptr)
{
GtkWidget *button = GTK_WIDGET(_PTR(widget_ptr));
g_signal_handlers_disconnect_matched(button, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, clicked_cb, NULL);
if (!on_click_listener)
return;
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
struct touch_callback_data *callback_data = malloc(sizeof(struct touch_callback_data));
callback_data->jvm = jvm;
callback_data->this = _REF(this);
callback_data->listener = _REF(on_click_listener);
callback_data->listener_method = _METHOD(_CLASS(on_click_listener), "onClick", "(Landroid/view/View;)V");
g_signal_connect(button, "clicked", G_CALLBACK(clicked_cb), callback_data);
g_signal_connect(button, "clicked", G_CALLBACK(clicked_cb), NULL);
}

View File

@@ -89,7 +89,7 @@ JNIEXPORT void JNICALL Java_android_widget_EditText_native_1setOnEditorActionLis
return;
struct changed_callback_data *callback_data = malloc(sizeof(struct changed_callback_data));
callback_data->this = _REF(this);
callback_data->this = _WEAK_REF(this);
callback_data->listener = _REF(listener);
callback_data->listener_method = _METHOD(_CLASS(listener), "onEditorAction", "(Landroid/widget/TextView;ILandroid/view/KeyEvent;)Z");

View File

@@ -26,41 +26,23 @@ JNIEXPORT void JNICALL Java_android_widget_ImageButton_native_1setPixbuf(JNIEnv
Java_android_widget_ImageButton_native_1setDrawable(env, this, widget_ptr, _INTPTR(paintable));
}
struct touch_callback_data {
JavaVM *jvm;
jobject this;
jobject listener;
jmethodID listener_method;
};
static void clicked_cb(GtkWidget *button, struct touch_callback_data *d) {
static void clicked_cb(GtkWidget *button, gpointer user_data) {
printf("clicked_cb\n");
JNIEnv *env;
(*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6);
JNIEnv *env = get_jni_env();
WrapperWidget *wrapper = WRAPPER_WIDGET(gtk_widget_get_parent(button));
(*env)->CallVoidMethod(env, d->listener, d->listener_method, d->this);
(*env)->CallBooleanMethod(env, wrapper->jobj, handle_cache.view.performClick);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
}
JNIEXPORT void JNICALL Java_android_widget_ImageButton_native_1setOnClickListener(JNIEnv *env, jobject this, jlong widget_ptr, jobject on_click_listener)
JNIEXPORT void JNICALL Java_android_widget_ImageButton_nativeSetOnClickListener(JNIEnv *env, jobject this, jlong widget_ptr)
{
GtkWidget *button = GTK_WIDGET(_PTR(widget_ptr));
g_signal_handlers_disconnect_matched(button, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, clicked_cb, NULL);
if (!on_click_listener)
return;
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
struct touch_callback_data *callback_data = malloc(sizeof(struct touch_callback_data));
callback_data->jvm = jvm;
callback_data->this = _REF(this);
callback_data->listener = _REF(on_click_listener);
callback_data->listener_method = _METHOD(_CLASS(on_click_listener), "onClick", "(Landroid/view/View;)V");
g_signal_connect(button, "clicked", G_CALLBACK(clicked_cb), callback_data);
g_signal_connect(button, "clicked", G_CALLBACK(clicked_cb), NULL);
}
JNIEXPORT void JNICALL Java_android_widget_ImageButton_native_1setDrawable(JNIEnv *env, jobject this, jlong widget_ptr, jlong paintable_ptr)

View File

@@ -30,6 +30,7 @@ JNIEXPORT jlong JNICALL Java_android_widget_TextView_native_1constructor(JNIEnv
gtk_widget_set_hexpand(label, TRUE);
gtk_box_append(GTK_BOX(box), label);
wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), box);
wrapper_widget_set_jobject(WRAPPER_WIDGET(wrapper), env, this);
PangoAttrList* pango_attrs = pango_attr_list_new();
pango_attr_list_insert(pango_attrs, pango_attr_font_features_new("tnum"));

Some files were not shown because too many files have changed in this diff Show More