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 <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-01-10 14:48:01 +01:00
parent acef4e6e2d
commit f7f94c93fe
+50 -7
View File
@@ -71,11 +71,20 @@ type window struct {
stage system.Stage stage system.Stage
started bool started bool
state, newState windowState
// mu protects the fields following it.
mu sync.Mutex mu sync.Mutex
win *C.ANativeWindow win *C.ANativeWindow
animating bool 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. // gioView hold cached JNI methods for GioView.
var gioView struct { var gioView struct {
once sync.Once once sync.Once
@@ -118,8 +127,12 @@ var android struct {
mwakeupMainThread C.jmethodID mwakeupMainThread C.jmethodID
} }
// view maps from GioView JNI refenreces to windows.
var views = make(map[C.jlong]*window) var views = make(map[C.jlong]*window)
// windows maps from Callbacks to windows
var windows = make(map[Callbacks]*window)
var mainWindow = newWindowRendezvous() var mainWindow = newWindowRendezvous()
var mainFuncs = make(chan func(env *C.JNIEnv), 1) 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") m.setCursor = getMethodID(env, class, "setCursor", "(I)V")
}) })
view = C.gio_jni_NewGlobalRef(env, view) view = C.gio_jni_NewGlobalRef(env, view)
w := &window{
view: view,
}
wopts := <-mainWindow.out 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.callbacks.SetDriver(w)
w.view = view
handle := C.jlong(view) handle := C.jlong(view)
views[handle] = w views[handle] = w
w.loadConfig(env, class) w.loadConfig(env, class)
applyStateDiff(env, view, windowState{}, w.state)
w.setStage(system.StagePaused) w.setStage(system.StagePaused)
w.callbacks.Event(ViewEvent{View: uintptr(view)}) w.callbacks.Event(ViewEvent{View: uintptr(view)})
return handle return handle
@@ -659,6 +677,33 @@ func (w *window) ReadClipboard() {
} }
func (w *window) SetCursor(name pointer.CursorName) { 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 var curID int
switch name { switch name {
default: default:
@@ -678,9 +723,7 @@ func (w *window) SetCursor(name pointer.CursorName) {
case pointer.CursorNone: case pointer.CursorNone:
curID = 0 // TYPE_NULL curID = 0 // TYPE_NULL
} }
runOnMain(func(env *C.JNIEnv) { callVoidMethod(env, view, gioView.setCursor, jvalue(curID))
callVoidMethod(env, w.view, gioView.setCursor, jvalue(curID))
})
} }
// Close the window. Not implemented for Android. // Close the window. Not implemented for Android.