forked from joejulian/gio
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()
|
||||
}
|
||||
|
||||
// androidDriver is an interface that allows the Window's run method
|
||||
// to call the RegisterFragment method of the Android window driver.
|
||||
type androidDriver interface {
|
||||
RegisterFragment(string)
|
||||
}
|
||||
|
||||
// RegisterFragment constructs a Java instance of the specified class
|
||||
// and registers it as a Fragment in the Context in which the View was
|
||||
// created.
|
||||
func (w *Window) RegisterFragment(del string) {
|
||||
w.driverDo(func() {
|
||||
d := w.driver.(androidDriver)
|
||||
d.RegisterFragment(del)
|
||||
})
|
||||
// Do invokes the function with a JNI jobject handle to the underlying
|
||||
// Android View. The function is invoked on the main thread, and the
|
||||
// handle is invalidated after the function returns.
|
||||
//
|
||||
// Note: Do may deadlock if called from the same goroutine that receives from
|
||||
// Events.
|
||||
func (w *Window) Do(f func(view uintptr)) {
|
||||
type androidDriver interface {
|
||||
Do(f func(view uintptr)) bool
|
||||
}
|
||||
success := make(chan bool)
|
||||
for {
|
||||
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);
|
||||
}
|
||||
|
||||
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 void onDestroyView(long handle);
|
||||
static private native void onStartView(long handle);
|
||||
|
||||
@@ -80,7 +80,6 @@ type window struct {
|
||||
mshowTextInput C.jmethodID
|
||||
mhideTextInput C.jmethodID
|
||||
mpostFrameCallback C.jmethodID
|
||||
mRegisterFragment C.jmethodID
|
||||
}
|
||||
|
||||
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"),
|
||||
mhideTextInput: getMethodID(env, class, "hideTextInput", "()V"),
|
||||
mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"),
|
||||
mRegisterFragment: getMethodID(env, class, "registerFragment", "(Ljava/lang/String;)V"),
|
||||
}
|
||||
wopts := <-mainWindow.out
|
||||
w.callbacks = wopts.window
|
||||
@@ -416,7 +414,6 @@ func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
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.JNI_EDETACHED {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
|
||||
func (w *window) RegisterFragment(del string) {
|
||||
w.runOnMain(func(env *C.JNIEnv) {
|
||||
jstr := javaString(env, del)
|
||||
callVoidMethod(env, w.view, w.mRegisterFragment, jvalue(jstr))
|
||||
// Do invokes the function with a global JNI handle to the view. If
|
||||
// the view is destroyed, Do returns false and does not invoke the
|
||||
// function.
|
||||
//
|
||||
// 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 {
|
||||
@@ -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.
|
||||
func runOnMain(f func(env *C.JNIEnv)) {
|
||||
go func() {
|
||||
|
||||
@@ -37,10 +37,14 @@ program's source code:
|
||||
Android -- Dangerous Permissions
|
||||
|
||||
Certain permissions on Android are marked with a protection level of
|
||||
"dangerous". This means that, in addition to including the relevant Gio
|
||||
permission packages, your app will need to prompt the user specifically
|
||||
to request access. This can be done with a java Fragment, installed using
|
||||
(*app.Window).RegisterFragment(). For more information on dangerous
|
||||
permissions, see: https://developer.android.com/guide/topics/permissions/overview#dangerous_permissions
|
||||
"dangerous". This means that, in addition to including the relevant
|
||||
Gio permission packages, your app will need to prompt the user
|
||||
specifically to request access. To access the Android Activity
|
||||
required for prompting, use the Do method on Window. Do exposes
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user