~eliasnaur/gio

app: add RegisterFragment method on *Window for Android v1 PROPOSED

Greg Pomerantz
Greg Pomerantz: 1
 app: add RegisterFragment method on *Window for Android

 7 files changed, 140 insertions(+), 15 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~eliasnaur/gio/patches/9107/mbox | git am -3
Learn more about email & git

[PATCH] app: add RegisterFragment method on *Window for Android Export this patch

Greg Pomerantz
RegisterFragment creates an instance of a Java class and attempts
to register it as a Fragment in the window's Context.

Signed-off-by: Greg Pomerantz <gmp.gio@wow.st>
---
 app/app.go                        |  7 ----
 app/app_android.go                | 29 ++++++++++++++++
 app/internal/window/GioView.java  | 70 ++++++++++++++++++++++++++++++++++++++-
 app/internal/window/handle.go     |  7 ----
 app/internal/window/os_android.c  | 13 ++++++++
 app/internal/window/os_android.go | 26 +++++++++++++++
 app/internal/window/os_android.h  |  3 ++
 7 files changed, 140 insertions(+), 15 deletions(-)
 create mode 100644 app/app_android.go
 delete mode 100644 app/internal/window/handle.go

diff --git a/app/app.go b/app/app.go
index 9a18ec4..71bc57b 100644
--- a/app/app.go
+++ b/app/app.go
@@ -9,13 +9,6 @@ import (
 	"gioui.org/app/internal/window"
 )
 
-type Handle window.Handle
-
-// PlatformHandle returns the platform specific Handle.
-func PlatformHandle() *Handle {
-	return (*Handle)(window.PlatformHandle)
-}
-
 // extraArgs contains extra arguments to append to
 // os.Args. The arguments are separated with |.
 // Useful for running programs on mobiles where the
diff --git a/app/app_android.go b/app/app_android.go
new file mode 100644
index 0000000..e83aa74
--- /dev/null
+++ b/app/app_android.go
@@ -0,0 +1,29 @@
+package app
+
+import (
+	"errors"
+
+	"gioui.org/app/internal/window"
+)
+
+type Handle window.Handle
+
+// PlatformHandle returns the Android platform-specific Handle.
+func PlatformHandle() *Handle {
+	return (*Handle)(window.PlatformHandle)
+}
+
+// RegisterFragment constructs a Java instance of the specified class
+// and attempts to register it as a Fragment in the Context in which
+// the View was created.
+//
+// NOTE: This method must not be called from the Gio application's main
+// event loop, as that would block the View's UI thread and result in a
+// deadlock.
+func (w *Window) RegisterFragment(del string) error {
+	d := w.driver.(window.AndroidDriver)
+	if d == nil {
+		return errors.New("RegisterFragment: no window driver found")
+	}
+	return d.RegisterFragment(del)
+}
diff --git a/app/internal/window/GioView.java b/app/internal/window/GioView.java
index 82bc36f..d601269 100644
--- a/app/internal/window/GioView.java
+++ b/app/internal/window/GioView.java
@@ -2,12 +2,27 @@
 
 package org.gioui;
 
+import java.lang.Class;
+import java.lang.ClassLoader;
+import java.lang.reflect.Method;
+import java.lang.IllegalAccessException;
+import java.lang.InstantiationException;
+import java.lang.ExceptionInInitializerError;
+import java.lang.NoSuchMethodException;
+import java.lang.NullPointerException;
+import java.lang.SecurityException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Handler;
-import android.util.AttributeSet;
 import android.text.Editable;
+import android.util.AttributeSet;
 import android.view.Choreographer;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -207,6 +222,59 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback
 		return onBack(nhandle);
 	}
 
+	public String registerFragment(String del) {
+		final Class cls;
+		final Fragment frag;
+		try {
+			cls = getContext().getClassLoader().loadClass(del);
+			frag = (Fragment)cls.newInstance();
+		}
+		catch (ClassNotFoundException ignored) {
+			return "RegisterFragment: fragment class not found";
+		}
+		catch (IllegalAccessException | InstantiationException | ExceptionInInitializerError ignored) {
+			return "RegisterFragment: cannot instantiate delegate object";
+		}
+		catch (SecurityException ignored) {
+			return "RegisterFragment: security exception";
+		}
+		catch (ClassCastException ignored) {
+			return "RegisterFragment: provided class does not extend Fragment";
+		}
+
+		final BlockingQueue<String> q = new LinkedBlockingQueue<String>();
+
+		handler.post(new Runnable() {
+			public void run() {
+				final Context ctx = getContext();
+				if (ctx == null) {
+					q.add("RegisterFragment: GioView has null context");
+					return;
+				}
+				final FragmentManager fm;
+				try {
+					final Method mth = ctx.getClass().getMethod("getFragmentManager");
+					fm = (FragmentManager)mth.invoke(ctx);
+				}
+				catch (NoSuchMethodException | NullPointerException | InvocationTargetException | ClassCastException | IllegalAccessException ignored) {
+					q.add("RegisterFragment: Cannot get Fragment manager from View Context");
+					return;
+				}
+				q.add("");
+				FragmentTransaction ft = fm.beginTransaction();
+				ft.add(frag, del);
+				ft.commitNow();
+			}
+		});
+		// NOTE: Because of the use of a BlockingQueue here, this method
+		// cannot be called while the main UI thread is blocked.
+		try {
+			return q.take();
+		} catch (InterruptedException e) {
+			return "RegisterFragment: interrupted";
+		}
+	}
+
 	static private native long onCreateView(GioView view);
 	static private native void onDestroyView(long handle);
 	static private native void onStartView(long handle);
diff --git a/app/internal/window/handle.go b/app/internal/window/handle.go
deleted file mode 100644
index 5d7b1a1..0000000
--- a/app/internal/window/handle.go
@@ -1,7 +0,0 @@
-// +build !android
-
-package window
-
-var PlatformHandle *Handle
-
-type Handle struct{}
diff --git a/app/internal/window/os_android.c b/app/internal/window/os_android.c
index 5150ab6..435350d 100644
--- a/app/internal/window/os_android.c
+++ b/app/internal/window/os_android.c
@@ -166,3 +166,16 @@ void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes)
 jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr) {
 	return (*env)->GetArrayLength(env, arr);
 }
+
+jstring gio_jni_RegisterFragment(JNIEnv *env, jobject view, jmethodID mid, char* del) {
+	jstring jdel = (*env)->NewStringUTF(env, del);
+	return (*env)->CallObjectMethod(env, view, mid, jdel);
+}
+
+const char *gio_jni_GetStringUTFChars(JNIEnv *env, jstring string) {
+	return (*env)->GetStringUTFChars(env, string, NULL);
+}
+
+void gio_jni_ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf) {
+	(*env)->ReleaseStringUTFChars(env, string, utf);
+}
diff --git a/app/internal/window/os_android.go b/app/internal/window/os_android.go
index 5cce88d..74d7482 100644
--- a/app/internal/window/os_android.go
+++ b/app/internal/window/os_android.go
@@ -54,6 +54,7 @@ type window struct {
 	mhideTextInput                 C.jmethodID
 	mpostFrameCallback             C.jmethodID
 	mpostFrameCallbackOnMainThread C.jmethodID
+	mRegisterFragment              C.jmethodID
 }
 
 var dataDirChan = make(chan string, 1)
@@ -119,6 +120,7 @@ func onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
 		mhideTextInput:                 jniGetMethodID(env, class, "hideTextInput", "()V"),
 		mpostFrameCallback:             jniGetMethodID(env, class, "postFrameCallback", "()V"),
 		mpostFrameCallbackOnMainThread: jniGetMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
+		mRegisterFragment:              jniGetMethodID(env, class, "registerFragment", "(Ljava/lang/String;)Ljava/lang/String;"),
 	}
 	wopts := <-mainWindow.out
 	w.callbacks = wopts.window
@@ -443,6 +445,30 @@ func (w *window) ShowTextInput(show bool) {
 	})
 }
 
+type AndroidDriver interface {
+	RegisterFragment(string) error
+}
+
+func (w *window) RegisterFragment(del string) error {
+	if w.view == 0 {
+		return errors.New("RegisterFragment: view is null")
+	}
+	ret := ""
+	runInJVM(func(env *C.JNIEnv) {
+		cdel := C.CString(del)
+		defer C.free(unsafe.Pointer(cdel))
+		jret := C.gio_jni_RegisterFragment(env, w.view, w.mRegisterFragment, cdel)
+		utf := C.gio_jni_GetStringUTFChars(env, jret)
+		ret = C.GoString(utf)
+		C.gio_jni_ReleaseStringUTFChars(env, jret, utf)
+	})
+	if ret == "" {
+		return nil
+	} else {
+		return errors.New(ret)
+	}
+}
+
 func Main() {
 }
 
diff --git a/app/internal/window/os_android.h b/app/internal/window/os_android.h
index 288d633..d17366c 100644
--- a/app/internal/window/os_android.h
+++ b/app/internal/window/os_android.h
@@ -17,3 +17,6 @@ __attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethod_J(JNIEnv *en
 __attribute__ ((visibility ("hidden"))) jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr);
 __attribute__ ((visibility ("hidden"))) void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes);
 __attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr);
+__attribute__ ((visibility ("hidden"))) jstring gio_jni_RegisterFragment(JNIEnv *env, jobject view, jmethodID mid, char* del);
+__attribute__ ((visibility ("hidden"))) const char *gio_jni_GetStringUTFChars(JNIEnv *env, jstring string);
+__attribute__ ((visibility ("hidden"))) void gio_jni_ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
-- 
2.16.2
View this thread in the archives