diff --git a/app/GoNativeActivity.java b/app/GoNativeActivity.java index 4a1234b..7017853 100644 --- a/app/GoNativeActivity.java +++ b/app/GoNativeActivity.java @@ -7,6 +7,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; +import android.view.KeyCharacterMap; public class GoNativeActivity extends NativeActivity { private static GoNativeActivity goNativeActivity; @@ -20,6 +21,21 @@ public class GoNativeActivity extends NativeActivity { return getCacheDir().getAbsolutePath(); } + int getRune(int deviceId, int keyCode, int metaState) { + try { + int rune = KeyCharacterMap.load(deviceId).get(keyCode, metaState); + if (rune == 0) { + return -1; + } + return rune; + } catch (KeyCharacterMap.UnavailableException e) { + return -1; + } catch (Exception e) { + Log.e("Go", "exception reading KeyCharacterMap", e); + return -1; + } + } + private void load() { // Interestingly, NativeActivity uses a different method // to find native code to execute, avoiding diff --git a/app/android.c b/app/android.c index f673dca..30b4083 100644 --- a/app/android.c +++ b/app/android.c @@ -35,6 +35,8 @@ static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const return m; } +jmethodID key_rune_method; + jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) { @@ -44,6 +46,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { // Load classes here, which uses the correct ClassLoader. current_ctx_clazz = find_class(env, "org/golang/app/GoNativeActivity"); current_ctx_clazz = (jclass)(*env)->NewGlobalRef(env, current_ctx_clazz); + key_rune_method = find_method(env, current_ctx_clazz, "getRune", "(III)I"); return JNI_VERSION_1_6; } @@ -63,8 +66,8 @@ void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_ JNIEnv* env = activity->env; // Note that activity->clazz is mis-named. - JavaVM* current_vm = activity->vm; - jobject current_ctx = activity->clazz; + current_vm = activity->vm; + current_ctx = activity->clazz; setCurrentContext(current_vm, (*env)->NewGlobalRef(env, current_ctx)); @@ -177,12 +180,12 @@ char* destroyEGLSurface() { return NULL; } -char* attachJNI(void *vm) { - JavaVM* current_vm = (JavaVM*)(vm); - JNIEnv* env; +char* attachJNI(JNIEnv** envp) { + if (current_vm == NULL) { + return "current_vm not set"; + } - int attached = 0; - switch ((*current_vm)->GetEnv(current_vm, (void**)&env, JNI_VERSION_1_6)) { + switch ((*current_vm)->GetEnv(current_vm, (void**)envp, JNI_VERSION_1_6)) { case JNI_OK: return NULL; case JNI_EDETACHED: @@ -190,7 +193,7 @@ char* attachJNI(void *vm) { // DetachCurrentThread, however attachJNI is called for // the duration of the main function, which exits when the // process exits. We let Unix take care of thread cleanup. - if ((*current_vm)->AttachCurrentThread(current_vm, &env, 0) != 0) { + if ((*current_vm)->AttachCurrentThread(current_vm, envp, 0) != 0) { return "cannot attach JVM"; } return NULL; @@ -200,3 +203,14 @@ char* attachJNI(void *vm) { return "unknown GetEnv error"; } } + +int32_t getKeyRune(JNIEnv* env, AInputEvent* e) { + return (int32_t)(*env)->CallIntMethod( + env, + current_ctx, + key_rune_method, + AInputEvent_getDeviceId(e), + AKeyEvent_getKeyCode(e), + AKeyEvent_getMetaState(e) + ); +} diff --git a/app/android.go b/app/android.go index 9cb47bd..ac28749 100644 --- a/app/android.go +++ b/app/android.go @@ -32,6 +32,8 @@ package app #include #include +JavaVM* current_vm; +jobject current_ctx; jclass current_ctx_clazz; jclass app_find_class(JNIEnv* env, const char* name); @@ -42,7 +44,8 @@ EGLSurface surface; char* initEGLDisplay(); char* createEGLSurface(ANativeWindow* window); char* destroyEGLSurface(); -char* attachJNI(void* vm); +char* attachJNI(JNIEnv**); +int32_t getKeyRune(JNIEnv* env, AInputEvent* e); */ import "C" import ( @@ -54,6 +57,7 @@ import ( "unsafe" "golang.org/x/mobile/app/internal/callfn" + "golang.org/x/mobile/event/key" "golang.org/x/mobile/event/lifecycle" "golang.org/x/mobile/event/paint" "golang.org/x/mobile/event/size" @@ -250,7 +254,8 @@ func main(f func(App)) { // Calls into NativeActivity functions must be made from // a thread attached to the JNI. - if errStr := C.attachJNI(mobileinit.Context{}.JavaVM()); errStr != nil { + var env *C.JNIEnv + if errStr := C.attachJNI(&env); errStr != nil { log.Fatalf("app: %s", C.GoString(errStr)) } @@ -279,7 +284,7 @@ func main(f func(App)) { for { if q != nil { - processEvents(q) + processEvents(env, q) } select { case <-windowCreated: @@ -342,21 +347,21 @@ func main(f func(App)) { } } -func processEvents(queue *C.AInputQueue) { +func processEvents(env *C.JNIEnv, queue *C.AInputQueue) { var event *C.AInputEvent for C.AInputQueue_getEvent(queue, &event) >= 0 { if C.AInputQueue_preDispatchEvent(queue, event) != 0 { continue } - processEvent(event) + processEvent(env, event) C.AInputQueue_finishEvent(queue, event, 0) } } -func processEvent(e *C.AInputEvent) { +func processEvent(env *C.JNIEnv, e *C.AInputEvent) { switch C.AInputEvent_getType(e) { case C.AINPUT_EVENT_TYPE_KEY: - log.Printf("TODO input event: key") + processKey(env, e) case C.AINPUT_EVENT_TYPE_MOTION: // At most one of the events in this batch is an up or down event; get its index and change. upDownIndex := C.size_t(C.AMotionEvent_getAction(e)&C.AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> C.AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT @@ -385,6 +390,32 @@ func processEvent(e *C.AInputEvent) { } } +func processKey(env *C.JNIEnv, e *C.AInputEvent) { + deviceID := C.AInputEvent_getDeviceId(e) + if deviceID == 0 { + // Software keyboard input, leaving for scribe/IME. + return + } + + k := key.Event{ + Rune: rune(C.getKeyRune(env, e)), + } + switch C.AKeyEvent_getAction(e) { + case C.AKEY_STATE_DOWN: + k.Direction = key.DirPress + case C.AKEY_STATE_UP: + k.Direction = key.DirRelease + default: + k.Direction = key.DirNone + } + + // TODO(crawshaw): set Modifiers. + // TODO(crawshaw): map Android key codes to USB HID key codes. + //keyCode := C.AKeyEvent_getKeyCode(e) + + eventsIn <- k +} + func eglGetError() string { switch errNum := C.eglGetError(); errNum { case C.EGL_SUCCESS: diff --git a/cmd/gomobile/dex.go b/cmd/gomobile/dex.go index b1a191a..4886bc9 100644 --- a/cmd/gomobile/dex.go +++ b/cmd/gomobile/dex.go @@ -6,39 +6,46 @@ package main -var dexStr = `ZGV4CjAzNQBgMPcbh5xnMiJMhJmv7LSwWLsd8n8QqVIUBwAAcAAAAHhWNBIAAAAAAAAAAH` + - `QGAAApAAAAcAAAAA8AAAAUAQAADAAAAFABAAACAAAA4AEAABAAAADwAQAAAQAAAHACAACE` + - `BAAAkAIAAJ4DAACmAwAAqgMAAMEDAADEAwAAyQMAAM8DAADSAwAA1gMAANsDAAD5AwAAGg` + - `QAADQEAABXBAAAfAQAAJEEAAClBAAAtQQAAMwEAADgBAAA9AQAAAsFAAAuBQAAMQUAADUF` + - `AABLBQAATgUAAF8FAABwBQAAfQUAAIsFAACWBQAAqQUAALQFAAC/BQAA0QUAANcFAADkBQ` + - `AA+AUAACEGAAArBgAAAwAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAA` + - `ABIAAAATAAAAFAAAABUAAAAWAAAABAAAAAAAAAB0AwAABQAAAAAAAAB8AwAABgAAAAIAAA` + - `AAAAAABgAAAAMAAAAAAAAACAAAAAQAAACIAwAABgAAAAUAAAAAAAAABgAAAAgAAAAAAAAA` + - `BgAAAAoAAAAAAAAABwAAAAoAAACQAwAAFgAAAA4AAAAAAAAAFwAAAA4AAACYAwAAFwAAAA` + - `4AAACQAwAABAAGACcAAAANAA0AIgAAAAEACQAAAAAAAQAKACgAAAADAAIAHQAAAAUABAAb` + - `AAAABgAIACAAAAAHAAAAGQAAAAcAAQAZAAAACAAHABoAAAALAAsAJAAAAA0ACQAAAAAADQ` + - `AGABwAAAANAAMAHgAAAA0ABQAfAAAADQAHACEAAAANAAkAIwAAAA0ACgAoAAAADQAAAAEA` + - `AAABAAAAAAAAAAIAAAAAAAAAWQYAAAAAAAABAAEAAQAAADUGAAAGAAAAcBAAAAAAaQABAA` + - `4ABAABAAMAAQA8BgAAMwAAAG4QDAADAAwAbhALAAMADAFuEAIAAQAMARMCgABuMAMAEAIM` + - `AFQBAAA5AQoAGgABABoBJgBxIAUAEAAOAFQAAAAaARgAbiAEABAADABxEAgAAAAo9A0AGg` + - `EBABoCJQBxMAYAIQAo6wAAAAAAACkAAQABAQkqAgABAAEAAABMBgAACQAAAG4QCgABAAwA` + - `bhAHAAAADAARAAAAAgACAAIAAABRBgAABwAAAHAQDgAAAG8gAQAQAA4AAAACAAAACgAKAA` + - `MAAAAKAAoADAAAAAIAAAACAAAAAQAAAAoAAAABAAAABgAGPGluaXQ+AAJHbwAVR29OYXRp` + - `dmVBY3Rpdml0eS5qYXZhAAFJAANJTEwABElMTEwAAUwAAkxMAANMTEkAHExhbmRyb2lkL2` + - `FwcC9OYXRpdmVBY3Rpdml0eTsAH0xhbmRyb2lkL2NvbnRlbnQvQ29tcG9uZW50TmFtZTsA` + - `GExhbmRyb2lkL2NvbnRlbnQvSW50ZW50OwAhTGFuZHJvaWQvY29udGVudC9wbS9BY3Rpdm` + - `l0eUluZm87ACNMYW5kcm9pZC9jb250ZW50L3BtL1BhY2thZ2VNYW5hZ2VyOwATTGFuZHJv` + - `aWQvb3MvQnVuZGxlOwASTGFuZHJvaWQvdXRpbC9Mb2c7AA5MamF2YS9pby9GaWxlOwAVTG` + - `phdmEvbGFuZy9FeGNlcHRpb247ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcv` + - `U3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ACFMb3JnL2dvbGFuZy9hcHAvR29OYX` + - `RpdmVBY3Rpdml0eTsAAVYAAlZMABRhbmRyb2lkLmFwcC5saWJfbmFtZQABZQAPZ2V0QWJz` + - `b2x1dGVQYXRoAA9nZXRBY3Rpdml0eUluZm8AC2dldENhY2hlRGlyAAxnZXRDb21wb25lbn` + - `QACWdldEludGVudAARZ2V0UGFja2FnZU1hbmFnZXIACWdldFN0cmluZwAJZ2V0VG1wZGly` + - `ABBnb05hdGl2ZUFjdGl2aXR5AARsb2FkAAtsb2FkTGlicmFyeQASbG9hZExpYnJhcnkgZm` + - `FpbGVkACdsb2FkTGlicmFyeTogbm8gbWFuaWZlc3QgbWV0YWRhdGEgZm91bmQACG1ldGFE` + - `YXRhAAhvbkNyZWF0ZQAPAAcOPC0AIQAHDgESEEt/Ansdh0seABQABw4AMAEABw48PAABAA` + - `ICAQoJgYAEkAUFAqwFDQCwBgIB1AYAAAANAAAAAAAAAAEAAAAAAAAAAQAAACkAAABwAAAA` + - `AgAAAA8AAAAUAQAAAwAAAAwAAABQAQAABAAAAAIAAADgAQAABQAAABAAAADwAQAABgAAAA` + - `EAAABwAgAAASAAAAQAAACQAgAAARAAAAUAAAB0AwAAAiAAACkAAACeAwAAAyAAAAQAAAA1` + - `BgAAACAAAAEAAABZBgAAABAAAAEAAAB0BgAA` + +var dexStr = `ZGV4CjAzNQBIdedFANrzNlK7iW3fcjAaCV9Jtg0Md7iQCAAAcAAAAHhWNBIAAAAAAAAAAP` + + `AHAAAxAAAAcAAAABEAAAA0AQAADwAAAHgBAAACAAAALAIAABMAAAA8AgAAAQAAANQCAACc` + + `BQAA9AIAAHIEAAB6BAAAfgQAAJUEAACYBAAAnQQAAKMEAACoBAAArgQAALEEAAC1BAAAuQ` + + `QAAL4EAADcBAAA/QQAABcFAAA6BQAAXwUAAHQFAACIBQAAvQUAAN0FAADtBQAABAYAABgG` + + `AAAsBgAAQwYAAGYGAABpBgAAbQYAAIMGAACGBgAAqQYAAK4GAAC/BgAA0AYAAN0GAADrBg` + + `AA9gYAAAkHAAASBwAAHQcAACgHAAA6BwAAQAcAAE0HAABhBwAAigcAAJQHAAADAAAADAAA` + + `AA0AAAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABkAAA` + + `AaAAAAGwAAAAQAAAAAAAAALAQAAAUAAAAAAAAANAQAAAYAAAAAAAAAQAQAAAcAAAAAAAAA` + + `SAQAAAgAAAACAAAAAAAAAAgAAAADAAAAAAAAAAsAAAAEAAAAVAQAAAgAAAAFAAAAAAAAAA` + + `kAAAAJAAAAXAQAAAgAAAAKAAAAAAAAAAgAAAAMAAAAAAAAAAoAAAAMAAAAZAQAABsAAAAQ` + + `AAAAAAAAABwAAAAQAAAAbAQAABwAAAAQAAAAZAQAAAQABgAvAAAADwAPACoAAAABAAwAAA` + + `AAAAEADQAwAAAAAwAEACQAAAAFAAYAIgAAAAYACwAoAAAABwACAB4AAAAHAAMAHgAAAAkA` + + `AAAgAAAACQAIACsAAAAKAAoAIQAAAA0ADgAsAAAADwAMAAAAAAAPAAkAIwAAAA8ABQAlAA` + + `AADwAHACYAAAAPAAEAJwAAAA8ACgApAAAADwAMACsAAAAPAA0AMAAAAA8AAAABAAAAAQAA` + + `AAAAAAACAAAAAAAAANIHAAAAAAAAAQABAAEAAACeBwAABgAAAHAQAAAAAGkAAQAOAAQAAQ` + + `ADAAEApQcAADMAAABuEA4AAwAMAG4QDQADAAwBbhACAAEADAETAoAAbjADABACDABUAQAA` + + `OQEKABoAAQAaAS4AcSAFABAADgBUAAAAGgEdAG4gBAAQAAwAcRAKAAAAKPQNABoBAQAaAi` + + `0AcTAGACEAKOsAAAAAAAApAAEAAQELKggABAADAAEAtQcAABkAAAAS8HEQCAAFAAwBbjAH` + + `AGEHCgE5AQMADwABECj+DQEaAgEAGgMfAHEwBgAyASj1DQEo8wAAAQAAAAcAAQABAggXCw` + + `4AAAIAAQABAAAAxQcAAAkAAABuEAwAAQAMAG4QCQAAAAwAEQAAAAIAAgACAAAAygcAAAcA` + + `AABwEBEAAABvIAEAEAAOAAAAAgAAAAAAAAADAAAAAAAAAAAAAAACAAAADAAMAAMAAAAMAA` + + `wADgAAAAIAAAACAAAAAQAAAAAAAAABAAAADAAAAAEAAAAGAAY8aW5pdD4AAkdvABVHb05h` + + `dGl2ZUFjdGl2aXR5LmphdmEAAUkAA0lJSQAESUlJSQADSUxMAARJTExMAAFMAAJMSQACTE` + + `wAA0xMSQAcTGFuZHJvaWQvYXBwL05hdGl2ZUFjdGl2aXR5OwAfTGFuZHJvaWQvY29udGVu` + + `dC9Db21wb25lbnROYW1lOwAYTGFuZHJvaWQvY29udGVudC9JbnRlbnQ7ACFMYW5kcm9pZC` + + `9jb250ZW50L3BtL0FjdGl2aXR5SW5mbzsAI0xhbmRyb2lkL2NvbnRlbnQvcG0vUGFja2Fn` + + `ZU1hbmFnZXI7ABNMYW5kcm9pZC9vcy9CdW5kbGU7ABJMYW5kcm9pZC91dGlsL0xvZzsAM0` + + `xhbmRyb2lkL3ZpZXcvS2V5Q2hhcmFjdGVyTWFwJFVuYXZhaWxhYmxlRXhjZXB0aW9uOwAe` + + `TGFuZHJvaWQvdmlldy9LZXlDaGFyYWN0ZXJNYXA7AA5MamF2YS9pby9GaWxlOwAVTGphdm` + + `EvbGFuZy9FeGNlcHRpb247ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lz` + + `dGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ACFMb3JnL2dvbGFuZy9hcHAvR29OYXRpdm` + + `VBY3Rpdml0eTsAAVYAAlZMABRhbmRyb2lkLmFwcC5saWJfbmFtZQABZQAhZXhjZXB0aW9u` + + `IHJlYWRpbmcgS2V5Q2hhcmFjdGVyTWFwAANnZXQAD2dldEFic29sdXRlUGF0aAAPZ2V0QW` + + `N0aXZpdHlJbmZvAAtnZXRDYWNoZURpcgAMZ2V0Q29tcG9uZW50AAlnZXRJbnRlbnQAEWdl` + + `dFBhY2thZ2VNYW5hZ2VyAAdnZXRSdW5lAAlnZXRTdHJpbmcACWdldFRtcGRpcgAQZ29OYX` + + `RpdmVBY3Rpdml0eQAEbG9hZAALbG9hZExpYnJhcnkAEmxvYWRMaWJyYXJ5IGZhaWxlZAAn` + + `bG9hZExpYnJhcnk6IG5vIG1hbmlmZXN0IG1ldGFkYXRhIGZvdW5kAAhtZXRhRGF0YQAIb2` + + `5DcmVhdGUAEAAHDjwtADEABw4BEhBLfwJ7HYdLHgAaAwAAAAcdhzQCeywgHoMAFQAHDgBA` + + `AQAHDjw8AAEAAgMBCguBgAT0BQYCkAYPAJQHAQDoBwIBjAgAAA0AAAAAAAAAAQAAAAAAAA` + + `ABAAAAMQAAAHAAAAACAAAAEQAAADQBAAADAAAADwAAAHgBAAAEAAAAAgAAACwCAAAFAAAA` + + `EwAAADwCAAAGAAAAAQAAANQCAAABIAAABQAAAPQCAAABEAAACAAAACwEAAACIAAAMQAAAH` + + `IEAAADIAAABQAAAJ4HAAAAIAAAAQAAANIHAAAAEAAAAQAAAPAHAAA=` + ``