123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500 |
- // SPDX-License-Identifier: Unlicense OR MIT
- package app
- /*
- #cgo CFLAGS: -Werror
- #cgo LDFLAGS: -landroid
- #include <android/native_window_jni.h>
- #include <android/configuration.h>
- #include <android/keycodes.h>
- #include <android/input.h>
- #include <stdlib.h>
- static jint jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
- return (*vm)->GetEnv(vm, (void **)env, version);
- }
- static jint jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) {
- return (*env)->GetJavaVM(env, jvm);
- }
- static jint jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
- return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
- }
- static jint jni_DetachCurrentThread(JavaVM *vm) {
- return (*vm)->DetachCurrentThread(vm);
- }
- static jobject jni_NewGlobalRef(JNIEnv *env, jobject obj) {
- return (*env)->NewGlobalRef(env, obj);
- }
- static void jni_DeleteGlobalRef(JNIEnv *env, jobject obj) {
- (*env)->DeleteGlobalRef(env, obj);
- }
- static jclass jni_GetObjectClass(JNIEnv *env, jobject obj) {
- return (*env)->GetObjectClass(env, obj);
- }
- static jmethodID jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
- return (*env)->GetMethodID(env, clazz, name, sig);
- }
- static jmethodID jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
- return (*env)->GetStaticMethodID(env, clazz, name, sig);
- }
- static jfloat jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
- return (*env)->CallFloatMethod(env, obj, methodID);
- }
- static jint jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
- return (*env)->CallIntMethod(env, obj, methodID);
- }
- static void jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) {
- (*env)->CallStaticVoidMethodA(env, cls, methodID, args);
- }
- static void jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
- (*env)->CallVoidMethodA(env, obj, methodID, args);
- }
- static jboolean jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) {
- return (*env)->CallBooleanMethodA(env, obj, methodID, args);
- }
- static jbyte *jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) {
- return (*env)->GetByteArrayElements(env, arr, NULL);
- }
- static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) {
- (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT);
- }
- static jsize jni_GetArrayLength(JNIEnv *env, jbyteArray arr) {
- return (*env)->GetArrayLength(env, arr);
- }
- static jstring jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) {
- return (*env)->NewString(env, unicodeChars, len);
- }
- static jsize jni_GetStringLength(JNIEnv *env, jstring str) {
- return (*env)->GetStringLength(env, str);
- }
- static const jchar *jni_GetStringChars(JNIEnv *env, jstring str) {
- return (*env)->GetStringChars(env, str, NULL);
- }
- static jthrowable jni_ExceptionOccurred(JNIEnv *env) {
- return (*env)->ExceptionOccurred(env);
- }
- static void jni_ExceptionClear(JNIEnv *env) {
- (*env)->ExceptionClear(env);
- }
- static jobject jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
- return (*env)->CallObjectMethodA(env, obj, method, args);
- }
- static jobject jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
- return (*env)->CallStaticObjectMethodA(env, cls, method, args);
- }
- static jclass jni_FindClass(JNIEnv *env, char *name) {
- return (*env)->FindClass(env, name);
- }
- static jobject jni_NewObjectA(JNIEnv *env, jclass cls, jmethodID cons, jvalue *args) {
- return (*env)->NewObjectA(env, cls, cons, args);
- }
- */
- import "C"
- import (
- "errors"
- "fmt"
- "image"
- "image/color"
- "io"
- "math"
- "os"
- "path/filepath"
- "runtime"
- "runtime/cgo"
- "runtime/debug"
- "strings"
- "sync"
- "time"
- "unicode/utf16"
- "unsafe"
- "gioui.org/internal/f32color"
- "gioui.org/op"
- "gioui.org/f32"
- "gioui.org/io/event"
- "gioui.org/io/input"
- "gioui.org/io/key"
- "gioui.org/io/pointer"
- "gioui.org/io/semantic"
- "gioui.org/io/system"
- "gioui.org/io/transfer"
- "gioui.org/unit"
- )
- type window struct {
- callbacks *callbacks
- loop *eventLoop
- view C.jobject
- handle cgo.Handle
- dpi int
- fontScale float32
- insets pixelInsets
- visible bool
- started bool
- animating bool
- win *C.ANativeWindow
- config Config
- inputHint key.InputHint
- semantic struct {
- hoverID input.SemanticID
- rootID input.SemanticID
- focusID input.SemanticID
- diffs []input.SemanticID
- }
- }
- // gioView hold cached JNI methods for GioView.
- var gioView struct {
- once sync.Once
- getDensity C.jmethodID
- getFontScale C.jmethodID
- showTextInput C.jmethodID
- hideTextInput C.jmethodID
- setInputHint C.jmethodID
- postFrameCallback C.jmethodID
- invalidate C.jmethodID // requests draw, called from UI thread
- setCursor C.jmethodID
- setOrientation C.jmethodID
- setNavigationColor C.jmethodID
- setStatusColor C.jmethodID
- setFullscreen C.jmethodID
- unregister C.jmethodID
- sendA11yEvent C.jmethodID
- sendA11yChange C.jmethodID
- isA11yActive C.jmethodID
- restartInput C.jmethodID
- updateSelection C.jmethodID
- updateCaret C.jmethodID
- }
- type pixelInsets struct {
- top, bottom, left, right int
- }
- // AndroidViewEvent is sent whenever the Window's underlying Android view
- // changes.
- type AndroidViewEvent struct {
- // View is a JNI global reference to the android.view.View
- // instance backing the Window. The reference is valid until
- // the next ViewEvent is received.
- // A zero View means that there is currently no view attached.
- View uintptr
- }
- type jvalue uint64 // The largest JNI type fits in 64 bits.
- var dataDirChan = make(chan string, 1)
- 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
- // appCtx is the global Android App context.
- appCtx C.jobject
- // gioCls is the class of the Gio class.
- gioCls C.jclass
- mwriteClipboard C.jmethodID
- mreadClipboard C.jmethodID
- mwakeupMainThread C.jmethodID
- // android.view.accessibility.AccessibilityNodeInfo class.
- accessibilityNodeInfo struct {
- cls C.jclass
- // addChild(View, int)
- addChild C.jmethodID
- // setBoundsInScreen(Rect)
- setBoundsInScreen C.jmethodID
- // setText(CharSequence)
- setText C.jmethodID
- // setContentDescription(CharSequence)
- setContentDescription C.jmethodID
- // setParent(View, int)
- setParent C.jmethodID
- // addAction(int)
- addAction C.jmethodID
- // setClassName(CharSequence)
- setClassName C.jmethodID
- // setCheckable(boolean)
- setCheckable C.jmethodID
- // setSelected(boolean)
- setSelected C.jmethodID
- // setChecked(boolean)
- setChecked C.jmethodID
- // setEnabled(boolean)
- setEnabled C.jmethodID
- // setAccessibilityFocused(boolean)
- setAccessibilityFocused C.jmethodID
- }
- // android.graphics.Rect class.
- rect struct {
- cls C.jclass
- // (int, int, int, int) constructor.
- cons C.jmethodID
- }
- strings struct {
- // "android.view.View"
- androidViewView C.jstring
- // "android.widget.Button"
- androidWidgetButton C.jstring
- // "android.widget.CheckBox"
- androidWidgetCheckBox C.jstring
- // "android.widget.EditText"
- androidWidgetEditText C.jstring
- // "android.widget.RadioButton"
- androidWidgetRadioButton C.jstring
- // "android.widget.Switch"
- androidWidgetSwitch C.jstring
- }
- }
- var windows = make(map[*callbacks]*window)
- var mainWindow = newWindowRendezvous()
- var mainFuncs = make(chan func(env *C.JNIEnv), 1)
- var (
- dataDirOnce sync.Once
- dataPath string
- )
- var (
- newAndroidVulkanContext func(w *window) (context, error)
- newAndroidGLESContext func(w *window) (context, error)
- )
- // AccessibilityNodeProvider.HOST_VIEW_ID.
- const HOST_VIEW_ID = -1
- const (
- // AccessibilityEvent constants.
- TYPE_VIEW_HOVER_ENTER = 128
- TYPE_VIEW_HOVER_EXIT = 256
- )
- const (
- // AccessibilityNodeInfo constants.
- ACTION_ACCESSIBILITY_FOCUS = 64
- ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128
- ACTION_CLICK = 16
- )
- func (w *window) NewContext() (context, error) {
- funcs := []func(w *window) (context, error){newAndroidGLESContext, newAndroidVulkanContext}
- var firstErr error
- for _, f := range funcs {
- if f == nil {
- continue
- }
- c, err := f(w)
- if err != nil {
- if firstErr == nil {
- firstErr = err
- }
- continue
- }
- return c, nil
- }
- if firstErr != nil {
- return nil, firstErr
- }
- return nil, errors.New("x11: no available GPU backends")
- }
- func dataDir() (string, error) {
- dataDirOnce.Do(func() {
- dataPath = <-dataDirChan
- })
- return dataPath, nil
- }
- func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
- m := C.CString(method)
- defer C.free(unsafe.Pointer(m))
- s := C.CString(sig)
- defer C.free(unsafe.Pointer(s))
- jm := C.jni_GetMethodID(env, class, m, s)
- if err := exception(env); err != nil {
- panic(err)
- }
- return jm
- }
- func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID {
- m := C.CString(method)
- defer C.free(unsafe.Pointer(m))
- s := C.CString(sig)
- defer C.free(unsafe.Pointer(s))
- jm := C.jni_GetStaticMethodID(env, class, m, s)
- if err := exception(env); err != nil {
- panic(err)
- }
- return jm
- }
- //export Java_org_gioui_Gio_runGoMain
- func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) {
- initJVM(env, class, context)
- dirBytes := C.jni_GetByteArrayElements(env, jdataDir)
- if dirBytes == nil {
- panic("runGoMain: GetByteArrayElements failed")
- }
- n := C.jni_GetArrayLength(env, jdataDir)
- dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n)
- // Set XDG_CACHE_HOME to make os.UserCacheDir work.
- if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists {
- cachePath := filepath.Join(dataDir, "cache")
- os.Setenv("XDG_CACHE_HOME", cachePath)
- }
- // Set XDG_CONFIG_HOME to make os.UserConfigDir work.
- if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists {
- cfgPath := filepath.Join(dataDir, "config")
- os.Setenv("XDG_CONFIG_HOME", cfgPath)
- }
- // Set HOME to make os.UserHomeDir work.
- if _, exists := os.LookupEnv("HOME"); !exists {
- os.Setenv("HOME", dataDir)
- }
- dataDirChan <- dataDir
- C.jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
- runMain()
- }
- func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) {
- android.mu.Lock()
- defer android.mu.Unlock()
- if res := C.jni_GetJavaVM(env, &android.jvm); res != 0 {
- panic("gio: GetJavaVM failed")
- }
- android.appCtx = C.jni_NewGlobalRef(env, ctx)
- android.gioCls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(gio)))
- cls := findClass(env, "android/view/accessibility/AccessibilityNodeInfo")
- android.accessibilityNodeInfo.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls)))
- android.accessibilityNodeInfo.addChild = getMethodID(env, cls, "addChild", "(Landroid/view/View;I)V")
- android.accessibilityNodeInfo.setBoundsInScreen = getMethodID(env, cls, "setBoundsInScreen", "(Landroid/graphics/Rect;)V")
- android.accessibilityNodeInfo.setText = getMethodID(env, cls, "setText", "(Ljava/lang/CharSequence;)V")
- android.accessibilityNodeInfo.setContentDescription = getMethodID(env, cls, "setContentDescription", "(Ljava/lang/CharSequence;)V")
- android.accessibilityNodeInfo.setParent = getMethodID(env, cls, "setParent", "(Landroid/view/View;I)V")
- android.accessibilityNodeInfo.addAction = getMethodID(env, cls, "addAction", "(I)V")
- android.accessibilityNodeInfo.setClassName = getMethodID(env, cls, "setClassName", "(Ljava/lang/CharSequence;)V")
- android.accessibilityNodeInfo.setCheckable = getMethodID(env, cls, "setCheckable", "(Z)V")
- android.accessibilityNodeInfo.setSelected = getMethodID(env, cls, "setSelected", "(Z)V")
- android.accessibilityNodeInfo.setChecked = getMethodID(env, cls, "setChecked", "(Z)V")
- android.accessibilityNodeInfo.setEnabled = getMethodID(env, cls, "setEnabled", "(Z)V")
- android.accessibilityNodeInfo.setAccessibilityFocused = getMethodID(env, cls, "setAccessibilityFocused", "(Z)V")
- cls = findClass(env, "android/graphics/Rect")
- android.rect.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls)))
- android.rect.cons = getMethodID(env, cls, "<init>", "(IIII)V")
- android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V")
- android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;")
- android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V")
- intern := func(s string) C.jstring {
- ref := C.jni_NewGlobalRef(env, C.jobject(javaString(env, s)))
- return C.jstring(ref)
- }
- android.strings.androidViewView = intern("android.view.View")
- android.strings.androidWidgetButton = intern("android.widget.Button")
- android.strings.androidWidgetCheckBox = intern("android.widget.CheckBox")
- android.strings.androidWidgetEditText = intern("android.widget.EditText")
- android.strings.androidWidgetRadioButton = intern("android.widget.RadioButton")
- android.strings.androidWidgetSwitch = intern("android.widget.Switch")
- }
- // JavaVM returns the global JNI JavaVM.
- func JavaVM() uintptr {
- jvm := javaVM()
- return uintptr(unsafe.Pointer(jvm))
- }
- func javaVM() *C.JavaVM {
- android.mu.Lock()
- defer android.mu.Unlock()
- return android.jvm
- }
- // AppContext returns the global Application context as a JNI jobject.
- func AppContext() uintptr {
- android.mu.Lock()
- defer android.mu.Unlock()
- return uintptr(android.appCtx)
- }
- //export Java_org_gioui_GioView_onCreateView
- func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong {
- gioView.once.Do(func() {
- m := &gioView
- m.getDensity = getMethodID(env, class, "getDensity", "()I")
- m.getFontScale = getMethodID(env, class, "getFontScale", "()F")
- m.showTextInput = getMethodID(env, class, "showTextInput", "()V")
- m.hideTextInput = getMethodID(env, class, "hideTextInput", "()V")
- m.setInputHint = getMethodID(env, class, "setInputHint", "(I)V")
- m.postFrameCallback = getMethodID(env, class, "postFrameCallback", "()V")
- m.invalidate = getMethodID(env, class, "invalidate", "()V")
- m.setCursor = getMethodID(env, class, "setCursor", "(I)V")
- m.setOrientation = getMethodID(env, class, "setOrientation", "(II)V")
- m.setNavigationColor = getMethodID(env, class, "setNavigationColor", "(II)V")
- m.setStatusColor = getMethodID(env, class, "setStatusColor", "(II)V")
- m.setFullscreen = getMethodID(env, class, "setFullscreen", "(Z)V")
- m.unregister = getMethodID(env, class, "unregister", "()V")
- m.sendA11yEvent = getMethodID(env, class, "sendA11yEvent", "(II)V")
- m.sendA11yChange = getMethodID(env, class, "sendA11yChange", "(I)V")
- m.isA11yActive = getMethodID(env, class, "isA11yActive", "()Z")
- m.restartInput = getMethodID(env, class, "restartInput", "()V")
- m.updateSelection = getMethodID(env, class, "updateSelection", "()V")
- m.updateCaret = getMethodID(env, class, "updateCaret", "(FFFFFFFFFF)V")
- })
- view = C.jni_NewGlobalRef(env, view)
- wopts := <-mainWindow.out
- var cnf Config
- w, ok := windows[wopts.window]
- if !ok {
- w = &window{
- callbacks: wopts.window,
- }
- w.loop = newEventLoop(w.callbacks, w.wakeup)
- w.callbacks.SetDriver(w)
- cnf.apply(unit.Metric{}, wopts.options)
- windows[wopts.window] = w
- } else {
- cnf = w.config
- }
- mainWindow.windows <- struct{}{}
- if w.view != 0 {
- w.detach(env)
- }
- w.view = view
- w.visible = false
- w.handle = cgo.NewHandle(w)
- w.loadConfig(env, class)
- w.setConfig(env, cnf)
- w.SetInputHint(w.inputHint)
- w.processEvent(AndroidViewEvent{View: uintptr(view)})
- return C.jlong(w.handle)
- }
- //export Java_org_gioui_GioView_onDestroyView
- func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := cgo.Handle(handle).Value().(*window)
- w.detach(env)
- }
- //export Java_org_gioui_GioView_onStopView
- func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := cgo.Handle(handle).Value().(*window)
- w.started = false
- w.visible = false
- }
- //export Java_org_gioui_GioView_onStartView
- func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := cgo.Handle(handle).Value().(*window)
- w.started = true
- if w.win != nil {
- w.setVisible(env)
- }
- }
- //export Java_org_gioui_GioView_onSurfaceDestroyed
- func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) {
- w := cgo.Handle(handle).Value().(*window)
- w.win = nil
- w.visible = false
- }
- //export Java_org_gioui_GioView_onSurfaceChanged
- func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) {
- w := cgo.Handle(handle).Value().(*window)
- w.win = C.ANativeWindow_fromSurface(env, surf)
- if w.started {
- w.setVisible(env)
- }
- }
- //export Java_org_gioui_GioView_onLowMemory
- func Java_org_gioui_GioView_onLowMemory(env *C.JNIEnv, class C.jclass) {
- runtime.GC()
- debug.FreeOSMemory()
- }
- //export Java_org_gioui_GioView_onConfigurationChanged
- func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) {
- w := cgo.Handle(view).Value().(*window)
- w.loadConfig(env, class)
- w.draw(env, true)
- }
- //export Java_org_gioui_GioView_onFrameCallback
- func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong) {
- w, exist := cgo.Handle(view).Value().(*window)
- if !exist {
- return
- }
- w.draw(env, false)
- }
- //export Java_org_gioui_GioView_onBack
- func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
- w := cgo.Handle(view).Value().(*window)
- if w.processEvent(key.Event{Name: key.NameBack}) {
- return C.JNI_TRUE
- }
- return C.JNI_FALSE
- }
- //export Java_org_gioui_GioView_onFocusChange
- func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
- w := cgo.Handle(view).Value().(*window)
- w.config.Focused = focus == C.JNI_TRUE
- w.processEvent(ConfigEvent{Config: w.config})
- }
- //export Java_org_gioui_GioView_onWindowInsets
- func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) {
- w := cgo.Handle(view).Value().(*window)
- w.insets = pixelInsets{
- top: int(top),
- bottom: int(bottom),
- left: int(left),
- right: int(right),
- }
- w.draw(env, true)
- }
- //export Java_org_gioui_GioView_initializeAccessibilityNodeInfo
- func Java_org_gioui_GioView_initializeAccessibilityNodeInfo(env *C.JNIEnv, class C.jclass, view C.jlong, virtID, screenX, screenY C.jint, info C.jobject) C.jobject {
- w := cgo.Handle(view).Value().(*window)
- semID := w.semIDFor(virtID)
- sem, found := w.callbacks.LookupSemantic(semID)
- if found {
- off := image.Pt(int(screenX), int(screenY))
- if err := w.initAccessibilityNodeInfo(env, sem, off, info); err != nil {
- panic(err)
- }
- }
- return info
- }
- //export Java_org_gioui_GioView_onTouchExploration
- func Java_org_gioui_GioView_onTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong, x, y C.jfloat) {
- w := cgo.Handle(view).Value().(*window)
- semID, _ := w.callbacks.SemanticAt(f32.Pt(float32(x), float32(y)))
- if w.semantic.hoverID == semID {
- return
- }
- // Android expects ENTER before EXIT.
- if semID != 0 {
- callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_ENTER, jvalue(w.virtualIDFor(semID)))
- }
- if prevID := w.semantic.hoverID; prevID != 0 {
- callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(prevID)))
- }
- w.semantic.hoverID = semID
- }
- //export Java_org_gioui_GioView_onExitTouchExploration
- func Java_org_gioui_GioView_onExitTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong) {
- w := cgo.Handle(view).Value().(*window)
- if w.semantic.hoverID != 0 {
- callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(w.semantic.hoverID)))
- w.semantic.hoverID = 0
- }
- }
- //export Java_org_gioui_GioView_onA11yFocus
- func Java_org_gioui_GioView_onA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) {
- w := cgo.Handle(view).Value().(*window)
- if semID := w.semIDFor(virtID); semID != w.semantic.focusID {
- w.semantic.focusID = semID
- // Android needs invalidate to refresh the TalkBack focus indicator.
- callVoidMethod(env, w.view, gioView.invalidate)
- }
- }
- //export Java_org_gioui_GioView_onClearA11yFocus
- func Java_org_gioui_GioView_onClearA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) {
- w := cgo.Handle(view).Value().(*window)
- if w.semantic.focusID == w.semIDFor(virtID) {
- w.semantic.focusID = 0
- }
- }
- func (w *window) ProcessEvent(e event.Event) {
- w.processEvent(e)
- }
- func (w *window) processEvent(e event.Event) bool {
- if !w.callbacks.ProcessEvent(e) {
- return false
- }
- w.loop.FlushEvents()
- return true
- }
- func (w *window) Event() event.Event {
- return w.loop.Event()
- }
- func (w *window) Invalidate() {
- w.loop.Invalidate()
- }
- func (w *window) Run(f func()) {
- w.loop.Run(f)
- }
- func (w *window) Frame(frame *op.Ops) {
- w.loop.Frame(frame)
- }
- func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem input.SemanticNode, off image.Point, info C.jobject) error {
- for _, ch := range sem.Children {
- err := callVoidMethod(env, info, android.accessibilityNodeInfo.addChild, jvalue(w.view), jvalue(w.virtualIDFor(ch.ID)))
- if err != nil {
- return err
- }
- }
- if sem.ParentID != 0 {
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setParent, jvalue(w.view), jvalue(w.virtualIDFor(sem.ParentID))); err != nil {
- return err
- }
- b := sem.Desc.Bounds.Add(off)
- rect, err := newObject(env, android.rect.cls, android.rect.cons,
- jvalue(b.Min.X),
- jvalue(b.Min.Y),
- jvalue(b.Max.X),
- jvalue(b.Max.Y),
- )
- if err != nil {
- return err
- }
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setBoundsInScreen, jvalue(rect)); err != nil {
- return err
- }
- }
- d := sem.Desc
- if l := d.Label; l != "" {
- jlbl := javaString(env, l)
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setText, jvalue(jlbl)); err != nil {
- return err
- }
- }
- if d.Description != "" {
- jd := javaString(env, d.Description)
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setContentDescription, jvalue(jd)); err != nil {
- return err
- }
- }
- addAction := func(act C.jint) {
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.addAction, jvalue(act)); err != nil {
- panic(err)
- }
- }
- if d.Gestures&input.ClickGesture != 0 {
- addAction(ACTION_CLICK)
- }
- clsName := android.strings.androidViewView
- selectMethod := android.accessibilityNodeInfo.setChecked
- checkable := false
- switch d.Class {
- case semantic.Button:
- clsName = android.strings.androidWidgetButton
- case semantic.CheckBox:
- checkable = true
- clsName = android.strings.androidWidgetCheckBox
- case semantic.Editor:
- clsName = android.strings.androidWidgetEditText
- case semantic.RadioButton:
- selectMethod = android.accessibilityNodeInfo.setSelected
- clsName = android.strings.androidWidgetRadioButton
- case semantic.Switch:
- checkable = true
- clsName = android.strings.androidWidgetSwitch
- }
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setClassName, jvalue(clsName)); err != nil {
- panic(err)
- }
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setCheckable, jvalue(javaBool(checkable))); err != nil {
- panic(err)
- }
- if err := callVoidMethod(env, info, selectMethod, jvalue(javaBool(d.Selected))); err != nil {
- panic(err)
- }
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setEnabled, jvalue(javaBool(!d.Disabled))); err != nil {
- panic(err)
- }
- isFocus := w.semantic.focusID == sem.ID
- if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setAccessibilityFocused, jvalue(javaBool(isFocus))); err != nil {
- panic(err)
- }
- if isFocus {
- addAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS)
- } else {
- addAction(ACTION_ACCESSIBILITY_FOCUS)
- }
- return nil
- }
- func (w *window) virtualIDFor(id input.SemanticID) C.jint {
- if id == w.semantic.rootID {
- return HOST_VIEW_ID
- }
- return C.jint(id)
- }
- func (w *window) semIDFor(virtID C.jint) input.SemanticID {
- if virtID == HOST_VIEW_ID {
- return w.semantic.rootID
- }
- return input.SemanticID(virtID)
- }
- func (w *window) detach(env *C.JNIEnv) {
- callVoidMethod(env, w.view, gioView.unregister)
- w.processEvent(AndroidViewEvent{})
- w.handle.Delete()
- C.jni_DeleteGlobalRef(env, w.view)
- w.view = 0
- }
- func (w *window) setVisible(env *C.JNIEnv) {
- width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
- if width == 0 || height == 0 {
- return
- }
- w.visible = true
- w.draw(env, true)
- }
- func (w *window) setVisual(visID int) error {
- if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 {
- return errors.New("ANativeWindow_setBuffersGeometry failed")
- }
- return nil
- }
- func (w *window) nativeWindow() (*C.ANativeWindow, int, int) {
- width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
- return w.win, int(width), int(height)
- }
- func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) {
- dpi := int(C.jni_CallIntMethod(env, w.view, gioView.getDensity))
- w.fontScale = float32(C.jni_CallFloatMethod(env, w.view, gioView.getFontScale))
- switch dpi {
- case C.ACONFIGURATION_DENSITY_NONE,
- C.ACONFIGURATION_DENSITY_DEFAULT,
- C.ACONFIGURATION_DENSITY_ANY:
- // Assume standard density.
- w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM
- default:
- w.dpi = int(dpi)
- }
- }
- func (w *window) SetAnimating(anim bool) {
- w.animating = anim
- if anim {
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- callVoidMethod(env, w.view, gioView.postFrameCallback)
- })
- }
- }
- func (w *window) draw(env *C.JNIEnv, sync bool) {
- if !w.visible {
- return
- }
- size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win)))
- if size != w.config.Size {
- w.config.Size = size
- w.processEvent(ConfigEvent{Config: w.config})
- }
- if size.X == 0 || size.Y == 0 {
- return
- }
- const inchPrDp = 1.0 / 160
- ppdp := float32(w.dpi) * inchPrDp
- dppp := unit.Dp(1.0 / ppdp)
- insets := Insets{
- Top: unit.Dp(w.insets.top) * dppp,
- Bottom: unit.Dp(w.insets.bottom) * dppp,
- Left: unit.Dp(w.insets.left) * dppp,
- Right: unit.Dp(w.insets.right) * dppp,
- }
- w.processEvent(frameEvent{
- FrameEvent: FrameEvent{
- Now: time.Now(),
- Size: w.config.Size,
- Insets: insets,
- Metric: unit.Metric{
- PxPerDp: ppdp,
- PxPerSp: w.fontScale * ppdp,
- },
- },
- Sync: sync,
- })
- if w.animating {
- callVoidMethod(env, w.view, gioView.postFrameCallback)
- }
- a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive)
- if err != nil {
- panic(err)
- }
- if a11yActive {
- if newR, oldR := w.callbacks.SemanticRoot(), w.semantic.rootID; newR != oldR {
- // Remap focus and hover.
- if oldR == w.semantic.hoverID {
- w.semantic.hoverID = newR
- }
- if oldR == w.semantic.focusID {
- w.semantic.focusID = newR
- }
- w.semantic.rootID = newR
- callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(newR)))
- }
- w.semantic.diffs = w.callbacks.AppendSemanticDiffs(w.semantic.diffs[:0])
- for _, id := range w.semantic.diffs {
- callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(id)))
- }
- }
- }
- func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
- if jvm == nil {
- panic("nil JVM")
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- var env *C.JNIEnv
- if res := C.jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
- if res != C.JNI_EDETACHED {
- panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
- }
- if C.jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
- panic(errors.New("runInJVM: AttachCurrentThread failed"))
- }
- defer C.jni_DetachCurrentThread(jvm)
- }
- f(env)
- }
- func convertKeyCode(code C.jint) (key.Name, bool) {
- var n key.Name
- switch code {
- case C.AKEYCODE_FORWARD_DEL:
- n = key.NameDeleteForward
- case C.AKEYCODE_DEL:
- n = key.NameDeleteBackward
- case C.AKEYCODE_NUMPAD_ENTER:
- n = key.NameEnter
- case C.AKEYCODE_ENTER:
- n = key.NameReturn
- case C.AKEYCODE_CTRL_LEFT, C.AKEYCODE_CTRL_RIGHT:
- n = key.NameCtrl
- case C.AKEYCODE_SHIFT_LEFT, C.AKEYCODE_SHIFT_RIGHT:
- n = key.NameShift
- case C.AKEYCODE_ALT_LEFT, C.AKEYCODE_ALT_RIGHT:
- n = key.NameAlt
- case C.AKEYCODE_META_LEFT, C.AKEYCODE_META_RIGHT:
- n = key.NameSuper
- case C.AKEYCODE_DPAD_UP:
- n = key.NameUpArrow
- case C.AKEYCODE_DPAD_DOWN:
- n = key.NameDownArrow
- case C.AKEYCODE_DPAD_LEFT:
- n = key.NameLeftArrow
- case C.AKEYCODE_DPAD_RIGHT:
- n = key.NameRightArrow
- default:
- return "", false
- }
- return n, true
- }
- //export Java_org_gioui_GioView_onKeyEvent
- func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, pressed C.jboolean, t C.jlong) {
- w := cgo.Handle(handle).Value().(*window)
- if pressed == C.JNI_TRUE && keyCode == C.AKEYCODE_DPAD_CENTER {
- w.callbacks.ClickFocus()
- return
- }
- if n, ok := convertKeyCode(keyCode); ok {
- state := key.Release
- if pressed == C.JNI_TRUE {
- state = key.Press
- }
- w.processEvent(key.Event{Name: n, State: state})
- }
- if pressed == C.JNI_TRUE && r != 0 && r != '\n' { // Checking for "\n" to prevent duplication with key.NameEnter (gio#224).
- w.callbacks.EditorInsert(string(rune(r)))
- }
- }
- //export Java_org_gioui_GioView_onTouchEvent
- func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) {
- w := cgo.Handle(handle).Value().(*window)
- var kind pointer.Kind
- switch action {
- case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
- kind = pointer.Press
- case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
- kind = pointer.Release
- case C.AMOTION_EVENT_ACTION_CANCEL:
- kind = pointer.Cancel
- case C.AMOTION_EVENT_ACTION_MOVE:
- kind = pointer.Move
- case C.AMOTION_EVENT_ACTION_SCROLL:
- kind = pointer.Scroll
- default:
- return
- }
- var src pointer.Source
- var btns pointer.Buttons
- if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 {
- btns |= pointer.ButtonPrimary
- }
- if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 {
- btns |= pointer.ButtonSecondary
- }
- if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 {
- btns |= pointer.ButtonTertiary
- }
- switch tool {
- case C.AMOTION_EVENT_TOOL_TYPE_FINGER:
- src = pointer.Touch
- case C.AMOTION_EVENT_TOOL_TYPE_STYLUS:
- src = pointer.Touch
- case C.AMOTION_EVENT_TOOL_TYPE_MOUSE:
- src = pointer.Mouse
- case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
- // For example, triggered via 'adb shell input tap'.
- // Instead of discarding it, treat it as a touch event.
- src = pointer.Touch
- default:
- return
- }
- w.processEvent(pointer.Event{
- Kind: kind,
- Source: src,
- Buttons: btns,
- PointerID: pointer.ID(pointerID),
- Time: time.Duration(t) * time.Millisecond,
- Position: f32.Point{X: float32(x), Y: float32(y)},
- Scroll: f32.Pt(float32(scrollX), float32(scrollY)),
- })
- }
- //export Java_org_gioui_GioView_imeSelectionStart
- func Java_org_gioui_GioView_imeSelectionStart(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
- w := cgo.Handle(handle).Value().(*window)
- sel := w.callbacks.EditorState().Selection
- start := sel.Start
- if sel.End < sel.Start {
- start = sel.End
- }
- return C.jint(start)
- }
- //export Java_org_gioui_GioView_imeSelectionEnd
- func Java_org_gioui_GioView_imeSelectionEnd(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
- w := cgo.Handle(handle).Value().(*window)
- sel := w.callbacks.EditorState().Selection
- end := sel.End
- if sel.End < sel.Start {
- end = sel.Start
- }
- return C.jint(end)
- }
- //export Java_org_gioui_GioView_imeComposingStart
- func Java_org_gioui_GioView_imeComposingStart(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
- w := cgo.Handle(handle).Value().(*window)
- comp := w.callbacks.EditorState().compose
- start := comp.Start
- if e := comp.End; e < start {
- start = e
- }
- return C.jint(start)
- }
- //export Java_org_gioui_GioView_imeComposingEnd
- func Java_org_gioui_GioView_imeComposingEnd(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
- w := cgo.Handle(handle).Value().(*window)
- comp := w.callbacks.EditorState().compose
- end := comp.End
- if s := comp.Start; s > end {
- end = s
- }
- return C.jint(end)
- }
- //export Java_org_gioui_GioView_imeSnippet
- func Java_org_gioui_GioView_imeSnippet(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jstring {
- w := cgo.Handle(handle).Value().(*window)
- snip := w.callbacks.EditorState().Snippet.Text
- return javaString(env, snip)
- }
- //export Java_org_gioui_GioView_imeSnippetStart
- func Java_org_gioui_GioView_imeSnippetStart(env *C.JNIEnv, class C.jclass, handle C.jlong) C.jint {
- w := cgo.Handle(handle).Value().(*window)
- return C.jint(w.callbacks.EditorState().Snippet.Start)
- }
- //export Java_org_gioui_GioView_imeSetSnippet
- func Java_org_gioui_GioView_imeSetSnippet(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint) {
- w := cgo.Handle(handle).Value().(*window)
- if start < 0 {
- start = 0
- }
- if end < start {
- end = start
- }
- r := key.Range{Start: int(start), End: int(end)}
- w.callbacks.SetEditorSnippet(r)
- }
- //export Java_org_gioui_GioView_imeSetSelection
- func Java_org_gioui_GioView_imeSetSelection(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint) {
- w := cgo.Handle(handle).Value().(*window)
- r := key.Range{Start: int(start), End: int(end)}
- w.callbacks.SetEditorSelection(r)
- }
- //export Java_org_gioui_GioView_imeSetComposingRegion
- func Java_org_gioui_GioView_imeSetComposingRegion(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint) {
- w := cgo.Handle(handle).Value().(*window)
- w.callbacks.SetComposingRegion(key.Range{
- Start: int(start),
- End: int(end),
- })
- }
- //export Java_org_gioui_GioView_imeReplace
- func Java_org_gioui_GioView_imeReplace(env *C.JNIEnv, class C.jclass, handle C.jlong, start, end C.jint, jtext C.jstring) {
- w := cgo.Handle(handle).Value().(*window)
- r := key.Range{Start: int(start), End: int(end)}
- text := goString(env, jtext)
- w.callbacks.EditorReplace(r, text)
- }
- //export Java_org_gioui_GioView_imeToRunes
- func Java_org_gioui_GioView_imeToRunes(env *C.JNIEnv, class C.jclass, handle C.jlong, chars C.jint) C.jint {
- w := cgo.Handle(handle).Value().(*window)
- state := w.callbacks.EditorState()
- return C.jint(state.RunesIndex(int(chars)))
- }
- //export Java_org_gioui_GioView_imeToUTF16
- func Java_org_gioui_GioView_imeToUTF16(env *C.JNIEnv, class C.jclass, handle C.jlong, runes C.jint) C.jint {
- w := cgo.Handle(handle).Value().(*window)
- state := w.callbacks.EditorState()
- return C.jint(state.UTF16Index(int(runes)))
- }
- func (w *window) EditorStateChanged(old, new editorState) {
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- if old.Snippet != new.Snippet {
- callVoidMethod(env, w.view, gioView.restartInput)
- return
- }
- if old.Selection.Range != new.Selection.Range {
- w.callbacks.SetComposingRegion(key.Range{Start: -1, End: -1})
- callVoidMethod(env, w.view, gioView.updateSelection)
- }
- if old.Selection.Transform != new.Selection.Transform || old.Selection.Caret != new.Selection.Caret {
- sel := new.Selection
- m00, m01, m02, m10, m11, m12 := sel.Transform.Elems()
- f := func(v float32) jvalue {
- return jvalue(math.Float32bits(v))
- }
- c := sel.Caret
- callVoidMethod(env, w.view, gioView.updateCaret, f(m00), f(m01), f(m02), f(m10), f(m11), f(m12), f(c.Pos.X), f(c.Pos.Y-c.Ascent), f(c.Pos.Y), f(c.Pos.Y+c.Descent))
- }
- })
- }
- func (w *window) ShowTextInput(show bool) {
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- if show {
- callVoidMethod(env, w.view, gioView.showTextInput)
- } else {
- callVoidMethod(env, w.view, gioView.hideTextInput)
- }
- })
- }
- func (w *window) SetInputHint(mode key.InputHint) {
- w.inputHint = mode
- // Constants defined at https://developer.android.com/reference/android/text/InputType.
- const (
- TYPE_NULL = 0
- TYPE_CLASS_TEXT = 1
- TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 32
- TYPE_TEXT_VARIATION_URI = 16
- TYPE_TEXT_VARIATION_PASSWORD = 128
- TYPE_TEXT_FLAG_CAP_SENTENCES = 16384
- TYPE_TEXT_FLAG_AUTO_CORRECT = 32768
- TYPE_CLASS_NUMBER = 2
- TYPE_NUMBER_FLAG_DECIMAL = 8192
- TYPE_NUMBER_FLAG_SIGNED = 4096
- TYPE_CLASS_PHONE = 3
- )
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- var m jvalue
- switch mode {
- case key.HintText:
- m = TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_AUTO_CORRECT | TYPE_TEXT_FLAG_CAP_SENTENCES
- case key.HintNumeric:
- m = TYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_DECIMAL | TYPE_NUMBER_FLAG_SIGNED
- case key.HintEmail:
- m = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS
- case key.HintURL:
- m = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_URI
- case key.HintTelephone:
- m = TYPE_CLASS_PHONE
- case key.HintPassword:
- m = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD
- default:
- m = TYPE_CLASS_TEXT
- }
- callVoidMethod(env, w.view, gioView.setInputHint, m)
- })
- }
- func javaBool(b bool) C.jboolean {
- if b {
- return C.JNI_TRUE
- } else {
- return C.JNI_FALSE
- }
- }
- func javaString(env *C.JNIEnv, str string) C.jstring {
- utf16Chars := utf16.Encode([]rune(str))
- var ptr *C.jchar
- if len(utf16Chars) > 0 {
- ptr = (*C.jchar)(unsafe.Pointer(&utf16Chars[0]))
- }
- return C.jni_NewString(env, ptr, C.int(len(utf16Chars)))
- }
- func varArgs(args []jvalue) *C.jvalue {
- if len(args) == 0 {
- return nil
- }
- return (*C.jvalue)(unsafe.Pointer(&args[0]))
- }
- func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error {
- C.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.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.jni_CallVoidMethodA(env, obj, method, varArgs(args))
- return exception(env)
- }
- func callBooleanMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (bool, error) {
- res := C.jni_CallBooleanMethodA(env, obj, method, varArgs(args))
- return res == C.JNI_TRUE, exception(env)
- }
- func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) {
- res := C.jni_CallObjectMethodA(env, obj, method, varArgs(args))
- return res, exception(env)
- }
- func newObject(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) {
- res := C.jni_NewObjectA(env, cls, 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.jni_ExceptionOccurred(env)
- if thr == 0 {
- return nil
- }
- C.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.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.jni_GetStringLength(env, C.jstring(str))
- chars := C.jni_GetStringChars(env, C.jstring(str))
- utf16Chars := unsafe.Slice((*uint16)(unsafe.Pointer(chars)), strlen)
- utf8 := utf16.Decode(utf16Chars)
- return string(utf8)
- }
- func findClass(env *C.JNIEnv, name string) C.jclass {
- cn := C.CString(name)
- defer C.free(unsafe.Pointer(cn))
- return C.jni_FindClass(env, cn)
- }
- func osMain() {
- }
- func newWindow(window *callbacks, options []Option) {
- mainWindow.in <- windowAndConfig{window, options}
- <-mainWindow.windows
- }
- func (w *window) WriteClipboard(mime string, s []byte) {
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- jstr := javaString(env, string(s))
- callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard,
- jvalue(android.appCtx), jvalue(jstr))
- })
- }
- func (w *window) ReadClipboard() {
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard,
- jvalue(android.appCtx))
- if err != nil {
- return
- }
- content := goString(env, C.jstring(c))
- w.processEvent(transfer.DataEvent{
- Type: "application/text",
- Open: func() io.ReadCloser {
- return io.NopCloser(strings.NewReader(content))
- },
- })
- })
- }
- func (w *window) Configure(options []Option) {
- cnf := w.config
- cnf.apply(unit.Metric{}, options)
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- w.setConfig(env, cnf)
- })
- }
- func (w *window) setConfig(env *C.JNIEnv, cnf Config) {
- prev := w.config
- // Decorations are never disabled.
- cnf.Decorated = true
- if prev.Orientation != cnf.Orientation {
- w.config.Orientation = cnf.Orientation
- setOrientation(env, w.view, cnf.Orientation)
- }
- if prev.NavigationColor != cnf.NavigationColor {
- w.config.NavigationColor = cnf.NavigationColor
- setNavigationColor(env, w.view, cnf.NavigationColor)
- }
- if prev.StatusColor != cnf.StatusColor {
- w.config.StatusColor = cnf.StatusColor
- setStatusColor(env, w.view, cnf.StatusColor)
- }
- if prev.Mode != cnf.Mode {
- switch cnf.Mode {
- case Fullscreen:
- callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_TRUE)
- w.config.Mode = Fullscreen
- case Windowed:
- callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_FALSE)
- w.config.Mode = Windowed
- }
- }
- if cnf.Decorated != prev.Decorated {
- w.config.Decorated = cnf.Decorated
- }
- w.processEvent(ConfigEvent{Config: w.config})
- }
- func (w *window) Perform(system.Action) {}
- func (w *window) SetCursor(cursor pointer.Cursor) {
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- setCursor(env, w.view, cursor)
- })
- }
- func (w *window) wakeup() {
- runOnMain(func(env *C.JNIEnv) {
- w.loop.Wakeup()
- w.loop.FlushEvents()
- })
- }
- var androidCursor = [...]uint16{
- pointer.CursorDefault: 1000, // TYPE_ARROW
- pointer.CursorNone: 0,
- pointer.CursorText: 1008, // TYPE_TEXT
- pointer.CursorVerticalText: 1009, // TYPE_VERTICAL_TEXT
- pointer.CursorPointer: 1002, // TYPE_HAND
- pointer.CursorCrosshair: 1007, // TYPE_CROSSHAIR
- pointer.CursorAllScroll: 1013, // TYPE_ALL_SCROLL
- pointer.CursorColResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
- pointer.CursorRowResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
- pointer.CursorGrab: 1020, // TYPE_GRAB
- pointer.CursorGrabbing: 1021, // TYPE_GRABBING
- pointer.CursorNotAllowed: 1012, // TYPE_NO_DROP
- pointer.CursorWait: 1004, // TYPE_WAIT
- pointer.CursorProgress: 1000, // TYPE_ARROW
- pointer.CursorNorthWestResize: 1017, // TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
- pointer.CursorNorthEastResize: 1016, // TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
- pointer.CursorSouthWestResize: 1016, // TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
- pointer.CursorSouthEastResize: 1017, // TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
- pointer.CursorNorthSouthResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
- pointer.CursorEastWestResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
- pointer.CursorWestResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
- pointer.CursorEastResize: 1014, // TYPE_HORIZONTAL_DOUBLE_ARROW
- pointer.CursorNorthResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
- pointer.CursorSouthResize: 1015, // TYPE_VERTICAL_DOUBLE_ARROW
- pointer.CursorNorthEastSouthWestResize: 1016, // TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
- pointer.CursorNorthWestSouthEastResize: 1017, // TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
- }
- func setCursor(env *C.JNIEnv, view C.jobject, cursor pointer.Cursor) {
- curID := androidCursor[cursor]
- callVoidMethod(env, view, gioView.setCursor, jvalue(curID))
- }
- func setOrientation(env *C.JNIEnv, view C.jobject, mode Orientation) {
- var (
- id int
- idFallback int // Used only for SDK 17 or older.
- )
- // Constants defined at https://developer.android.com/reference/android/content/pm/ActivityInfo.
- switch mode {
- case AnyOrientation:
- id, idFallback = 2, 2 // SCREEN_ORIENTATION_USER
- case LandscapeOrientation:
- id, idFallback = 11, 0 // SCREEN_ORIENTATION_USER_LANDSCAPE (or SCREEN_ORIENTATION_LANDSCAPE)
- case PortraitOrientation:
- id, idFallback = 12, 1 // SCREEN_ORIENTATION_USER_PORTRAIT (or SCREEN_ORIENTATION_PORTRAIT)
- }
- callVoidMethod(env, view, gioView.setOrientation, jvalue(id), jvalue(idFallback))
- }
- func setStatusColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) {
- callVoidMethod(env, view, gioView.setStatusColor,
- jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)),
- jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)),
- )
- }
- func setNavigationColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) {
- callVoidMethod(env, view, gioView.setNavigationColor,
- jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)),
- jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)),
- )
- }
- // runOnMain runs a function on the Java main thread.
- func runOnMain(f func(env *C.JNIEnv)) {
- go func() {
- mainFuncs <- f
- runInJVM(javaVM(), func(env *C.JNIEnv) {
- callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread)
- })
- }()
- }
- //export Java_org_gioui_Gio_scheduleMainFuncs
- func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
- for {
- select {
- case f := <-mainFuncs:
- f(env)
- default:
- return
- }
- }
- }
- func (AndroidViewEvent) implementsViewEvent() {}
- func (AndroidViewEvent) ImplementsEvent() {}
- func (a AndroidViewEvent) Valid() bool {
- return a != (AndroidViewEvent{})
- }
|