2
0
mirror of synced 2025-02-24 07:18:15 +00:00
mobile/app/android.c
David Crawshaw 8f20c3e9d3 app, cmd/gomobile: subclass NativeActivity
Subclassing NativeActivity makes two things possible. Firstly, we can
implement an InputConnection to offer good support for IMEs,
necessary for good keyboard support. Secondly, we can use it to
overlay WebViews onto the NativeActivity.

But to sublcass NativeActivity, we need to compile Java. To keep the
toolchain go gettable, this is done with go generate.

While here, check the exception after FindClass. Apparently it can
throw an exception.

Updates golang/go#9361.
Updates golang/go#10247.

Change-Id: I672545997f0c9a7580f06988a273c03404772247
Reviewed-on: https://go-review.googlesource.com/11980
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2015-07-13 19:05:26 +00:00

105 lines
3.6 KiB
C

// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build android
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "_cgo_export.h"
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__)
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__)
static jclass find_class(JNIEnv *env, const char *class_name) {
jclass clazz = (*env)->FindClass(env, class_name);
if (clazz == NULL) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find %s", class_name);
return NULL;
}
return clazz;
}
static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
if (m == 0) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return m;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
// Load classes here, which uses the correct ClassLoader.
current_vm = vm;
current_ctx = NULL;
current_ctx_clazz = find_class(env, "org/golang/app/GoNativeActivity");
current_ctx_clazz = (jclass)(*env)->NewGlobalRef(env, current_ctx_clazz);
return JNI_VERSION_1_6;
}
// Entry point from our subclassed NativeActivity.
//
// By here, the Go runtime has been initialized (as we are running in
// -buildmode=c-shared) but main.main hasn't been called yet.
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
JNIEnv* env = activity->env;
// Note that activity->clazz is mis-named.
current_vm = activity->vm;
current_ctx = (*env)->NewGlobalRef(env, activity->clazz);
// Set TMPDIR.
jmethodID gettmpdir = find_method(env, current_ctx_clazz, "getTmpdir", "()Ljava/lang/String;");
jstring jpath = (jstring)(*env)->CallObjectMethod(env, current_ctx, gettmpdir, NULL);
const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
if (setenv("TMPDIR", tmpdir, 1) != 0) {
LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
}
(*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
// Call the Go main.main.
uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
if (!mainPC) {
LOG_FATAL("missing main.main");
}
callMain(mainPC);
// These functions match the methods on Activity, described at
// http://developer.android.com/reference/android/app/Activity.html
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowResized = onNativeWindowResized;
activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
// TODO(crawshaw): Type mismatch for onContentRectChanged.
//activity->callbacks->onContentRectChanged = onContentRectChanged;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
onCreate(activity);
}