ble/ble_android.go

296 lines
6.2 KiB
Go

//go:generate mkdir -p classes
//go:generate javac -bootclasspath $ANDROID_HOME/platforms/android-29/android.jar -d classes BleConnect.java
//go:generate jar cf Ble.jar -C classes .
//go:generate rm -rf classes
package ble
import (
"log"
"sync"
"gioui.org/app"
_ "gioui.org/app/permission/bluetooth_le"
"git.wow.st/gmp/ble/gatt"
)
/*
#include "jni_android.h"
*/
import "C"
// Types required for ble.go
type bleHandle struct {
BleConnect C.jobject
state int
}
type bleState string
type Peripheral struct {
Name string
RSSI int
Identifier string
device C.jobject
}
type Service string
type Characteristic string
// Internal global variables
var (
gBLE *BLE // FIXME: move to lookup tables as in ble_darwin.go?
installCompleteOnce sync.Once
waitch chan struct{}
)
const (
STATE_OFF int = 10
STATE_TURNING_ON = 11
STATE_ON = 12
STATE_TURNING_OFF = 13
)
func init() {
waitch = make(chan struct{})
}
func connect() {
<-waitch
}
// Functions required by API
//peripheralLookup returns a pointer to a BLE struct related to the given
//Peripheral.
func peripheralLookup(p Peripheral) *BLE {
return gBLE
}
//newPeripheral creates a new Peripheral struct
func newPeripheral(name, id string, rssi int, dev C.jobject) Peripheral {
return Peripheral{
Name: name,
RSSI: rssi,
Identifier: id,
device: dev,
}
}
func (p Peripheral) IsIncomplete() bool {
if p.device == 0 {
return true
} else {
return false
}
}
func (p Peripheral) Retain() {
}
//stringState returns a string version of the BLE state
func (b *BLE) stringState() string {
switch b.handle.state {
case STATE_OFF:
return "powered off"
case STATE_TURNING_ON:
return "turning on"
case STATE_ON:
return "powered on"
case STATE_TURNING_OFF:
return "turning off"
default:
return "no state"
}
}
//readyToScan returns true if the hardware is ready to initiate a scan
func (b *BLE) readyToScan() bool {
connect()
var ret bool
runInJVM(func(env *C.JNIEnv) {
ret = C.enabled(env, b.handle.BleConnect) == C.JNI_TRUE
})
return ret
}
//scan puts the BLE hardware into scanning mode
func (b *BLE) scan() {
connect()
runInJVM(func(env *C.JNIEnv) {
C.scan(env, b.handle.BleConnect);
})
}
//stopScan stops a scan in progress
func (b *BLE) stopScan() {
connect()
runInJVM(func(env *C.JNIEnv) {
C.stopScan(env, b.handle.BleConnect);
})
}
//connectPeripheral attempts to connect to a Peripheral
func (b *BLE) connectPeripheral(x Peripheral) {
connect()
runInJVM(func(env *C.JNIEnv) {
C.connect(env, b.handle.BleConnect, x.device)
})
}
//cancelConnection cancels an in-progress connection attempt
func (b *BLE) cancelConnection(p Peripheral) {
connect()
runInJVM(func(env *C.JNIEnv) {
C.disconnect(env, b.handle.BleConnect)
})
}
//knownPeripheral returns a Peripheral that is known to the system without
//scanning
//Not implemented for Android
func (b *BLE) knownPeripheral(p Peripheral) (Peripheral, bool) {
return Peripheral{}, false
}
//DiscoverServices asks a Peripheral for its Services
func (x Peripheral) DiscoverServices() {
connect()
log.Printf("discovering services")
runInJVM(func(env *C.JNIEnv) {
C.discoverServices(env, gBLE.handle.BleConnect, x.device)
})
}
//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)}
gBLE = &BLE{
events: make(chan interface{}),
peripherals: ps,
}
return gBLE
}
//Enable
func (b *BLE) Enable(w *app.Window) {
log.Printf("ble.Enable()")
err := w.RegisterFragment("st/wow/git/ble/BleConnect")
log.Printf("ble.Enable() RegisterFragment() returned")
if err != nil {
log.Printf("Error! %s", err)
}
}
// Go callbacks from Java
//export Java_st_wow_git_ble_BleConnect_installComplete
func Java_st_wow_git_ble_BleConnect_installComplete(env *C.JNIEnv, class C.jclass, b C.jobject) {
log.Printf("installComplete()")
if (b == 0) {
log.Printf("BleConnect object is nil!")
}
gBLE.handle.BleConnect = (C.NewGlobalRef)(env,b)
h := app.PlatformHandle()
setJVM(h.JVM)
if C.enabled(env, gBLE.handle.BleConnect) == C.JNI_TRUE {
gBLE.handle.state = STATE_ON
gBLE.ready = true
gBLE.events <- UpdateStateEvent{State: gBLE.stringState()}
}
installCompleteOnce.Do(func() {
close(waitch)
})
log.Printf("installComplete() returning")
}
//export Java_st_wow_git_ble_BleConnect_updateState
func Java_st_wow_git_ble_BleConnect_updateState(env *C.JNIEnv, class C.jclass, s C.jint) {
log.Printf("UpdateState: %d", s)
gBLE.handle.state = (int)(s)
if gBLE.handle.state == STATE_ON {
gBLE.ready = true
}
gBLE.events <- UpdateStateEvent{State: gBLE.stringState()}
}
//export goOnScan
func goOnScan(cname, cid *C.char, rssi C.int, dev C.jobject) {
name := C.GoString(cname);
id := C.GoString(cid);
if name == "" {
return
}
peripheral := newPeripheral(name, id, (int)(rssi), dev)
if ok := gBLE.peripherals.Add(peripheral); ok {
gBLE.events <- DiscoverPeripheralEvent{Peripheral: peripheral}
}
}
//export goOnConnect
func goOnConnect(cid *C.char) {
id := C.GoString(cid)
var peripheral Peripheral
found := false
gBLE.peripherals.Lock()
for _, item := range gBLE.peripherals.items {
if item.p.Identifier == id {
peripheral = item.p
found = true
break
}
}
gBLE.peripherals.Unlock()
if !found {
log.Printf("Go: peripheral not found!")
}
gBLE.connections.UpdateState(peripheral, "connected")
gBLE.events <- ConnectEvent{peripheral}
log.Printf("Go: goOnConnect returning\n")
}
//export goOnDiscoverService
func goOnDiscoverService(cid *C.char, cuuid *C.char) {
id := C.GoString(cid)
uuid := C.GoString(cuuid)
var peripheral Peripheral
found := false
gBLE.peripherals.Lock()
for _, item := range gBLE.peripherals.items {
if item.p.Identifier == id {
peripheral = item.p
found = true
break
}
}
gBLE.peripherals.Unlock()
if !found {
log.Printf("Go: peripheral not found!")
}
gBLE.events <- DiscoverServiceEvent{
Peripheral: peripheral,
Gatt: gatt.Service{uuid},
}
}