mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-04 00:45:35 +00:00
app/internal/window: [Android] implement clipboard
Updates gio#31 Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
package org.gioui;
|
package org.gioui;
|
||||||
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@@ -37,19 +35,5 @@ public final class Gio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeClipboard(Context ctx, String s) {
|
|
||||||
ClipboardManager m = (ClipboardManager)ctx.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
m.setPrimaryClip(ClipData.newPlainText(null, s));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String readClipboard(Context ctx) {
|
|
||||||
ClipboardManager m = (ClipboardManager)ctx.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
ClipData c = m.getPrimaryClip();
|
|
||||||
if (c == null || c.getItemCount() < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return c.getItemAt(0).coerceToText(ctx).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
static private native void runGoMain(byte[] dataDir, Context context);
|
static private native void runGoMain(byte[] dataDir, Context context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import android.view.View;
|
|||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
public class GioActivity extends Activity {
|
public final class GioActivity extends Activity {
|
||||||
private GioView view;
|
private GioView view;
|
||||||
|
|
||||||
@Override public void onCreate(Bundle state) {
|
@Override public void onCreate(Bundle state) {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import android.app.Activity;
|
|||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.FragmentManager;
|
import android.app.FragmentManager;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.ClipData;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -33,7 +35,7 @@ import android.view.inputmethod.EditorInfo;
|
|||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
public class GioView extends SurfaceView implements Choreographer.FrameCallback {
|
public final class GioView extends SurfaceView implements Choreographer.FrameCallback {
|
||||||
private final static Object initLock = new Object();
|
private final static Object initLock = new Object();
|
||||||
private static boolean jniLoaded;
|
private static boolean jniLoaded;
|
||||||
|
|
||||||
@@ -199,7 +201,15 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback
|
|||||||
return onBack(nhandle);
|
return onBack(nhandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerFragment(String del) {
|
protected void wakeupMainThread() {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
scheduleMainFuncs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void registerFragment(String del) {
|
||||||
final Class cls;
|
final Class cls;
|
||||||
try {
|
try {
|
||||||
cls = getContext().getClassLoader().loadClass(del);
|
cls = getContext().getClassLoader().loadClass(del);
|
||||||
@@ -228,6 +238,20 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void writeClipboard(String s) {
|
||||||
|
ClipboardManager m = (ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
m.setPrimaryClip(ClipData.newPlainText(null, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String readClipboard() {
|
||||||
|
ClipboardManager m = (ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData c = m.getPrimaryClip();
|
||||||
|
if (c == null || c.getItemCount() < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return c.getItemAt(0).coerceToText(getContext()).toString();
|
||||||
|
}
|
||||||
|
|
||||||
static private native long onCreateView(GioView view);
|
static private native long onCreateView(GioView view);
|
||||||
static private native void onDestroyView(long handle);
|
static private native void onDestroyView(long handle);
|
||||||
static private native void onStartView(long handle);
|
static private native void onStartView(long handle);
|
||||||
@@ -242,6 +266,7 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback
|
|||||||
static private native void onFrameCallback(long handle, long nanos);
|
static private native void onFrameCallback(long handle, long nanos);
|
||||||
static private native boolean onBack(long handle);
|
static private native boolean onBack(long handle);
|
||||||
static private native void onFocusChange(long handle, boolean focus);
|
static private native void onFocusChange(long handle, boolean focus);
|
||||||
|
static private native void scheduleMainFuncs();
|
||||||
|
|
||||||
private static class InputConnection extends BaseInputConnection {
|
private static class InputConnection extends BaseInputConnection {
|
||||||
private final Editable editable;
|
private final Editable editable;
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ type window struct {
|
|||||||
win *C.ANativeWindow
|
win *C.ANativeWindow
|
||||||
animating bool
|
animating bool
|
||||||
|
|
||||||
|
// Cached Java methods.
|
||||||
mgetDensity C.jmethodID
|
mgetDensity C.jmethodID
|
||||||
mgetFontScale C.jmethodID
|
mgetFontScale C.jmethodID
|
||||||
mshowTextInput C.jmethodID
|
mshowTextInput C.jmethodID
|
||||||
@@ -81,6 +82,9 @@ type window struct {
|
|||||||
mpostFrameCallback C.jmethodID
|
mpostFrameCallback C.jmethodID
|
||||||
mpostFrameCallbackOnMainThread C.jmethodID
|
mpostFrameCallbackOnMainThread C.jmethodID
|
||||||
mRegisterFragment C.jmethodID
|
mRegisterFragment C.jmethodID
|
||||||
|
mwakeupMainThread C.jmethodID
|
||||||
|
mwriteClipboard C.jmethodID
|
||||||
|
mreadClipboard C.jmethodID
|
||||||
}
|
}
|
||||||
|
|
||||||
type jvalue uint64 // The largest JNI type fits in 64 bits.
|
type jvalue uint64 // The largest JNI type fits in 64 bits.
|
||||||
@@ -96,18 +100,14 @@ var android struct {
|
|||||||
|
|
||||||
// The global Android App context.
|
// The global Android App context.
|
||||||
appCtx C.jobject
|
appCtx C.jobject
|
||||||
// The Gio class reference.
|
|
||||||
gioCls C.jclass
|
|
||||||
|
|
||||||
// Cached Java methods.
|
|
||||||
writeClipboard C.jmethodID
|
|
||||||
readClipboard C.jmethodID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var views = make(map[C.jlong]*window)
|
var views = make(map[C.jlong]*window)
|
||||||
|
|
||||||
var mainWindow = newWindowRendezvous()
|
var mainWindow = newWindowRendezvous()
|
||||||
|
|
||||||
|
var mainFuncs = make(chan func(env *C.JNIEnv), 1)
|
||||||
|
|
||||||
func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
|
func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
|
||||||
m := C.CString(method)
|
m := C.CString(method)
|
||||||
defer C.free(unsafe.Pointer(m))
|
defer C.free(unsafe.Pointer(m))
|
||||||
@@ -153,10 +153,7 @@ func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) {
|
|||||||
if res := C.gio_jni_GetJavaVM(env, &android.jvm); res != 0 {
|
if res := C.gio_jni_GetJavaVM(env, &android.jvm); res != 0 {
|
||||||
panic("gio: GetJavaVM failed")
|
panic("gio: GetJavaVM failed")
|
||||||
}
|
}
|
||||||
android.writeClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V")
|
|
||||||
android.readClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;")
|
|
||||||
android.appCtx = C.gio_jni_NewGlobalRef(env, ctx)
|
android.appCtx = C.gio_jni_NewGlobalRef(env, ctx)
|
||||||
android.gioCls = C.jclass(C.gio_jni_NewGlobalRef(env, C.jobject(gio)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func JavaVM() uintptr {
|
func JavaVM() uintptr {
|
||||||
@@ -192,6 +189,9 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j
|
|||||||
mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"),
|
mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"),
|
||||||
mpostFrameCallbackOnMainThread: getMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
|
mpostFrameCallbackOnMainThread: getMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
|
||||||
mRegisterFragment: getMethodID(env, class, "registerFragment", "(Ljava/lang/String;)V"),
|
mRegisterFragment: getMethodID(env, class, "registerFragment", "(Ljava/lang/String;)V"),
|
||||||
|
mwakeupMainThread: getMethodID(env, class, "wakeupMainThread", "()V"),
|
||||||
|
mwriteClipboard: getMethodID(env, class, "writeClipboard", "(Ljava/lang/String;)V"),
|
||||||
|
mreadClipboard: getMethodID(env, class, "readClipboard", "()Ljava/lang/String;"),
|
||||||
}
|
}
|
||||||
wopts := <-mainWindow.out
|
wopts := <-mainWindow.out
|
||||||
w.callbacks = wopts.window
|
w.callbacks = wopts.window
|
||||||
@@ -619,33 +619,40 @@ func NewWindow(window Callbacks, opts *Options) error {
|
|||||||
return <-mainWindow.errs
|
return <-mainWindow.errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteClipboard(s string) error {
|
func (w *window) WriteClipboard(s string) {
|
||||||
var jerr error
|
w.runOnMain(func(env *C.JNIEnv) {
|
||||||
jvm := javaVM()
|
|
||||||
if jvm == nil {
|
|
||||||
return errors.New("clipboard: the JVM is not yet available")
|
|
||||||
}
|
|
||||||
runInJVM(jvm, func(env *C.JNIEnv) {
|
|
||||||
jstr := javaString(env, s)
|
jstr := javaString(env, s)
|
||||||
jerr = callStaticVoidMethod(env, android.gioCls, android.writeClipboard, jvalue(android.appCtx), jvalue(jstr))
|
callVoidMethod(env, w.view, w.mwriteClipboard, jvalue(jstr))
|
||||||
})
|
})
|
||||||
return jerr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadClipboard() (string, error) {
|
func (w *window) ReadClipboard() {
|
||||||
var clipboard string
|
w.runOnMain(func(env *C.JNIEnv) {
|
||||||
var jerr error
|
c, err := callObjectMethod(env, w.view, w.mreadClipboard)
|
||||||
jvm := javaVM()
|
|
||||||
if jvm == nil {
|
|
||||||
return "", errors.New("clipboard: the JVM is not yet available")
|
|
||||||
}
|
|
||||||
runInJVM(jvm, func(env *C.JNIEnv) {
|
|
||||||
c, err := callStaticObjectMethod(env, android.gioCls, android.readClipboard, jvalue(android.appCtx))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jerr = err
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clipboard = goString(env, C.jstring(c))
|
content := goString(env, C.jstring(c))
|
||||||
|
w.callbacks.Event(system.ClipboardEvent{Text: content})
|
||||||
})
|
})
|
||||||
return clipboard, jerr
|
}
|
||||||
|
|
||||||
|
// runOnMain runs a function on the Java main thread.
|
||||||
|
func (w *window) runOnMain(f func(env *C.JNIEnv)) {
|
||||||
|
mainFuncs <- f
|
||||||
|
runInJVM(javaVM(), func(env *C.JNIEnv) {
|
||||||
|
callVoidMethod(env, w.view, w.mwakeupMainThread)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//export Java_org_gioui_GioView_scheduleMainFuncs
|
||||||
|
func Java_org_gioui_GioView_scheduleMainFuncs(env *C.JNIEnv, this C.jobject) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case f := <-mainFuncs:
|
||||||
|
f(env)
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user