github.com/shranet/mobile@v0.0.0-20200814083559-5702cdcd481b/app/android.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build android 6 7 /* 8 Android Apps are built with -buildmode=c-shared. They are loaded by a 9 running Java process. 10 11 Before any entry point is reached, a global constructor initializes the 12 Go runtime, calling all Go init functions. All cgo calls will block 13 until this is complete. Next JNI_OnLoad is called. When that is 14 complete, one of two entry points is called. 15 16 All-Go apps built using NativeActivity enter at ANativeActivity_onCreate. 17 18 Go libraries (for example, those built with gomobile bind) do not use 19 the app package initialization. 20 */ 21 22 package app 23 24 /* 25 #cgo LDFLAGS: -landroid -llog -lEGL -lGLESv2 26 27 #include <android/configuration.h> 28 #include <android/input.h> 29 #include <android/keycodes.h> 30 #include <android/looper.h> 31 #include <android/native_activity.h> 32 #include <android/native_window.h> 33 #include <EGL/egl.h> 34 #include <jni.h> 35 #include <pthread.h> 36 #include <stdlib.h> 37 38 EGLDisplay display; 39 EGLSurface surface; 40 41 char* createEGLSurface(ANativeWindow* window); 42 char* destroyEGLSurface(); 43 int32_t getKeyRune(JNIEnv* env, AInputEvent* e); 44 */ 45 import "C" 46 import ( 47 "fmt" 48 "log" 49 "os" 50 "time" 51 "unsafe" 52 53 "github.com/shranet/mobile/app/internal/callfn" 54 "github.com/shranet/mobile/event/key" 55 "github.com/shranet/mobile/event/lifecycle" 56 "github.com/shranet/mobile/event/paint" 57 "github.com/shranet/mobile/event/size" 58 "github.com/shranet/mobile/event/touch" 59 "github.com/shranet/mobile/geom" 60 "github.com/shranet/mobile/internal/mobileinit" 61 ) 62 63 // RunOnJVM runs fn on a new goroutine locked to an OS thread with a JNIEnv. 64 // 65 // RunOnJVM blocks until the call to fn is complete. Any Java 66 // exception or failure to attach to the JVM is returned as an error. 67 // 68 // The function fn takes vm, the current JavaVM*, 69 // env, the current JNIEnv*, and 70 // ctx, a jobject representing the global android.context.Context. 71 func RunOnJVM(fn func(vm, jniEnv, ctx uintptr) error) error { 72 return mobileinit.RunOnJVM(fn) 73 } 74 75 //export setCurrentContext 76 func setCurrentContext(vm *C.JavaVM, ctx C.jobject) { 77 mobileinit.SetCurrentContext(unsafe.Pointer(vm), uintptr(ctx)) 78 } 79 80 //export callMain 81 func callMain(mainPC uintptr) { 82 for _, name := range []string{"TMPDIR", "PATH", "LD_LIBRARY_PATH"} { 83 n := C.CString(name) 84 os.Setenv(name, C.GoString(C.getenv(n))) 85 C.free(unsafe.Pointer(n)) 86 } 87 88 // Set timezone. 89 // 90 // Note that Android zoneinfo is stored in /system/usr/share/zoneinfo, 91 // but it is in some kind of packed TZiff file that we do not support 92 // yet. As a stopgap, we build a fixed zone using the tm_zone name. 93 var curtime C.time_t 94 var curtm C.struct_tm 95 C.time(&curtime) 96 C.localtime_r(&curtime, &curtm) 97 tzOffset := int(curtm.tm_gmtoff) 98 tz := C.GoString(curtm.tm_zone) 99 time.Local = time.FixedZone(tz, tzOffset) 100 101 go callfn.CallFn(mainPC) 102 } 103 104 //export callFunc 105 func callFunc(fn uintptr) { 106 go callfn.CallFn(fn) 107 } 108 109 //export onStart 110 func onStart(activity *C.ANativeActivity) { 111 } 112 113 //export onResume 114 func onResume(activity *C.ANativeActivity) { 115 } 116 117 //export onSaveInstanceState 118 func onSaveInstanceState(activity *C.ANativeActivity, outSize *C.size_t) unsafe.Pointer { 119 return nil 120 } 121 122 //export onPause 123 func onPause(activity *C.ANativeActivity) { 124 } 125 126 //export onStop 127 func onStop(activity *C.ANativeActivity) { 128 } 129 130 //export onCreate 131 func onCreate(activity *C.ANativeActivity) { 132 // Set the initial configuration. 133 // 134 // Note we use unbuffered channels to talk to the activity loop, and 135 // NativeActivity calls these callbacks sequentially, so configuration 136 // will be set before <-windowRedrawNeeded is processed. 137 windowConfigChange <- windowConfigRead(activity) 138 } 139 140 //export onDestroy 141 func onDestroy(activity *C.ANativeActivity) { 142 } 143 144 //export onWindowFocusChanged 145 func onWindowFocusChanged(activity *C.ANativeActivity, hasFocus C.int) { 146 } 147 148 //export onNativeWindowCreated 149 func onNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) { 150 } 151 152 //export onNativeWindowRedrawNeeded 153 func onNativeWindowRedrawNeeded(activity *C.ANativeActivity, window *C.ANativeWindow) { 154 // Called on orientation change and window resize. 155 // Send a request for redraw, and block this function 156 // until a complete draw and buffer swap is completed. 157 // This is required by the redraw documentation to 158 // avoid bad draws. 159 windowRedrawNeeded <- window 160 <-windowRedrawDone 161 } 162 163 //export onNativeWindowDestroyed 164 func onNativeWindowDestroyed(activity *C.ANativeActivity, window *C.ANativeWindow) { 165 windowDestroyed <- window 166 } 167 168 //export onInputQueueCreated 169 func onInputQueueCreated(activity *C.ANativeActivity, q *C.AInputQueue) { 170 inputQueue <- q 171 <-inputQueueDone 172 } 173 174 //export onInputQueueDestroyed 175 func onInputQueueDestroyed(activity *C.ANativeActivity, q *C.AInputQueue) { 176 inputQueue <- nil 177 <-inputQueueDone 178 } 179 180 //export onContentRectChanged 181 func onContentRectChanged(activity *C.ANativeActivity, rect *C.ARect) { 182 } 183 184 type windowConfig struct { 185 orientation size.Orientation 186 pixelsPerPt float32 187 } 188 189 func windowConfigRead(activity *C.ANativeActivity) windowConfig { 190 aconfig := C.AConfiguration_new() 191 C.AConfiguration_fromAssetManager(aconfig, activity.assetManager) 192 orient := C.AConfiguration_getOrientation(aconfig) 193 density := C.AConfiguration_getDensity(aconfig) 194 C.AConfiguration_delete(aconfig) 195 196 // Calculate the screen resolution. This value is approximate. For example, 197 // a physical resolution of 200 DPI may be quantized to one of the 198 // ACONFIGURATION_DENSITY_XXX values such as 160 or 240. 199 // 200 // A more accurate DPI could possibly be calculated from 201 // https://developer.android.com/reference/android/util/DisplayMetrics.html#xdpi 202 // but this does not appear to be accessible via the NDK. In any case, the 203 // hardware might not even provide a more accurate number, as the system 204 // does not apparently use the reported value. See golang.org/issue/13366 205 // for a discussion. 206 var dpi int 207 switch density { 208 case C.ACONFIGURATION_DENSITY_DEFAULT: 209 dpi = 160 210 case C.ACONFIGURATION_DENSITY_LOW, 211 C.ACONFIGURATION_DENSITY_MEDIUM, 212 213, // C.ACONFIGURATION_DENSITY_TV 213 C.ACONFIGURATION_DENSITY_HIGH, 214 320, // ACONFIGURATION_DENSITY_XHIGH 215 480, // ACONFIGURATION_DENSITY_XXHIGH 216 640: // ACONFIGURATION_DENSITY_XXXHIGH 217 dpi = int(density) 218 case C.ACONFIGURATION_DENSITY_NONE: 219 log.Print("android device reports no screen density") 220 dpi = 72 221 default: 222 log.Printf("android device reports unknown density: %d", density) 223 // All we can do is guess. 224 if density > 0 { 225 dpi = int(density) 226 } else { 227 dpi = 72 228 } 229 } 230 231 o := size.OrientationUnknown 232 switch orient { 233 case C.ACONFIGURATION_ORIENTATION_PORT: 234 o = size.OrientationPortrait 235 case C.ACONFIGURATION_ORIENTATION_LAND: 236 o = size.OrientationLandscape 237 } 238 239 return windowConfig{ 240 orientation: o, 241 pixelsPerPt: float32(dpi) / 72, 242 } 243 } 244 245 //export onConfigurationChanged 246 func onConfigurationChanged(activity *C.ANativeActivity) { 247 // A rotation event first triggers onConfigurationChanged, then 248 // calls onNativeWindowRedrawNeeded. We extract the orientation 249 // here and save it for the redraw event. 250 windowConfigChange <- windowConfigRead(activity) 251 } 252 253 //export onLowMemory 254 func onLowMemory(activity *C.ANativeActivity) { 255 } 256 257 var ( 258 inputQueue = make(chan *C.AInputQueue) 259 inputQueueDone = make(chan struct{}) 260 windowDestroyed = make(chan *C.ANativeWindow) 261 windowRedrawNeeded = make(chan *C.ANativeWindow) 262 windowRedrawDone = make(chan struct{}) 263 windowConfigChange = make(chan windowConfig) 264 ) 265 266 func init() { 267 theApp.registerGLViewportFilter() 268 } 269 270 func main(f func(App)) { 271 mainUserFn = f 272 // TODO: merge the runInputQueue and mainUI functions? 273 go func() { 274 if err := mobileinit.RunOnJVM(runInputQueue); err != nil { 275 log.Fatalf("app: %v", err) 276 } 277 }() 278 // Preserve this OS thread for: 279 // 1. the attached JNI thread 280 // 2. the GL context 281 if err := mobileinit.RunOnJVM(mainUI); err != nil { 282 log.Fatalf("app: %v", err) 283 } 284 } 285 286 var mainUserFn func(App) 287 288 func mainUI(vm, jniEnv, ctx uintptr) error { 289 workAvailable := theApp.worker.WorkAvailable() 290 291 donec := make(chan struct{}) 292 go func() { 293 mainUserFn(theApp) 294 close(donec) 295 }() 296 297 var pixelsPerPt float32 298 var orientation size.Orientation 299 300 for { 301 select { 302 case <-donec: 303 return nil 304 case cfg := <-windowConfigChange: 305 pixelsPerPt = cfg.pixelsPerPt 306 orientation = cfg.orientation 307 case w := <-windowRedrawNeeded: 308 if C.surface == nil { 309 if errStr := C.createEGLSurface(w); errStr != nil { 310 return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError()) 311 } 312 } 313 theApp.sendLifecycle(lifecycle.StageFocused) 314 widthPx := int(C.ANativeWindow_getWidth(w)) 315 heightPx := int(C.ANativeWindow_getHeight(w)) 316 theApp.eventsIn <- size.Event{ 317 WidthPx: widthPx, 318 HeightPx: heightPx, 319 WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt), 320 HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt), 321 PixelsPerPt: pixelsPerPt, 322 Orientation: orientation, 323 } 324 theApp.eventsIn <- paint.Event{External: true} 325 case <-windowDestroyed: 326 if C.surface != nil { 327 if errStr := C.destroyEGLSurface(); errStr != nil { 328 return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError()) 329 } 330 } 331 C.surface = nil 332 theApp.sendLifecycle(lifecycle.StageAlive) 333 case <-workAvailable: 334 theApp.worker.DoWork() 335 case <-theApp.publish: 336 // TODO: compare a generation number to redrawGen for stale paints? 337 if C.surface != nil { 338 // eglSwapBuffers blocks until vsync. 339 if C.eglSwapBuffers(C.display, C.surface) == C.EGL_FALSE { 340 log.Printf("app: failed to swap buffers (%s)", eglGetError()) 341 } 342 } 343 select { 344 case windowRedrawDone <- struct{}{}: 345 default: 346 } 347 theApp.publishResult <- PublishResult{} 348 } 349 } 350 } 351 352 func runInputQueue(vm, jniEnv, ctx uintptr) error { 353 env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer 354 355 // Android loopers select on OS file descriptors, not Go channels, so we 356 // translate the inputQueue channel to an ALooper_wake call. 357 l := C.ALooper_prepare(C.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) 358 pending := make(chan *C.AInputQueue, 1) 359 go func() { 360 for q := range inputQueue { 361 pending <- q 362 C.ALooper_wake(l) 363 } 364 }() 365 366 var q *C.AInputQueue 367 for { 368 if C.ALooper_pollAll(-1, nil, nil, nil) == C.ALOOPER_POLL_WAKE { 369 select { 370 default: 371 case p := <-pending: 372 if q != nil { 373 processEvents(env, q) 374 C.AInputQueue_detachLooper(q) 375 } 376 q = p 377 if q != nil { 378 C.AInputQueue_attachLooper(q, l, 0, nil, nil) 379 } 380 inputQueueDone <- struct{}{} 381 } 382 } 383 if q != nil { 384 processEvents(env, q) 385 } 386 } 387 } 388 389 func processEvents(env *C.JNIEnv, q *C.AInputQueue) { 390 var e *C.AInputEvent 391 for C.AInputQueue_getEvent(q, &e) >= 0 { 392 if C.AInputQueue_preDispatchEvent(q, e) != 0 { 393 continue 394 } 395 processEvent(env, e) 396 C.AInputQueue_finishEvent(q, e, 0) 397 } 398 } 399 400 func processEvent(env *C.JNIEnv, e *C.AInputEvent) { 401 switch C.AInputEvent_getType(e) { 402 case C.AINPUT_EVENT_TYPE_KEY: 403 processKey(env, e) 404 case C.AINPUT_EVENT_TYPE_MOTION: 405 // At most one of the events in this batch is an up or down event; get its index and change. 406 upDownIndex := C.size_t(C.AMotionEvent_getAction(e)&C.AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> C.AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT 407 upDownType := touch.TypeMove 408 switch C.AMotionEvent_getAction(e) & C.AMOTION_EVENT_ACTION_MASK { 409 case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN: 410 upDownType = touch.TypeBegin 411 case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP: 412 upDownType = touch.TypeEnd 413 } 414 415 for i, n := C.size_t(0), C.AMotionEvent_getPointerCount(e); i < n; i++ { 416 t := touch.TypeMove 417 if i == upDownIndex { 418 t = upDownType 419 } 420 theApp.eventsIn <- touch.Event{ 421 X: float32(C.AMotionEvent_getX(e, i)), 422 Y: float32(C.AMotionEvent_getY(e, i)), 423 Sequence: touch.Sequence(C.AMotionEvent_getPointerId(e, i)), 424 Type: t, 425 } 426 } 427 default: 428 log.Printf("unknown input event, type=%d", C.AInputEvent_getType(e)) 429 } 430 } 431 432 func processKey(env *C.JNIEnv, e *C.AInputEvent) { 433 deviceID := C.AInputEvent_getDeviceId(e) 434 if deviceID == 0 { 435 // Software keyboard input, leaving for scribe/IME. 436 return 437 } 438 439 k := key.Event{ 440 Rune: rune(C.getKeyRune(env, e)), 441 Code: convAndroidKeyCode(int32(C.AKeyEvent_getKeyCode(e))), 442 } 443 switch C.AKeyEvent_getAction(e) { 444 case C.AKEY_EVENT_ACTION_DOWN: 445 k.Direction = key.DirPress 446 case C.AKEY_EVENT_ACTION_UP: 447 k.Direction = key.DirRelease 448 default: 449 k.Direction = key.DirNone 450 } 451 // TODO(crawshaw): set Modifiers. 452 theApp.eventsIn <- k 453 } 454 455 func eglGetError() string { 456 switch errNum := C.eglGetError(); errNum { 457 case C.EGL_SUCCESS: 458 return "EGL_SUCCESS" 459 case C.EGL_NOT_INITIALIZED: 460 return "EGL_NOT_INITIALIZED" 461 case C.EGL_BAD_ACCESS: 462 return "EGL_BAD_ACCESS" 463 case C.EGL_BAD_ALLOC: 464 return "EGL_BAD_ALLOC" 465 case C.EGL_BAD_ATTRIBUTE: 466 return "EGL_BAD_ATTRIBUTE" 467 case C.EGL_BAD_CONTEXT: 468 return "EGL_BAD_CONTEXT" 469 case C.EGL_BAD_CONFIG: 470 return "EGL_BAD_CONFIG" 471 case C.EGL_BAD_CURRENT_SURFACE: 472 return "EGL_BAD_CURRENT_SURFACE" 473 case C.EGL_BAD_DISPLAY: 474 return "EGL_BAD_DISPLAY" 475 case C.EGL_BAD_SURFACE: 476 return "EGL_BAD_SURFACE" 477 case C.EGL_BAD_MATCH: 478 return "EGL_BAD_MATCH" 479 case C.EGL_BAD_PARAMETER: 480 return "EGL_BAD_PARAMETER" 481 case C.EGL_BAD_NATIVE_PIXMAP: 482 return "EGL_BAD_NATIVE_PIXMAP" 483 case C.EGL_BAD_NATIVE_WINDOW: 484 return "EGL_BAD_NATIVE_WINDOW" 485 case C.EGL_CONTEXT_LOST: 486 return "EGL_CONTEXT_LOST" 487 default: 488 return fmt.Sprintf("Unknown EGL err: %d", errNum) 489 } 490 } 491 492 func convAndroidKeyCode(aKeyCode int32) key.Code { 493 // Many Android key codes do not map into USB HID codes. 494 // For those, key.CodeUnknown is returned. This switch has all 495 // cases, even the unknown ones, to serve as a documentation 496 // and search aid. 497 switch aKeyCode { 498 case C.AKEYCODE_UNKNOWN: 499 case C.AKEYCODE_SOFT_LEFT: 500 case C.AKEYCODE_SOFT_RIGHT: 501 case C.AKEYCODE_HOME: 502 return key.CodeHome 503 case C.AKEYCODE_BACK: 504 case C.AKEYCODE_CALL: 505 case C.AKEYCODE_ENDCALL: 506 case C.AKEYCODE_0: 507 return key.Code0 508 case C.AKEYCODE_1: 509 return key.Code1 510 case C.AKEYCODE_2: 511 return key.Code2 512 case C.AKEYCODE_3: 513 return key.Code3 514 case C.AKEYCODE_4: 515 return key.Code4 516 case C.AKEYCODE_5: 517 return key.Code5 518 case C.AKEYCODE_6: 519 return key.Code6 520 case C.AKEYCODE_7: 521 return key.Code7 522 case C.AKEYCODE_8: 523 return key.Code8 524 case C.AKEYCODE_9: 525 return key.Code9 526 case C.AKEYCODE_STAR: 527 case C.AKEYCODE_POUND: 528 case C.AKEYCODE_DPAD_UP: 529 case C.AKEYCODE_DPAD_DOWN: 530 case C.AKEYCODE_DPAD_LEFT: 531 case C.AKEYCODE_DPAD_RIGHT: 532 case C.AKEYCODE_DPAD_CENTER: 533 case C.AKEYCODE_VOLUME_UP: 534 return key.CodeVolumeUp 535 case C.AKEYCODE_VOLUME_DOWN: 536 return key.CodeVolumeDown 537 case C.AKEYCODE_POWER: 538 case C.AKEYCODE_CAMERA: 539 case C.AKEYCODE_CLEAR: 540 case C.AKEYCODE_A: 541 return key.CodeA 542 case C.AKEYCODE_B: 543 return key.CodeB 544 case C.AKEYCODE_C: 545 return key.CodeC 546 case C.AKEYCODE_D: 547 return key.CodeD 548 case C.AKEYCODE_E: 549 return key.CodeE 550 case C.AKEYCODE_F: 551 return key.CodeF 552 case C.AKEYCODE_G: 553 return key.CodeG 554 case C.AKEYCODE_H: 555 return key.CodeH 556 case C.AKEYCODE_I: 557 return key.CodeI 558 case C.AKEYCODE_J: 559 return key.CodeJ 560 case C.AKEYCODE_K: 561 return key.CodeK 562 case C.AKEYCODE_L: 563 return key.CodeL 564 case C.AKEYCODE_M: 565 return key.CodeM 566 case C.AKEYCODE_N: 567 return key.CodeN 568 case C.AKEYCODE_O: 569 return key.CodeO 570 case C.AKEYCODE_P: 571 return key.CodeP 572 case C.AKEYCODE_Q: 573 return key.CodeQ 574 case C.AKEYCODE_R: 575 return key.CodeR 576 case C.AKEYCODE_S: 577 return key.CodeS 578 case C.AKEYCODE_T: 579 return key.CodeT 580 case C.AKEYCODE_U: 581 return key.CodeU 582 case C.AKEYCODE_V: 583 return key.CodeV 584 case C.AKEYCODE_W: 585 return key.CodeW 586 case C.AKEYCODE_X: 587 return key.CodeX 588 case C.AKEYCODE_Y: 589 return key.CodeY 590 case C.AKEYCODE_Z: 591 return key.CodeZ 592 case C.AKEYCODE_COMMA: 593 return key.CodeComma 594 case C.AKEYCODE_PERIOD: 595 return key.CodeFullStop 596 case C.AKEYCODE_ALT_LEFT: 597 return key.CodeLeftAlt 598 case C.AKEYCODE_ALT_RIGHT: 599 return key.CodeRightAlt 600 case C.AKEYCODE_SHIFT_LEFT: 601 return key.CodeLeftShift 602 case C.AKEYCODE_SHIFT_RIGHT: 603 return key.CodeRightShift 604 case C.AKEYCODE_TAB: 605 return key.CodeTab 606 case C.AKEYCODE_SPACE: 607 return key.CodeSpacebar 608 case C.AKEYCODE_SYM: 609 case C.AKEYCODE_EXPLORER: 610 case C.AKEYCODE_ENVELOPE: 611 case C.AKEYCODE_ENTER: 612 return key.CodeReturnEnter 613 case C.AKEYCODE_DEL: 614 return key.CodeDeleteBackspace 615 case C.AKEYCODE_GRAVE: 616 return key.CodeGraveAccent 617 case C.AKEYCODE_MINUS: 618 return key.CodeHyphenMinus 619 case C.AKEYCODE_EQUALS: 620 return key.CodeEqualSign 621 case C.AKEYCODE_LEFT_BRACKET: 622 return key.CodeLeftSquareBracket 623 case C.AKEYCODE_RIGHT_BRACKET: 624 return key.CodeRightSquareBracket 625 case C.AKEYCODE_BACKSLASH: 626 return key.CodeBackslash 627 case C.AKEYCODE_SEMICOLON: 628 return key.CodeSemicolon 629 case C.AKEYCODE_APOSTROPHE: 630 return key.CodeApostrophe 631 case C.AKEYCODE_SLASH: 632 return key.CodeSlash 633 case C.AKEYCODE_AT: 634 case C.AKEYCODE_NUM: 635 case C.AKEYCODE_HEADSETHOOK: 636 case C.AKEYCODE_FOCUS: 637 case C.AKEYCODE_PLUS: 638 case C.AKEYCODE_MENU: 639 case C.AKEYCODE_NOTIFICATION: 640 case C.AKEYCODE_SEARCH: 641 case C.AKEYCODE_MEDIA_PLAY_PAUSE: 642 case C.AKEYCODE_MEDIA_STOP: 643 case C.AKEYCODE_MEDIA_NEXT: 644 case C.AKEYCODE_MEDIA_PREVIOUS: 645 case C.AKEYCODE_MEDIA_REWIND: 646 case C.AKEYCODE_MEDIA_FAST_FORWARD: 647 case C.AKEYCODE_MUTE: 648 case C.AKEYCODE_PAGE_UP: 649 return key.CodePageUp 650 case C.AKEYCODE_PAGE_DOWN: 651 return key.CodePageDown 652 case C.AKEYCODE_PICTSYMBOLS: 653 case C.AKEYCODE_SWITCH_CHARSET: 654 case C.AKEYCODE_BUTTON_A: 655 case C.AKEYCODE_BUTTON_B: 656 case C.AKEYCODE_BUTTON_C: 657 case C.AKEYCODE_BUTTON_X: 658 case C.AKEYCODE_BUTTON_Y: 659 case C.AKEYCODE_BUTTON_Z: 660 case C.AKEYCODE_BUTTON_L1: 661 case C.AKEYCODE_BUTTON_R1: 662 case C.AKEYCODE_BUTTON_L2: 663 case C.AKEYCODE_BUTTON_R2: 664 case C.AKEYCODE_BUTTON_THUMBL: 665 case C.AKEYCODE_BUTTON_THUMBR: 666 case C.AKEYCODE_BUTTON_START: 667 case C.AKEYCODE_BUTTON_SELECT: 668 case C.AKEYCODE_BUTTON_MODE: 669 case C.AKEYCODE_ESCAPE: 670 return key.CodeEscape 671 case C.AKEYCODE_FORWARD_DEL: 672 return key.CodeDeleteForward 673 case C.AKEYCODE_CTRL_LEFT: 674 return key.CodeLeftControl 675 case C.AKEYCODE_CTRL_RIGHT: 676 return key.CodeRightControl 677 case C.AKEYCODE_CAPS_LOCK: 678 return key.CodeCapsLock 679 case C.AKEYCODE_SCROLL_LOCK: 680 case C.AKEYCODE_META_LEFT: 681 return key.CodeLeftGUI 682 case C.AKEYCODE_META_RIGHT: 683 return key.CodeRightGUI 684 case C.AKEYCODE_FUNCTION: 685 case C.AKEYCODE_SYSRQ: 686 case C.AKEYCODE_BREAK: 687 case C.AKEYCODE_MOVE_HOME: 688 case C.AKEYCODE_MOVE_END: 689 case C.AKEYCODE_INSERT: 690 return key.CodeInsert 691 case C.AKEYCODE_FORWARD: 692 case C.AKEYCODE_MEDIA_PLAY: 693 case C.AKEYCODE_MEDIA_PAUSE: 694 case C.AKEYCODE_MEDIA_CLOSE: 695 case C.AKEYCODE_MEDIA_EJECT: 696 case C.AKEYCODE_MEDIA_RECORD: 697 case C.AKEYCODE_F1: 698 return key.CodeF1 699 case C.AKEYCODE_F2: 700 return key.CodeF2 701 case C.AKEYCODE_F3: 702 return key.CodeF3 703 case C.AKEYCODE_F4: 704 return key.CodeF4 705 case C.AKEYCODE_F5: 706 return key.CodeF5 707 case C.AKEYCODE_F6: 708 return key.CodeF6 709 case C.AKEYCODE_F7: 710 return key.CodeF7 711 case C.AKEYCODE_F8: 712 return key.CodeF8 713 case C.AKEYCODE_F9: 714 return key.CodeF9 715 case C.AKEYCODE_F10: 716 return key.CodeF10 717 case C.AKEYCODE_F11: 718 return key.CodeF11 719 case C.AKEYCODE_F12: 720 return key.CodeF12 721 case C.AKEYCODE_NUM_LOCK: 722 return key.CodeKeypadNumLock 723 case C.AKEYCODE_NUMPAD_0: 724 return key.CodeKeypad0 725 case C.AKEYCODE_NUMPAD_1: 726 return key.CodeKeypad1 727 case C.AKEYCODE_NUMPAD_2: 728 return key.CodeKeypad2 729 case C.AKEYCODE_NUMPAD_3: 730 return key.CodeKeypad3 731 case C.AKEYCODE_NUMPAD_4: 732 return key.CodeKeypad4 733 case C.AKEYCODE_NUMPAD_5: 734 return key.CodeKeypad5 735 case C.AKEYCODE_NUMPAD_6: 736 return key.CodeKeypad6 737 case C.AKEYCODE_NUMPAD_7: 738 return key.CodeKeypad7 739 case C.AKEYCODE_NUMPAD_8: 740 return key.CodeKeypad8 741 case C.AKEYCODE_NUMPAD_9: 742 return key.CodeKeypad9 743 case C.AKEYCODE_NUMPAD_DIVIDE: 744 return key.CodeKeypadSlash 745 case C.AKEYCODE_NUMPAD_MULTIPLY: 746 return key.CodeKeypadAsterisk 747 case C.AKEYCODE_NUMPAD_SUBTRACT: 748 return key.CodeKeypadHyphenMinus 749 case C.AKEYCODE_NUMPAD_ADD: 750 return key.CodeKeypadPlusSign 751 case C.AKEYCODE_NUMPAD_DOT: 752 return key.CodeKeypadFullStop 753 case C.AKEYCODE_NUMPAD_COMMA: 754 case C.AKEYCODE_NUMPAD_ENTER: 755 return key.CodeKeypadEnter 756 case C.AKEYCODE_NUMPAD_EQUALS: 757 return key.CodeKeypadEqualSign 758 case C.AKEYCODE_NUMPAD_LEFT_PAREN: 759 case C.AKEYCODE_NUMPAD_RIGHT_PAREN: 760 case C.AKEYCODE_VOLUME_MUTE: 761 return key.CodeMute 762 case C.AKEYCODE_INFO: 763 case C.AKEYCODE_CHANNEL_UP: 764 case C.AKEYCODE_CHANNEL_DOWN: 765 case C.AKEYCODE_ZOOM_IN: 766 case C.AKEYCODE_ZOOM_OUT: 767 case C.AKEYCODE_TV: 768 case C.AKEYCODE_WINDOW: 769 case C.AKEYCODE_GUIDE: 770 case C.AKEYCODE_DVR: 771 case C.AKEYCODE_BOOKMARK: 772 case C.AKEYCODE_CAPTIONS: 773 case C.AKEYCODE_SETTINGS: 774 case C.AKEYCODE_TV_POWER: 775 case C.AKEYCODE_TV_INPUT: 776 case C.AKEYCODE_STB_POWER: 777 case C.AKEYCODE_STB_INPUT: 778 case C.AKEYCODE_AVR_POWER: 779 case C.AKEYCODE_AVR_INPUT: 780 case C.AKEYCODE_PROG_RED: 781 case C.AKEYCODE_PROG_GREEN: 782 case C.AKEYCODE_PROG_YELLOW: 783 case C.AKEYCODE_PROG_BLUE: 784 case C.AKEYCODE_APP_SWITCH: 785 case C.AKEYCODE_BUTTON_1: 786 case C.AKEYCODE_BUTTON_2: 787 case C.AKEYCODE_BUTTON_3: 788 case C.AKEYCODE_BUTTON_4: 789 case C.AKEYCODE_BUTTON_5: 790 case C.AKEYCODE_BUTTON_6: 791 case C.AKEYCODE_BUTTON_7: 792 case C.AKEYCODE_BUTTON_8: 793 case C.AKEYCODE_BUTTON_9: 794 case C.AKEYCODE_BUTTON_10: 795 case C.AKEYCODE_BUTTON_11: 796 case C.AKEYCODE_BUTTON_12: 797 case C.AKEYCODE_BUTTON_13: 798 case C.AKEYCODE_BUTTON_14: 799 case C.AKEYCODE_BUTTON_15: 800 case C.AKEYCODE_BUTTON_16: 801 case C.AKEYCODE_LANGUAGE_SWITCH: 802 case C.AKEYCODE_MANNER_MODE: 803 case C.AKEYCODE_3D_MODE: 804 case C.AKEYCODE_CONTACTS: 805 case C.AKEYCODE_CALENDAR: 806 case C.AKEYCODE_MUSIC: 807 case C.AKEYCODE_CALCULATOR: 808 } 809 /* Defined in an NDK API version beyond what we use today: 810 C.AKEYCODE_ASSIST 811 C.AKEYCODE_BRIGHTNESS_DOWN 812 C.AKEYCODE_BRIGHTNESS_UP 813 C.AKEYCODE_EISU 814 C.AKEYCODE_HENKAN 815 C.AKEYCODE_KANA 816 C.AKEYCODE_KATAKANA_HIRAGANA 817 C.AKEYCODE_MEDIA_AUDIO_TRACK 818 C.AKEYCODE_MUHENKAN 819 C.AKEYCODE_RO 820 C.AKEYCODE_YEN 821 C.AKEYCODE_ZENKAKU_HANKAKU 822 */ 823 return key.CodeUnknown 824 }