From f7f94c93fe0d1d220a2b4827e59b09caf39aa24d Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Sun, 10 Jan 2021 14:48:01 +0100 Subject: [PATCH] app/internal/window: [android] re-apply view state lost on rotate etc. Android can re-create our Activity and GioView at any time, losing view and activity state such as the current cursor. Further, setting state while there is no GioView attached is lost. Track the current and unapplied state, and apply it when a GioView is available. Signed-off-by: Elias Naur --- app/internal/window/os_android.go | 57 +++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/app/internal/window/os_android.go b/app/internal/window/os_android.go index 67f168f9..6fa166c1 100644 --- a/app/internal/window/os_android.go +++ b/app/internal/window/os_android.go @@ -71,11 +71,20 @@ type window struct { stage system.Stage started bool + state, newState windowState + + // mu protects the fields following it. mu sync.Mutex win *C.ANativeWindow animating bool } +// windowState tracks the View or Activity specific state lost when Android +// re-creates our Activity. +type windowState struct { + cursor *pointer.CursorName +} + // gioView hold cached JNI methods for GioView. var gioView struct { once sync.Once @@ -118,8 +127,12 @@ var android struct { mwakeupMainThread C.jmethodID } +// view maps from GioView JNI refenreces to windows. var views = make(map[C.jlong]*window) +// windows maps from Callbacks to windows +var windows = make(map[Callbacks]*window) + var mainWindow = newWindowRendezvous() var mainFuncs = make(chan func(env *C.JNIEnv), 1) @@ -209,15 +222,20 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j m.setCursor = getMethodID(env, class, "setCursor", "(I)V") }) view = C.gio_jni_NewGlobalRef(env, view) - w := &window{ - view: view, - } wopts := <-mainWindow.out - w.callbacks = wopts.window + w, ok := windows[wopts.window] + if !ok { + w = &window{ + callbacks: wopts.window, + } + windows[wopts.window] = w + } w.callbacks.SetDriver(w) + w.view = view handle := C.jlong(view) views[handle] = w w.loadConfig(env, class) + applyStateDiff(env, view, windowState{}, w.state) w.setStage(system.StagePaused) w.callbacks.Event(ViewEvent{View: uintptr(view)}) return handle @@ -659,6 +677,33 @@ func (w *window) ReadClipboard() { } func (w *window) SetCursor(name pointer.CursorName) { + w.setState(func(state *windowState) { + state.cursor = &name + }) +} + +// setState adjust the window state on the main thread. +func (w *window) setState(f func(state *windowState)) { + runOnMain(func(env *C.JNIEnv) { + f(&w.newState) + if w.view == 0 { + // No View attached. The state will be applied at next onCreateView. + return + } + old := w.state + state := w.newState + applyStateDiff(env, w.view, old, state) + w.state = state + }) +} + +func applyStateDiff(env *C.JNIEnv, view C.jobject, old, state windowState) { + if state.cursor != nil && old.cursor != state.cursor { + setCursor(env, view, *state.cursor) + } +} + +func setCursor(env *C.JNIEnv, view C.jobject, name pointer.CursorName) { var curID int switch name { default: @@ -678,9 +723,7 @@ func (w *window) SetCursor(name pointer.CursorName) { case pointer.CursorNone: curID = 0 // TYPE_NULL } - runOnMain(func(env *C.JNIEnv) { - callVoidMethod(env, w.view, gioView.setCursor, jvalue(curID)) - }) + callVoidMethod(env, view, gioView.setCursor, jvalue(curID)) } // Close the window. Not implemented for Android.