mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 10:25:37 +00:00
app/internal/window: [Android] replace RegisterFragment with Do
Do is a function for accessing the underlying Android View in a safe context, the main thread. Do is - more general than RegisterFragment and may be expanded to other platforms - simpler to implement (from the Gio side) and as a bonus, the Do implementation avoids a race condition where a call to RegisterFragment during an Activity re-create would be ignored. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+28
-14
@@ -17,18 +17,32 @@ func AppContext() uintptr {
|
|||||||
return window.AppContext()
|
return window.AppContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
// androidDriver is an interface that allows the Window's run method
|
// Do invokes the function with a JNI jobject handle to the underlying
|
||||||
// to call the RegisterFragment method of the Android window driver.
|
// Android View. The function is invoked on the main thread, and the
|
||||||
type androidDriver interface {
|
// handle is invalidated after the function returns.
|
||||||
RegisterFragment(string)
|
//
|
||||||
}
|
// Note: Do may deadlock if called from the same goroutine that receives from
|
||||||
|
// Events.
|
||||||
// RegisterFragment constructs a Java instance of the specified class
|
func (w *Window) Do(f func(view uintptr)) {
|
||||||
// and registers it as a Fragment in the Context in which the View was
|
type androidDriver interface {
|
||||||
// created.
|
Do(f func(view uintptr)) bool
|
||||||
func (w *Window) RegisterFragment(del string) {
|
}
|
||||||
w.driverDo(func() {
|
success := make(chan bool)
|
||||||
d := w.driver.(androidDriver)
|
for {
|
||||||
d.RegisterFragment(del)
|
driver := make(chan androidDriver, 1)
|
||||||
})
|
// two-stage process: first wait for a valid driver...
|
||||||
|
w.driverDo(func() {
|
||||||
|
driver <- w.driver.(androidDriver)
|
||||||
|
})
|
||||||
|
// .. then run the function on the main thread using the
|
||||||
|
// driver. The driver Do method returns false if the
|
||||||
|
// view was invalidated while switching to the main thread.
|
||||||
|
window.RunOnMain(func() {
|
||||||
|
d := <-driver
|
||||||
|
success <- d.Do(f)
|
||||||
|
})
|
||||||
|
if <-success {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,31 +191,6 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
|
|||||||
return onBack(nhandle);
|
return onBack(nhandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerFragment(String del) {
|
|
||||||
final Class cls;
|
|
||||||
try {
|
|
||||||
cls = getContext().getClassLoader().loadClass(del);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
throw new RuntimeException("RegisterFragment: fragment class not found: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
final Fragment frag;
|
|
||||||
try {
|
|
||||||
frag = (Fragment)cls.newInstance();
|
|
||||||
} catch (IllegalAccessException | InstantiationException | ExceptionInInitializerError | SecurityException | ClassCastException e) {
|
|
||||||
throw new RuntimeException("RegisterFragment: error instantiating fragment: " + e.getMessage());
|
|
||||||
}
|
|
||||||
final FragmentManager fm;
|
|
||||||
try {
|
|
||||||
fm = ((Activity)getContext()).getFragmentManager();
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new RuntimeException("RegisterFragment: cannot get fragment manager from View Context: " + e.getMessage());
|
|
||||||
}
|
|
||||||
FragmentTransaction ft = fm.beginTransaction();
|
|
||||||
ft.add(frag, del);
|
|
||||||
ft.commitNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
static private native long onCreateView(GioView view);
|
static private native long onCreateView(GioView view);
|
||||||
static private native void onDestroyView(long handle);
|
static private native void onDestroyView(long handle);
|
||||||
static private native void onStartView(long handle);
|
static private native void onStartView(long handle);
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ type window struct {
|
|||||||
mshowTextInput C.jmethodID
|
mshowTextInput C.jmethodID
|
||||||
mhideTextInput C.jmethodID
|
mhideTextInput C.jmethodID
|
||||||
mpostFrameCallback C.jmethodID
|
mpostFrameCallback C.jmethodID
|
||||||
mRegisterFragment C.jmethodID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type jvalue uint64 // The largest JNI type fits in 64 bits.
|
type jvalue uint64 // The largest JNI type fits in 64 bits.
|
||||||
@@ -193,7 +192,6 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j
|
|||||||
mshowTextInput: getMethodID(env, class, "showTextInput", "()V"),
|
mshowTextInput: getMethodID(env, class, "showTextInput", "()V"),
|
||||||
mhideTextInput: getMethodID(env, class, "hideTextInput", "()V"),
|
mhideTextInput: getMethodID(env, class, "hideTextInput", "()V"),
|
||||||
mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"),
|
mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"),
|
||||||
mRegisterFragment: getMethodID(env, class, "registerFragment", "(Ljava/lang/String;)V"),
|
|
||||||
}
|
}
|
||||||
wopts := <-mainWindow.out
|
wopts := <-mainWindow.out
|
||||||
w.callbacks = wopts.window
|
w.callbacks = wopts.window
|
||||||
@@ -416,7 +414,6 @@ func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
|
|||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
var env *C.JNIEnv
|
var env *C.JNIEnv
|
||||||
var detach bool
|
|
||||||
if res := C.gio_jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
|
if res := C.gio_jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
|
||||||
if res != C.JNI_EDETACHED {
|
if res != C.JNI_EDETACHED {
|
||||||
panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
|
panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
|
||||||
@@ -424,14 +421,9 @@ func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
|
|||||||
if C.gio_jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
|
if C.gio_jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK {
|
||||||
panic(errors.New("runInJVM: AttachCurrentThread failed"))
|
panic(errors.New("runInJVM: AttachCurrentThread failed"))
|
||||||
}
|
}
|
||||||
detach = true
|
defer C.gio_jni_DetachCurrentThread(jvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
if detach {
|
|
||||||
defer func() {
|
|
||||||
C.gio_jni_DetachCurrentThread(jvm)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
f(env)
|
f(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,11 +529,21 @@ func javaString(env *C.JNIEnv, str string) C.jstring {
|
|||||||
return C.gio_jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars)))
|
return C.gio_jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) RegisterFragment(del string) {
|
// Do invokes the function with a global JNI handle to the view. If
|
||||||
w.runOnMain(func(env *C.JNIEnv) {
|
// the view is destroyed, Do returns false and does not invoke the
|
||||||
jstr := javaString(env, del)
|
// function.
|
||||||
callVoidMethod(env, w.view, w.mRegisterFragment, jvalue(jstr))
|
//
|
||||||
|
// NOTE: Do must be invoked on the Android main thread.
|
||||||
|
func (w *window) Do(f func(view uintptr)) bool {
|
||||||
|
if w.view == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
runInJVM(javaVM(), func(env *C.JNIEnv) {
|
||||||
|
view := C.gio_jni_NewGlobalRef(env, w.view)
|
||||||
|
defer C.gio_jni_DeleteGlobalRef(env, view)
|
||||||
|
f(uintptr(view))
|
||||||
})
|
})
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func varArgs(args []jvalue) *C.jvalue {
|
func varArgs(args []jvalue) *C.jvalue {
|
||||||
@@ -645,6 +647,14 @@ func (w *window) ReadClipboard() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunOnMain is the exported version of runOnMain without a JNI
|
||||||
|
// environement.
|
||||||
|
func RunOnMain(f func()) {
|
||||||
|
runOnMain(func(_ *C.JNIEnv) {
|
||||||
|
f()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// runOnMain runs a function on the Java main thread.
|
// runOnMain runs a function on the Java main thread.
|
||||||
func runOnMain(f func(env *C.JNIEnv)) {
|
func runOnMain(f func(env *C.JNIEnv)) {
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -37,10 +37,14 @@ program's source code:
|
|||||||
Android -- Dangerous Permissions
|
Android -- Dangerous Permissions
|
||||||
|
|
||||||
Certain permissions on Android are marked with a protection level of
|
Certain permissions on Android are marked with a protection level of
|
||||||
"dangerous". This means that, in addition to including the relevant Gio
|
"dangerous". This means that, in addition to including the relevant
|
||||||
permission packages, your app will need to prompt the user specifically
|
Gio permission packages, your app will need to prompt the user
|
||||||
to request access. This can be done with a java Fragment, installed using
|
specifically to request access. To access the Android Activity
|
||||||
(*app.Window).RegisterFragment(). For more information on dangerous
|
required for prompting, use the Do method on Window. Do exposes
|
||||||
permissions, see: https://developer.android.com/guide/topics/permissions/overview#dangerous_permissions
|
the underlying Android View, on which the getContext method returns
|
||||||
|
the Activity.
|
||||||
|
|
||||||
|
For more information on dangerous permissions, see:
|
||||||
|
https://developer.android.com/guide/topics/permissions/overview#dangerous_permissions
|
||||||
*/
|
*/
|
||||||
package permission
|
package permission
|
||||||
|
|||||||
Reference in New Issue
Block a user