mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 01:45:36 +00:00
app/clipboard: implement clipboard for Android
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
|
// +build android
|
||||||
|
|
||||||
|
// Package clipboard accesses the system clipboard.
|
||||||
|
package clipboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gioui.org/app/internal/window"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read the content of the clipboard as a string.
|
||||||
|
func Read() (string, error) {
|
||||||
|
return window.ReadClipboard()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a string to the clipboard.
|
||||||
|
func Write(s string) error {
|
||||||
|
return window.WriteClipboard(s)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
|
// +build android
|
||||||
|
|
||||||
|
package clipboard
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestClipboard(t *testing.T) {
|
||||||
|
const want = "Hello, 世界"
|
||||||
|
if err := Write(want); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
got, err := Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("read %q from the clipboard, wanted %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
public class Gio {
|
public final class Gio {
|
||||||
private final static Object initLock = new Object();
|
private final static Object initLock = new Object();
|
||||||
private static boolean jniLoaded;
|
private static boolean jniLoaded;
|
||||||
|
|
||||||
@@ -35,5 +37,19 @@ public 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
|
|||||||
return (*env)->CallIntMethod(env, obj, methodID);
|
return (*env)->CallIntMethod(env, obj, methodID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gio_jni_CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
|
void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) {
|
||||||
|
(*env)->CallStaticVoidMethodA(env, cls, methodID, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
|
||||||
(*env)->CallVoidMethodA(env, obj, methodID, args);
|
(*env)->CallVoidMethodA(env, obj, methodID, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,3 +71,27 @@ jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr) {
|
|||||||
jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) {
|
jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) {
|
||||||
return (*env)->NewString(env, unicodeChars, len);
|
return (*env)->NewString(env, unicodeChars, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jsize gio_jni_GetStringLength(JNIEnv *env, jstring str) {
|
||||||
|
return (*env)->GetStringLength(env, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str) {
|
||||||
|
return (*env)->GetStringChars(env, str, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
jthrowable gio_jni_ExceptionOccurred(JNIEnv *env) {
|
||||||
|
return (*env)->ExceptionOccurred(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gio_jni_ExceptionClear(JNIEnv *env) {
|
||||||
|
(*env)->ExceptionClear(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
|
||||||
|
return (*env)->CallObjectMethodA(env, obj, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
|
||||||
|
return (*env)->CallStaticObjectMethodA(env, cls, method, args);
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -62,35 +63,54 @@ type jvalue uint64 // The largest JNI type fits in 64 bits.
|
|||||||
|
|
||||||
var dataDirChan = make(chan string, 1)
|
var dataDirChan = make(chan string, 1)
|
||||||
|
|
||||||
var theJVM *C.JavaVM
|
var android struct {
|
||||||
|
// mu protects all fields of this structure. However, once a
|
||||||
|
// non-nil jvm is returned from javaVM, all the other fields may
|
||||||
|
// be accessed unlocked.
|
||||||
|
mu sync.Mutex
|
||||||
|
jvm *C.JavaVM
|
||||||
|
|
||||||
var appContext C.jobject
|
// The global Android App context.
|
||||||
|
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()
|
||||||
|
|
||||||
func jniGetMethodID(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))
|
||||||
s := C.CString(sig)
|
s := C.CString(sig)
|
||||||
defer C.free(unsafe.Pointer(s))
|
defer C.free(unsafe.Pointer(s))
|
||||||
return C.gio_jni_GetMethodID(env, class, m, s)
|
jm := C.gio_jni_GetMethodID(env, class, m, s)
|
||||||
|
if err := exception(env); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return jm
|
||||||
}
|
}
|
||||||
|
|
||||||
func jniGetStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
|
func getStaticMethodID(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))
|
||||||
s := C.CString(sig)
|
s := C.CString(sig)
|
||||||
defer C.free(unsafe.Pointer(s))
|
defer C.free(unsafe.Pointer(s))
|
||||||
return C.gio_jni_GetStaticMethodID(env, class, m, s)
|
jm := C.gio_jni_GetStaticMethodID(env, class, m, s)
|
||||||
|
if err := exception(env); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return jm
|
||||||
}
|
}
|
||||||
|
|
||||||
//export Java_org_gioui_Gio_runGoMain
|
//export Java_org_gioui_Gio_runGoMain
|
||||||
func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) {
|
func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) {
|
||||||
if res := C.gio_jni_GetJavaVM(env, &theJVM); res != 0 {
|
initJVM(env, class, context)
|
||||||
panic("gio: GetJavaVM failed")
|
|
||||||
}
|
|
||||||
dirBytes := C.gio_jni_GetByteArrayElements(env, jdataDir)
|
dirBytes := C.gio_jni_GetByteArrayElements(env, jdataDir)
|
||||||
if dirBytes == nil {
|
if dirBytes == nil {
|
||||||
panic("runGoMain: GetByteArrayElements failed")
|
panic("runGoMain: GetByteArrayElements failed")
|
||||||
@@ -99,17 +119,37 @@ func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyt
|
|||||||
dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
|
dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
|
||||||
dataDirChan <- dataDir
|
dataDirChan <- dataDir
|
||||||
C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
|
C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
|
||||||
appContext = C.gio_jni_NewGlobalRef(env, context)
|
|
||||||
|
|
||||||
runMain()
|
runMain()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) {
|
||||||
|
android.mu.Lock()
|
||||||
|
defer android.mu.Unlock()
|
||||||
|
if res := C.gio_jni_GetJavaVM(env, &android.jvm); res != 0 {
|
||||||
|
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.gioCls = C.jclass(C.gio_jni_NewGlobalRef(env, C.jobject(gio)))
|
||||||
|
}
|
||||||
|
|
||||||
func JavaVM() uintptr {
|
func JavaVM() uintptr {
|
||||||
return uintptr(unsafe.Pointer(theJVM))
|
jvm := javaVM()
|
||||||
|
return uintptr(unsafe.Pointer(jvm))
|
||||||
|
}
|
||||||
|
|
||||||
|
func javaVM() *C.JavaVM {
|
||||||
|
android.mu.Lock()
|
||||||
|
defer android.mu.Unlock()
|
||||||
|
return android.jvm
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppContext() uintptr {
|
func AppContext() uintptr {
|
||||||
return uintptr(appContext)
|
android.mu.Lock()
|
||||||
|
defer android.mu.Unlock()
|
||||||
|
return uintptr(android.appCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDataDir() string {
|
func GetDataDir() string {
|
||||||
@@ -121,13 +161,13 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j
|
|||||||
view = C.gio_jni_NewGlobalRef(env, view)
|
view = C.gio_jni_NewGlobalRef(env, view)
|
||||||
w := &window{
|
w := &window{
|
||||||
view: view,
|
view: view,
|
||||||
mgetDensity: jniGetMethodID(env, class, "getDensity", "()I"),
|
mgetDensity: getMethodID(env, class, "getDensity", "()I"),
|
||||||
mgetFontScale: jniGetMethodID(env, class, "getFontScale", "()F"),
|
mgetFontScale: getMethodID(env, class, "getFontScale", "()F"),
|
||||||
mshowTextInput: jniGetMethodID(env, class, "showTextInput", "()V"),
|
mshowTextInput: getMethodID(env, class, "showTextInput", "()V"),
|
||||||
mhideTextInput: jniGetMethodID(env, class, "hideTextInput", "()V"),
|
mhideTextInput: getMethodID(env, class, "hideTextInput", "()V"),
|
||||||
mpostFrameCallback: jniGetMethodID(env, class, "postFrameCallback", "()V"),
|
mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"),
|
||||||
mpostFrameCallbackOnMainThread: jniGetMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
|
mpostFrameCallbackOnMainThread: getMethodID(env, class, "postFrameCallbackOnMainThread", "()V"),
|
||||||
mRegisterFragment: jniGetMethodID(env, class, "registerFragment", "(Ljava/lang/String;)V"),
|
mRegisterFragment: getMethodID(env, class, "registerFragment", "(Ljava/lang/String;)V"),
|
||||||
}
|
}
|
||||||
wopts := <-mainWindow.out
|
wopts := <-mainWindow.out
|
||||||
w.callbacks = wopts.window
|
w.callbacks = wopts.window
|
||||||
@@ -212,7 +252,7 @@ func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view
|
|||||||
anim := w.animating
|
anim := w.animating
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
if anim {
|
if anim {
|
||||||
runInJVM(func(env *C.JNIEnv) {
|
runInJVM(javaVM(), func(env *C.JNIEnv) {
|
||||||
callVoidMethod(env, w.view, w.mpostFrameCallback)
|
callVoidMethod(env, w.view, w.mpostFrameCallback)
|
||||||
})
|
})
|
||||||
w.draw(false)
|
w.draw(false)
|
||||||
@@ -306,7 +346,7 @@ func (w *window) SetAnimating(anim bool) {
|
|||||||
w.animating = anim
|
w.animating = anim
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
if anim {
|
if anim {
|
||||||
runInJVM(func(env *C.JNIEnv) {
|
runInJVM(javaVM(), func(env *C.JNIEnv) {
|
||||||
callVoidMethod(env, w.view, w.mpostFrameCallbackOnMainThread)
|
callVoidMethod(env, w.view, w.mpostFrameCallbackOnMainThread)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -339,16 +379,19 @@ func (w *window) draw(sync bool) {
|
|||||||
|
|
||||||
type keyMapper func(devId, keyCode C.int32_t) rune
|
type keyMapper func(devId, keyCode C.int32_t) rune
|
||||||
|
|
||||||
func runInJVM(f func(env *C.JNIEnv)) {
|
func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
|
||||||
|
if jvm == nil {
|
||||||
|
panic("nil JVM")
|
||||||
|
}
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
var env *C.JNIEnv
|
var env *C.JNIEnv
|
||||||
var detach bool
|
var detach bool
|
||||||
if res := C.gio_jni_GetEnv(theJVM, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
|
if res := C.gio_jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
|
||||||
if res != C.JNI_EDETACHED {
|
if res != C.JNI_EDETACHED {
|
||||||
panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
|
panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
|
||||||
}
|
}
|
||||||
if C.gio_jni_AttachCurrentThread(theJVM, &env, nil) != C.JNI_OK {
|
if C.gio_jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
|
||||||
panic(errors.New("runInJVM: AttachCurrentThread failed"))
|
panic(errors.New("runInJVM: AttachCurrentThread failed"))
|
||||||
}
|
}
|
||||||
detach = true
|
detach = true
|
||||||
@@ -356,7 +399,7 @@ func runInJVM(f func(env *C.JNIEnv)) {
|
|||||||
|
|
||||||
if detach {
|
if detach {
|
||||||
defer func() {
|
defer func() {
|
||||||
C.gio_jni_DetachCurrentThread(theJVM)
|
C.gio_jni_DetachCurrentThread(jvm)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
f(env)
|
f(env)
|
||||||
@@ -447,7 +490,7 @@ func (w *window) ShowTextInput(show bool) {
|
|||||||
if w.view == 0 {
|
if w.view == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runInJVM(func(env *C.JNIEnv) {
|
runInJVM(javaVM(), func(env *C.JNIEnv) {
|
||||||
if show {
|
if show {
|
||||||
callVoidMethod(env, w.view, w.mshowTextInput)
|
callVoidMethod(env, w.view, w.mshowTextInput)
|
||||||
} else {
|
} else {
|
||||||
@@ -465,7 +508,7 @@ func javaString(env *C.JNIEnv, str string) C.jstring {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) RegisterFragment(del string) {
|
func (w *window) RegisterFragment(del string) {
|
||||||
runInJVM(func(env *C.JNIEnv) {
|
runInJVM(javaVM(), func(env *C.JNIEnv) {
|
||||||
jstr := javaString(env, del)
|
jstr := javaString(env, del)
|
||||||
callVoidMethod(env, w.view, w.mRegisterFragment, jvalue(jstr))
|
callVoidMethod(env, w.view, w.mRegisterFragment, jvalue(jstr))
|
||||||
})
|
})
|
||||||
@@ -478,8 +521,70 @@ func varArgs(args []jvalue) *C.jvalue {
|
|||||||
return (*C.jvalue)(unsafe.Pointer(&args[0]))
|
return (*C.jvalue)(unsafe.Pointer(&args[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) {
|
func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error {
|
||||||
C.gio_jni_CallVoidMethod(env, obj, method, varArgs(args))
|
C.gio_jni_CallStaticVoidMethodA(env, cls, method, varArgs(args))
|
||||||
|
return exception(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
|
||||||
|
res := C.gio_jni_CallStaticObjectMethodA(env, cls, method, varArgs(args))
|
||||||
|
return res, exception(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error {
|
||||||
|
C.gio_jni_CallVoidMethodA(env, obj, method, varArgs(args))
|
||||||
|
return exception(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) {
|
||||||
|
res := C.gio_jni_CallObjectMethodA(env, obj, method, varArgs(args))
|
||||||
|
return res, exception(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// exception returns an error corresponding to the pending
|
||||||
|
// exception, or nil if no exception is pending. The pending
|
||||||
|
// exception is cleared.
|
||||||
|
func exception(env *C.JNIEnv) error {
|
||||||
|
thr := C.gio_jni_ExceptionOccurred(env)
|
||||||
|
if thr == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
C.gio_jni_ExceptionClear(env)
|
||||||
|
cls := getObjectClass(env, C.jobject(thr))
|
||||||
|
toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;")
|
||||||
|
msg, err := callObjectMethod(env, C.jobject(thr), toString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return errors.New(goString(env, C.jstring(msg)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass {
|
||||||
|
if obj == 0 {
|
||||||
|
panic("null object")
|
||||||
|
}
|
||||||
|
cls := C.gio_jni_GetObjectClass(env, C.jobject(obj))
|
||||||
|
if err := exception(env); err != nil {
|
||||||
|
// GetObjectClass should never fail.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return cls
|
||||||
|
}
|
||||||
|
|
||||||
|
// goString converts the JVM jstring to a Go string.
|
||||||
|
func goString(env *C.JNIEnv, str C.jstring) string {
|
||||||
|
if str == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
strlen := C.gio_jni_GetStringLength(env, C.jstring(str))
|
||||||
|
chars := C.gio_jni_GetStringChars(env, C.jstring(str))
|
||||||
|
var utf16Chars []uint16
|
||||||
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars))
|
||||||
|
hdr.Data = uintptr(unsafe.Pointer(chars))
|
||||||
|
hdr.Cap = int(strlen)
|
||||||
|
hdr.Len = int(strlen)
|
||||||
|
utf8 := utf16.Decode(utf16Chars)
|
||||||
|
return string(utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
@@ -489,3 +594,34 @@ func NewWindow(window Callbacks, opts *Options) error {
|
|||||||
mainWindow.in <- windowAndOptions{window, opts}
|
mainWindow.in <- windowAndOptions{window, opts}
|
||||||
return <-mainWindow.errs
|
return <-mainWindow.errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WriteClipboard(s string) error {
|
||||||
|
var jerr error
|
||||||
|
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)
|
||||||
|
jerr = callStaticVoidMethod(env, android.gioCls, android.writeClipboard, jvalue(android.appCtx), jvalue(jstr))
|
||||||
|
})
|
||||||
|
return jerr
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadClipboard() (string, error) {
|
||||||
|
var clipboard string
|
||||||
|
var jerr error
|
||||||
|
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 {
|
||||||
|
jerr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clipboard = goString(env, C.jstring(c))
|
||||||
|
})
|
||||||
|
return clipboard, jerr
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,8 +12,15 @@ __attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetStaticMethodID(JNIE
|
|||||||
__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
|
__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
|
||||||
__attribute__ ((visibility ("hidden"))) jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID);
|
__attribute__ ((visibility ("hidden"))) jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID);
|
||||||
__attribute__ ((visibility ("hidden"))) jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID);
|
__attribute__ ((visibility ("hidden"))) jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
|
__attribute__ ((visibility ("hidden"))) void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args);
|
||||||
|
__attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
|
||||||
__attribute__ ((visibility ("hidden"))) jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr);
|
__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"))) void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes);
|
||||||
__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr);
|
__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr);
|
||||||
__attribute__ ((visibility ("hidden"))) jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
|
__attribute__ ((visibility ("hidden"))) jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
|
||||||
|
__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetStringLength(JNIEnv *env, jstring str);
|
||||||
|
__attribute__ ((visibility ("hidden"))) const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str);
|
||||||
|
__attribute__ ((visibility ("hidden"))) jthrowable gio_jni_ExceptionOccurred(JNIEnv *env);
|
||||||
|
__attribute__ ((visibility ("hidden"))) void gio_jni_ExceptionClear(JNIEnv *env);
|
||||||
|
__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
|
||||||
|
__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args);
|
||||||
|
|||||||
Reference in New Issue
Block a user