app: plumbing for physical android key events
This is only the first half of physical key event mapping. The modifiers and hardware key codes will be in a followup CL. I'm splitting this out because it covers two other parts of the problem: first is maintaining a JNIEnv pointer for the main routine, the second is access to the Android unicode key map. The NDK does not have a method to give us the key mapping, so we get to it via GoNativeActivity. Tested with a USB keyboard. I'll attempt an abd-based unit test later, but I suspect it will be difficult to set a device ID. Change-Id: Ie93700d1f2a5d382a9b17cdd668cb4acaa6e4bcc Reviewed-on: https://go-review.googlesource.com/13649 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
parent
0cfb79fc77
commit
377063dfa4
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ package app
|
|||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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:
|
||||
|
|
|
@ -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=` +
|
||||
``
|
||||
|
|
Loading…
Reference in New Issue