diff --git a/ble_android.go b/ble_android.go index 463edc2..349bbd6 100644 --- a/ble_android.go +++ b/ble_android.go @@ -1,14 +1,22 @@ package ble import ( + "log" "gioui.org/app" ) +/* +#include +*/ +import "C" + // Types required for ble.go type bleState string type bleHandle struct { + adapter C.jobject + class C.jclass } type Peripheral struct { @@ -22,12 +30,16 @@ type Characteristic string // Internal global variables +var jvmContext uintptr + // Functions required by API //Init needs to be called before the BLE library can be used. On Android we //need to set up the JVM by calling setJVM in jni_android.go. func Init() { + log.Print("ble.Init()") h := app.PlatformHandle() + jvmContext = h.Context setJVM(h.JVM, h.Context) } @@ -98,6 +110,31 @@ func (p Peripheral) SetNotifyValue(c Characteristic) { //Bluetooth API. func NewBLE() *BLE { ps := Peripherals{items: make([]PeripheralListItem, 0)} - return &BLE{events: make(chan interface{}), peripherals: ps} + h := bleHandle{} + RunInJVM(func(env *JNIEnv) { + h.adapter = GetBluetoothAdapter(env, jvmContext) + h.class = JniGetObjectClass(env, h.adapter) + }) + + ret := &BLE{ + events: make(chan interface{}), + peripherals: ps, + handle: h, + } + + //check if Bluetooth is enabled and if so, send an UpdateStateEvent + go func() { + RunInJVM(func(env *JNIEnv) { + mid := JniGetMethodID(env, h.class, "isEnabled", "()Z") + en := JniCallBooleanMethod(env, h.adapter, mid) + if en { + log.Print("It's enabled!") + ret.events <-UpdateStateEvent{"powered on"} + } else { + log.Print("It's not enabled") + } + }) + }() + return ret } diff --git a/jni_android.go b/jni_android.go index 0b2ba87..108fa94 100644 --- a/jni_android.go +++ b/jni_android.go @@ -1,7 +1,7 @@ package ble /* -#cgo LDFLAGS: -landroid +#cgo LDFLAGS: -landroid -llog #include #include @@ -10,6 +10,13 @@ package ble static jobject gClassLoader; static jmethodID gFindClassMethod; +jclass +GetObjectClass(JNIEnv* env, jobject obj) { + jclass cls = (*env)->GetObjectClass(env, obj); + cls = (*env)->NewGlobalRef(env, cls); + return cls; +} + void SetLoader(JNIEnv* env, jobject context) { jclass cclass = (*env)->GetObjectClass(env, context); @@ -20,6 +27,68 @@ SetLoader(JNIEnv* env, jobject context) { gFindClassMethod = (*env)->GetMethodID(env, lclass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); } +jobject +GetBluetoothAdapter(JNIEnv* env, jobject ctx) { + jclass cls = (*env)->GetObjectClass(env, ctx); + if (cls == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get context class"); + return NULL; + } + jfieldID id = (*env)->GetStaticFieldID(env, cls, "BLUETOOTH_SERVICE", "Ljava/lang/String;"); + if (id == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get field ID"); + return NULL; + } + jstring btsrv = (jstring)(*env)->GetStaticObjectField(env, cls, id); + if (btsrv == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get static object field"); + return NULL; + } + jmethodID mid = (*env)->GetMethodID(env, cls, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + if (mid == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get method ID"); + return NULL; + } + jobject manager = (*env)->CallObjectMethod(env, ctx, mid, btsrv); + if (manager == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get bluetooth manager"); + return NULL; + } + cls = (*env)->GetObjectClass(env, manager); + if (cls == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get class of bluetooth manager"); + return NULL; + } + mid = (*env)->GetMethodID(env, cls, "getAdapter", "()Landroid/bluetooth/BluetoothAdapter;"); + if (mid == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get methodID for getAdapter()"); + return NULL; + } + jobject ad = (*env)->CallObjectMethod(env, manager, mid); + if (ad == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Can't get adapter"); + } else { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetBluetoothAdapter(): Found"); + } + ad = (*env)->NewGlobalRef(env, ad); + return ad; +} + +jstring +GetObjectStaticStringField(JNIEnv* env, jobject obj, char* fld) { + jclass cls = (*env)->GetObjectClass(env, obj); + if (cls == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetObjectStaticStringField(): Can't get class"); + return NULL; + } + jfieldID id = (*env)->GetStaticFieldID(env, cls, fld, "Ljava/lang/String;"); + if (id == NULL) { + __android_log_write(ANDROID_LOG_DEBUG, "gio", "GetObjectStaticStringField(): Can't find FieldID"); + return NULL; + } + return (jstring)(*env)->GetStaticObjectField(env, cls, id); +} + jclass FindClass(JNIEnv* env, char* name) { jstring strClassName = (*env)->NewStringUTF(env, name); @@ -36,6 +105,10 @@ void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID) { (*env)->CallVoidMethod(env, obj, methodID); } +jboolean CallBooleanMethod(JNIEnv *env, jobject obj, jmethodID mid) { + return (*env)->CallBooleanMethod(env, obj, mid); +} + jint CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) { return (*env)->CallIntMethod(env, obj, methodID); } @@ -74,7 +147,7 @@ type JNIEnv = C.JNIEnv func setJVM(jvm, context uintptr) { log.Print("set theJVM") theJVM = (*C.JavaVM)(unsafe.Pointer(jvm)) - log.Printf("theJVM = %d", uintptr(unsafe.Pointer(theJVM))) + //log.Printf("theJVM = %d", uintptr(unsafe.Pointer(theJVM))) RunInJVM(func(env *C.JNIEnv) { C.SetLoader(env, (C.jobject)(context)) }) @@ -86,10 +159,28 @@ func FindClass(env *C.JNIEnv, name string) C.jclass { return C.FindClass((*C.JNIEnv)(env), cname) } +func JniGetObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass { + return C.GetObjectClass(env, obj) +} + +func JniGetObjectStaticStringField(env *C.JNIEnv, obj C.jobject, fld string) C.jstring { + cstring := C.CString(fld) + defer C.free(unsafe.Pointer(cstring)) + return C.GetObjectStaticStringField(env, obj, cstring) +} + +func GetBluetoothAdapter(env *C.JNIEnv, ctx uintptr) C.jobject { + return C.GetBluetoothAdapter(env, (C.jobject)(ctx)) +} + func JniCallVoidMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) { C.CallVoidMethod(env, obj, methodID) } +func JniCallBooleanMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) bool { + return C.CallBooleanMethod(env, obj, methodID) == 0 +} + func JniCallIntMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) int { return (int)(C.CallIntMethod(env, obj, methodID)) } @@ -111,7 +202,7 @@ func RunInJVM(f func(env *C.JNIEnv)) { defer runtime.UnlockOSThread() var env *C.JNIEnv var detach bool - log.Printf("RunInJVM(): theJVM = %d", uintptr(unsafe.Pointer(theJVM))) + //log.Printf("RunInJVM(): theJVM = %d", uintptr(unsafe.Pointer(theJVM))) if res := C.GetEnv(theJVM, &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))