Further restructuring and add stub implementation for Android.

This commit is contained in:
Greg 2019-10-28 12:43:33 -04:00
parent 206bd70461
commit f036c5e134
4 changed files with 260 additions and 18 deletions

2
ble.go
View File

@ -245,7 +245,7 @@ func (b *BLE) Connect(p Peripheral) bool {
return false return false
} }
b.Unlock() b.Unlock()
if p.p == nil { if p.IsIncomplete() {
var ok bool var ok bool
p, ok = b.knownPeripheral(p) p, ok = b.knownPeripheral(p)
if !ok { if !ok {

103
ble_android.go Normal file
View File

@ -0,0 +1,103 @@
package ble
import (
"gioui.org/app"
)
// Types required for ble.go
type bleState string
type bleHandle struct {
}
type Peripheral struct {
Name string
RSSI int
Identifier string
}
type Service string
type Characteristic string
// Internal global variables
// 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() {
h := app.PlatformHandle()
setJVM(h.JVM, h.Context)
}
//peripheralLookup returns a pointer to a BLE struct related to the given
//Peripheral.
func peripheralLookup(p Peripheral) *BLE {
return &BLE{}
}
//newPeripheral creates a new Peripheral struct
func newPeripheral() Peripheral {
return Peripheral{}
}
func (p Peripheral) IsIncomplete() bool {
return false
}
func (p Peripheral) Retain() {
}
//stringState returns a string version of the BLE state
func (b *BLE) stringState() string {
return ""
}
//readyToScan returns true if the hardware is ready to initiate a scan
func (b *BLE) readyToScan() bool {
return true
}
//scan puts the BLE hardware into scanning mode
func (b *BLE) scan() {
}
//stopScan stops a scan in progress
func (b *BLE) stopScan() {
}
//connectPeripheral attempts to connect to a Peripheral
func (b *BLE) connectPeripheral(x Peripheral) {
}
//cancelConnection cancels an in-progress connection attempt
func (b *BLE) cancelConnection(p Peripheral) {
}
//knownPeripheral returns a Peripheral that is known to the system without
//scanning
func (b *BLE) knownPeripheral(p Peripheral) (Peripheral, bool) {
return Peripheral{}, false
}
//DiscoverServices asks a Peripheral for its Services
func (x Peripheral) DiscoverServices() {
}
//DiscoverCharacteristics asks a Peripheral for the Characteristics related
//to a Service
func (p Peripheral) DiscoverCharacteristics(serv Service) {
}
//SetNotifyValue subscribes to a characteristic
func (p Peripheral) SetNotifyValue(c Characteristic) {
}
//NewBLE returns a pointer to a BLE struct after setting up the OS
//Bluetooth API.
func NewBLE() *BLE {
ps := Peripherals{items: make([]PeripheralListItem, 0)}
return &BLE{events: make(chan interface{}), peripherals: ps}
}

View File

@ -21,10 +21,6 @@ type bleHandle struct {
cm *ns.CBCentralManager cm *ns.CBCentralManager
} }
var cdLookup map[unsafe.Pointer]*BLE
var pdLookup map[unsafe.Pointer]*BLE
var pcache map[unsafe.Pointer]*Peripheral
type Peripheral struct { type Peripheral struct {
Name string Name string
RSSI int RSSI int
@ -36,11 +32,13 @@ type Peripheral struct {
type Service *ns.CBService type Service *ns.CBService
type Characteristic *ns.CBCharacteristic type Characteristic *ns.CBCharacteristic
// Functions required by API // Internal global variables
func peripheralLookup(p Peripheral) *BLE { var cdLookup map[unsafe.Pointer]*BLE
return pdLookup[p.p.Ptr()] var pdLookup map[unsafe.Pointer]*BLE
} var pcache map[unsafe.Pointer]*Peripheral
// Functions required by API
//Init needs to be called before the BLE library can be used. No setup needed //Init needs to be called before the BLE library can be used. No setup needed
//on Darwin. //on Darwin.
@ -48,6 +46,12 @@ func Init() {
} }
//peripheralLookup returns a pointer to a BLE struct related to the given
//Peripheral.
func peripheralLookup(p Peripheral) *BLE {
return pdLookup[p.p.Ptr()]
}
//newPeripheral creates a new Peripheral struct //newPeripheral creates a new Peripheral struct
func newPeripheral(x *ns.CBPeripheral) Peripheral { func newPeripheral(x *ns.CBPeripheral) Peripheral {
if pcache == nil { if pcache == nil {
@ -66,6 +70,19 @@ func newPeripheral(x *ns.CBPeripheral) Peripheral {
return ret return ret
} }
func (p Peripheral) IsIncomplete() bool {
return p.p == nil
}
func (x Peripheral) Retain() {
//NOTE: ns.ObjectAtIndex() calls SetFinalizer for us, which will be a problem
//later when we call GC(), so we always first clear the finalizer before
//setting a new one.
runtime.SetFinalizer(x.p, nil)
x.p.Retain()
x.p.GC()
}
//stringState returns a string version of the BLE state //stringState returns a string version of the BLE state
func (b *BLE) stringState() string { func (b *BLE) stringState() string {
x := b.state x := b.state
@ -319,15 +336,6 @@ func peripheralName(p *ns.CBPeripheral) string {
return ret return ret
} }
func (x *Peripheral) Retain() {
//NOTE: ns.ObjectAtIndex() calls SetFinalizer for us, which will be a problem
//later when we call GC(), so we always first clear the finalizer before
//setting a new one.
runtime.SetFinalizer(x.p, nil)
x.p.Retain()
x.p.GC()
}
func (b *BLE) setState(x ns.CBManagerState) { func (b *BLE) setState(x ns.CBManagerState) {
b.Lock() b.Lock()
defer b.Unlock() defer b.Unlock()

131
jni_android.go Normal file
View File

@ -0,0 +1,131 @@
package ble
/*
#cgo LDFLAGS: -landroid
#include <stdlib.h>
#include <jni.h>
#include <android/log.h>
static jobject gClassLoader;
static jmethodID gFindClassMethod;
void
SetLoader(JNIEnv* env, jobject context) {
jclass cclass = (*env)->GetObjectClass(env, context);
jmethodID gcl_id = (*env)->GetMethodID(env, cclass, "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject loader = (*env)->CallObjectMethod(env, context, gcl_id);
gClassLoader = (*env)->NewGlobalRef(env, loader);
jclass lclass = (*env)->GetObjectClass(env, loader);
gFindClassMethod = (*env)->GetMethodID(env, lclass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
}
jclass
FindClass(JNIEnv* env, char* name) {
jstring strClassName = (*env)->NewStringUTF(env, name);
return (*env)->CallObjectMethod(env, gClassLoader, gFindClassMethod, strClassName);
}
JNIEXPORT jobject
CreateObject(JNIEnv* env, jclass cls) {
jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "()V");
return (*env)->NewObject(env, cls, init);
}
void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
(*env)->CallVoidMethod(env, obj, methodID);
}
jint CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
return (*env)->CallIntMethod(env, obj, methodID);
}
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
return (*env)->GetMethodID(env, clazz, name, sig);
}
jint GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
return (*vm)->GetEnv(vm, (void **)env, version);
}
jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
}
jint DetachCurrentThread(JavaVM *vm) {
return (*vm)->DetachCurrentThread(vm);
}
*/
import "C"
import (
"errors"
"fmt"
"log"
"runtime"
"unsafe"
)
var theJVM *C.JavaVM
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)))
RunInJVM(func(env *C.JNIEnv) {
C.SetLoader(env, (C.jobject)(context))
})
}
func FindClass(env *C.JNIEnv, name string) C.jclass {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return C.FindClass((*C.JNIEnv)(env), cname)
}
func JniCallVoidMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) {
C.CallVoidMethod(env, obj, methodID)
}
func JniCallIntMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) int {
return (int)(C.CallIntMethod(env, obj, methodID))
}
func JniGetMethodID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jmethodID {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
csig := C.CString(sig)
defer C.free(unsafe.Pointer(csig))
return C.GetMethodID(env, cls, cname, csig)
}
func CreateObject(env *C.JNIEnv, cls C.jclass) C.jobject {
return C.CreateObject(env, (C.jclass)(cls))
}
func RunInJVM(f func(env *C.JNIEnv)) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var env *C.JNIEnv
var detach bool
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))
}
if C.AttachCurrentThread(theJVM, &env, nil) != C.JNI_OK {
panic(errors.New("runInJVM: AttachCurrentThread failed"))
}
detach = true
}
if detach {
defer func() {
C.DetachCurrentThread(theJVM)
}()
}
f(env)
}