diff --git a/Ble.jar b/Ble.jar index 73c9293..4c6f7bd 100644 Binary files a/Ble.jar and b/Ble.jar differ diff --git a/BleConnect.java b/BleConnect.java deleted file mode 100644 index b4e4d69..0000000 --- a/BleConnect.java +++ /dev/null @@ -1,293 +0,0 @@ -package st.wow.git.ble; - -import java.lang.Runnable; -import java.lang.String; -import java.util.List; -import java.util.UUID; -import android.util.Log; -import java.lang.Class; -import java.lang.ClassLoader; -import java.lang.reflect.Constructor; -import android.app.Activity; -import android.app.Fragment; -import android.os.Handler; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallback; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothManager; -import android.bluetooth.BluetoothProfile; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.Manifest; - -public class BleConnect extends Fragment { - BluetoothManager manager; - BluetoothAdapter adapter; - Handler handler; - boolean wantScan = false; - - final int PERMISSION_REQUEST = 1; - - final int REQUEST_ENABLE_BT = 1; - - public BleConnect() { - Log.d("gio", "BleConnect()"); - } - - @Override public void onAttach(Context ctx) { - super.onAttach(ctx); - Log.d("gio", "BleConnect: onAttach()"); - ctx.registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)); - manager = (BluetoothManager) ctx.getSystemService(ctx.BLUETOOTH_SERVICE); - adapter = manager.getAdapter(); - handler = new Handler(ctx.getMainLooper()); - if (!enabled()) { - Log.d("gio", "BleConnect: enabling adapter"); - Intent enableBtIntent = new Intent(adapter.ACTION_REQUEST_ENABLE); - handler.post(new Runnable() { - public void run() { - startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); - } - }); - } else { - Log.d("gio", "BleConnect: adapter is enabled"); - } - if (ctx.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST); - } - installComplete(this); - } - - public boolean enabled() { - return (adapter != null && adapter.isEnabled()); - } - - private final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Log.d("gio", "Received broadcast"); - if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { - updateState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)); - } - } - }; - - @Override - public void onDestroy() { - Log.d("gio","onDestroy()"); - stopScan(); - getContext().unregisterReceiver(receiver); - super.onDestroy(); - } - - private final BluetoothAdapter.LeScanCallback scanCallback = new BluetoothAdapter.LeScanCallback() { - public void onLeScan(final BluetoothDevice dev, int rssi, byte[] scanRecord) { - Log.d("gio","onLeScan(): " + dev.getName()); - onScan(dev.getName(), dev.getAddress(), rssi, dev); - } - }; - - public void scan() { - if (!enabled() || getContext().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - Log.d("gio","BleConnect: scan() not enabled or no permissions, wantScan = true"); - wantScan = true; - return; - } - Log.d("gio","BleConnect: scan() starting scan"); - wantScan = false; - handler.post(new Runnable() { - public void run() { - adapter.startLeScan(scanCallback); - } - }); - } - - public void stopScan() { - if (!enabled()) { - return; - } - Log.d("gio", "Stop scan"); - handler.post(new Runnable() { - public void run() { - adapter.stopLeScan(scanCallback); - } - }); - } - - private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() { -@Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - BluetoothDevice device = gatt.getDevice(); - String addr = device.getAddress(); - if (status != BluetoothGatt.GATT_SUCCESS) { - Log.d("gio", "onConnectionStateChange: error code " + status); - onDisconnect(gatt, addr); - Log.d("gio", "gatt.close()"); - gatt.close(); - return; - } - switch (newState) { - case BluetoothProfile.STATE_CONNECTED: { - Log.d("gio", "Connected"); - Log.d("gio", "Address = " + addr); - onConnect(gatt, addr); - break; - } - case BluetoothProfile.STATE_CONNECTING: - case BluetoothProfile.STATE_DISCONNECTING: { - break; - } - case BluetoothProfile.STATE_DISCONNECTED: { - Log.d("gio", "Disconnected"); - onDisconnect(gatt, addr); - Log.d("gio", "gatt.close()"); - gatt.close(); - break; - } - default: { - Log.d("gio", "onConnectionStateChange: unknown state"); - onDisconnect(gatt, addr); - Log.d("gio", "gatt.close()"); - gatt.close(); - break; - } - } - } - public void onServicesDiscovered(BluetoothGatt gatt, int status) { - for (BluetoothGattService serv : gatt.getServices()) { - onDiscoverService(gatt.getDevice().getAddress(), serv.getUuid().toString(), serv); - } - } - public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic chr) { - byte[] v = chr.getValue(); - characteristicChanged(gatt.getDevice().getAddress(), chr.getUuid().toString(), chr, v, v.length); - } - }; - - public void connect(BluetoothDevice dev) { - if (dev == null) { - return; - } - Log.d("gio","BleConnect: connect"); - - handler.post(new Runnable() { - public void run() { - dev.connectGatt(getContext(), false, gattCallback, BluetoothDevice.TRANSPORT_LE); - } - }); - } - - public void disconnect(BluetoothGatt gatt) { - if (gatt == null) { - return; - } - Log.d("gio","BleConnect: disconnect"); - handler.post(new Runnable() { - public void run() { - boolean callClose = false; - BluetoothDevice device = gatt.getDevice(); - if (device != null) { - callClose = (manager.getConnectionState(device, BluetoothProfile.GATT) != BluetoothGatt.STATE_CONNECTED); - } - gatt.disconnect(); - if (callClose) { - gatt.close(); - } - } - }); - } - - public void discoverServices(BluetoothGatt gatt) { - if (gatt == null) { - return; - } - handler.post(new Runnable() { - public void run() { - gatt.discoverServices(); - } - }); - } - - public void discoverCharacteristics(BluetoothGatt gatt, BluetoothGattService serv) { - Log.d("gio","BleConnect: discoverCharacteristics()"); - if (gatt == null) { - Log.d("gio","BleConnect: gatt == null"); - return; - } - List chrs = serv.getCharacteristics(); - if (chrs.isEmpty()) { - Log.d("gio", "BleConnect: no characteristics found!"); - } - for (BluetoothGattCharacteristic chr : chrs) { - Log.d("gio","BleConnect: -- " + chr.getUuid().toString()); - onDiscoverCharacteristic(gatt.getDevice().getAddress(), serv.getUuid().toString(), serv, chr.getUuid().toString(), chr); - } - } - - public void setCharacteristicNotification(BluetoothGatt gatt, BluetoothGattCharacteristic chr) { - gatt.setCharacteristicNotification(chr, true); - - BluetoothGattDescriptor descriptor = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); // Client Characteristic Config - descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); - gatt.writeDescriptor(descriptor); - } - - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - Log.d("gio", "BleConnect: onActivityResult()"); - if (requestCode == REQUEST_ENABLE_BT) { - Log.d("gio", "BleConnect: onActivityResult() REQUEST_ENABLE_BT"); - switch (resultCode) { - case Activity.RESULT_OK: { - Log.d("gio", "BleConnect: onActivityResult() -- OK"); - if (wantScan) { - scan(); - } - break; - } - case Activity.RESULT_CANCELED: { - Log.d("gio", "BleConnect: onActivityResult() -- Cancelled"); - break; - } - } - } - } - - @Override - public void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults) { - Log.d("gio", "BleConnect: onRequestPermissionsResult"); - if (requestCode == PERMISSION_REQUEST) { - boolean granted = true; - for (int x : grantResults) { - if (x == PackageManager.PERMISSION_DENIED) { - granted = false; - break; - } - } - if (!granted) { - Log.d("gio", "BleConnect: permissions not granted"); - return; - } - Log.d("gio", "BleConnect: permissions granted"); - if (wantScan) { - scan(); - } - } - } - - static private native void installComplete(BleConnect p); - static private native void updateState(int s); - static private native void onScan(String name, String id, int rssi, BluetoothDevice dev); - static private native void onConnect(BluetoothGatt gatt, String id); - static private native void onDisconnect(BluetoothGatt gatt, String id); - static private native void onDiscoverService(String id, String uuid, BluetoothGattService serv); - static private native void onDiscoverCharacteristic(String id, String suuid, BluetoothGattService serv, String cuuid, BluetoothGattCharacteristic chr); - static private native void characteristicChanged(String id, String cuuid, BluetoothGattCharacteristic chr, byte[] value, int length); -} - diff --git a/BlessedConnect.java b/BlessedConnect.java new file mode 100644 index 0000000..bbd1ed6 --- /dev/null +++ b/BlessedConnect.java @@ -0,0 +1,275 @@ +package st.wow.git.ble; + +import java.lang.String; +import java.util.List; +import java.util.UUID; +import android.app.Activity; +import android.app.Fragment; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.bluetooth.le.ScanResult; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.Manifest; +import android.util.Log; + +import com.welie.blessed.BluetoothCentral; +import com.welie.blessed.BluetoothCentralCallback; +import com.welie.blessed.BluetoothPeripheral; +import com.welie.blessed.BluetoothPeripheralCallback; + +public class BlessedConnect extends Fragment { + BluetoothCentral central; + boolean wantScan = false; + + final int PERMISSION_REQUEST = 1; + final int REQUEST_ENABLE_BT = 2; + + public BlessedConnect() { + Log.d("gio", "BlessedConnect()"); + } + + @Override public void onAttach(Context ctx) { + super.onAttach(ctx); + Log.d("gio", "BlessedConnect: onAttach()"); + ctx.registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)); + if (ctx.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST); + } + central = new BluetoothCentral(ctx, centralCallback, null); + installComplete(this); + } + + private final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d("gio", "Received broadcast"); + if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + updateState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)); + } + } + }; + + public boolean enabled() { + Context ctx = getContext(); + //The Blessed library does not expose this functionality, so + //we need to get our own BluetoothAdapter and check its status. + BluetoothAdapter adapter = ((BluetoothManager)ctx.getSystemService(ctx.BLUETOOTH_SERVICE)).getAdapter(); + return (adapter != null && adapter.isEnabled()); + } + + @Override + public void onDestroy() { + Log.d("gio","onDestroy()"); + stopScan(); + getContext().unregisterReceiver(receiver); + super.onDestroy(); + } + + public void scan() { + if (enabled()) { + central.scanForPeripherals(); + } else { + wantScan = true; + } + } + + public void stopScan() { + central.stopScan(); + } + + private final BluetoothCentralCallback centralCallback = new BluetoothCentralCallback() { + @Override + public void onDiscoveredPeripheral(BluetoothPeripheral peripheral, ScanResult scanResult) { + Log.d("gio", "BlessedConnect: onDiscoveredPeripheral(): " + peripheral.getName()); + onScan(peripheral.getName(), peripheral.getAddress(), scanResult.getRssi(), peripheral); + } + + @Override + public void onConnectedPeripheral(BluetoothPeripheral peripheral) { + if (peripheral.getState() == peripheral.STATE_CONNECTED) { + Log.d("gio", "BlessedConnect: Connected to " + peripheral.getName()); + Log.d("gio", "BlessedConnect: Address = " + peripheral.getAddress()); + + onConnect(peripheral, peripheral.getAddress()); + } else { + onDisconnect(peripheral, peripheral.getAddress()); + } + } + + @Override + public void onConnectionFailed(BluetoothPeripheral peripheral, int status) { + Log.d("gio", "BlessedConnect: onConnectionFailed()"); + onDisconnect(peripheral, peripheral.getAddress()); + } + + @Override + public void onDisconnectedPeripheral(BluetoothPeripheral peripheral, int status) { + Log.d("gio", "BlessedConnect: Disconnected"); + onDisconnect(peripheral, peripheral.getAddress()); + } + + @Override + public void onScanFailed(int errorCode) { + Log.d("gio", "BlessedConnect: Scan failed"); + } + + }; + + private final BluetoothPeripheralCallback peripheralCallback = new BluetoothPeripheralCallback() { + public void onBondingFailed(BluetoothPeripheral peripheral) { + Log.d("gio", "BlessedConnect: onBondingFailed()"); + } + public void onBondingStarted(BluetoothPeripheral peripheral) { + Log.d("gio", "BlessedConnect: onBondingStarted()"); + } + public void onBondingSucceeded(BluetoothPeripheral peripheral) { + Log.d("gio", "BlessedConnect: onBondingSucceeded()"); + } + public void onBondLost(BluetoothPeripheral peripheral) { + Log.d("gio", "BlessedConnect: onBondLost()"); + } + public void onCharacteristicUpdate(BluetoothPeripheral peripheral, byte[] value, BluetoothGattCharacteristic characteristic, int status) { + Log.d("gio", "BlessedConnect: onCharacteristicUpdate()"); + characteristicChanged(peripheral.getAddress(), characteristic.getUuid().toString(), characteristic, value, value.length); + } + public void onCharacteristicWrite(BluetoothPeripheral peripheral, byte[] value, android.bluetooth.BluetoothGattCharacteristic characteristic, int status) { + Log.d("gio", "onCharacteristicWrite(): " + characteristic.getUuid().toString()); + } + public void onDescriptorRead(BluetoothPeripheral peripheral, byte[] value, android.bluetooth.BluetoothGattDescriptor descriptor, int status) { + Log.d("gio", "onDescriptorRead()"); + } + public void onDescriptorWrite(BluetoothPeripheral peripheral, byte[] value, android.bluetooth.BluetoothGattDescriptor descriptor, int status) { + Log.d("gio", "onDescriptorWrite(): " + descriptor.getUuid().toString()); + } + public void onMtuChanged(BluetoothPeripheral peripheral, int mtu, int status) { + Log.d("gio", "onMtuChanged()"); + } + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + Log.d("gio", "onReadRemoteRssi"); + } + public void onNotificationStateUpdate(BluetoothPeripheral peripheral, BluetoothGattCharacteristic characteristic, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + if(peripheral.isNotifying(characteristic)) { + Log.i("gio", String.format("BlessedConnect: Notify set to 'on' for %s", characteristic.getUuid())); + } else { + Log.i("gio", String.format("BlessedConnect: Notify set to 'off' for %s", characteristic.getUuid())); + } + } else { + Log.d("gio", "BlessedConnect: onNotificationStateUpdate(): failed"); + } + } + public void onServicesDiscovered(BluetoothPeripheral peripheral) { + Log.d("gio", "BlessedConnect: onServicesDiscovered()"); + } + }; + + public void connect(BluetoothPeripheral peripheral) { + if (peripheral == null) { + return; + } + Log.d("gio","BlessedConnect: connect"); + central.connectPeripheral(peripheral, peripheralCallback); + } + + public void disconnect(BluetoothPeripheral peripheral) { + if (peripheral == null) { + return; + } + Log.d("gio","BlessedConnect: disconnect"); + peripheral.cancelConnection(); + onDisconnect(peripheral, peripheral.getAddress()); + } + + public void discoverServices(BluetoothPeripheral peripheral) { + if (peripheral == null) { + return; + } + List services = peripheral.getServices(); + for (BluetoothGattService serv : services) { + onDiscoverService(peripheral.getAddress(), serv.getUuid().toString(), serv); + } + } + + public void discoverCharacteristics(BluetoothPeripheral peripheral, BluetoothGattService serv) { + if (peripheral == null || serv == null) { + return; + } + List chrs = serv.getCharacteristics(); + for (BluetoothGattCharacteristic chr : chrs) { + onDiscoverCharacteristic(peripheral.getAddress(), serv.getUuid().toString(), serv, chr.getUuid().toString(), chr); + } + } + + public void readCharacteristic(BluetoothPeripheral peripheral, BluetoothGattCharacteristic chr) { + if (peripheral == null || chr == null) { + return; + } + peripheral.readCharacteristic(chr); + } + public boolean setCharacteristicNotification(BluetoothPeripheral peripheral, BluetoothGattCharacteristic chr) { + if (peripheral == null || chr == null) { + return false; + } + return peripheral.setNotify(chr, true); + } + + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.d("gio", "BlessedConnect: onActivityResult()"); + if (requestCode == REQUEST_ENABLE_BT) { + Log.d("gio", "BlessedConnect: onActivityResult() REQUEST_ENABLE_BT"); + switch (resultCode) { + case Activity.RESULT_OK: { + Log.d("gio", "BlessedConnect: onActivityResult() -- OK"); + if (wantScan) { + scan(); + } + break; + } + case Activity.RESULT_CANCELED: { + Log.d("gio", "BlessedConnect: onActivityResult() -- Cancelled"); + break; + } + } + } + } + + @Override + public void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults) { + Log.d("gio", "BlessedConnect: onRequestPermissionsResult"); + if (requestCode == PERMISSION_REQUEST) { + boolean granted = true; + for (int x : grantResults) { + if (x == PackageManager.PERMISSION_DENIED) { + granted = false; + break; + } + } + if (!granted) { + Log.d("gio", "BlessedConnect: permissions not granted"); + return; + } + Log.d("gio", "BlessedConnect: permissions granted"); + if (wantScan) { + scan(); + } + } + } + + static private native void installComplete(BlessedConnect p); + + static private native void updateState(int s); + static private native void onScan(String name, String id, int rssi, BluetoothPeripheral peripheral); + static private native void onConnect(BluetoothPeripheral peripheral, String id); + static private native void onDisconnect(BluetoothPeripheral peripheral, String id); + static private native void onDiscoverService(String id, String uuid, BluetoothGattService serv); + static private native void onDiscoverCharacteristic(String id, String suuid, BluetoothGattService serv, String cuuid, BluetoothGattCharacteristic chr); + static private native void characteristicChanged(String id, String cuuid, BluetoothGattCharacteristic chr, byte[] value, int length); +} diff --git a/README.md b/README.md index fc07c55..d42ccc9 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,5 @@ Using Gio, your main event loop will look something like this: } } ``` + +An example heart rate monitor app can be found at [git.wow.st/gmp/hrm](https://git.wow.st/gmp/hrm). diff --git a/ble_android.go b/ble_android.go index 6e52018..8b4b773 100644 --- a/ble_android.go +++ b/ble_android.go @@ -1,5 +1,5 @@ //go:generate mkdir -p classes -//go:generate javac -bootclasspath $ANDROID_HOME/platforms/android-29/android.jar -d classes BleConnect.java +//go:generate javac -bootclasspath $ANDROID_HOME/platforms/android-29/android.jar -classpath blessed-full.jar -d classes BlessedConnect.java //go:generate jar cf Ble.jar -C classes . //go:generate rm -rf classes @@ -24,7 +24,7 @@ import "C" // Types required for ble.go type bleHandle struct { - BleConnect C.jobject + BlessedConnect C.jobject state int } @@ -34,8 +34,7 @@ type Peripheral struct { Name string RSSI int Identifier string - device C.jobject - gatt C.jobject + peripheral C.jobject } type Service struct { @@ -90,17 +89,17 @@ func peripheralLookup(p Peripheral) *BLE { // FIXME: check what happens with "incomplete" Peripherals that have only // an Identifier set. Do the other functions on Peripherals do something // sensible? -func newPeripheral(name, id string, rssi int, dev C.jobject) Peripheral { +func newPeripheral(name, id string, rssi int, peripheral C.jobject) Peripheral { return Peripheral{ Name: name, RSSI: rssi, Identifier: id, - device: dev, + peripheral: peripheral, } } func (p Peripheral) IsIncomplete() bool { - if p.device == 0 { + if p.peripheral == 0 { return true } else { return false @@ -132,7 +131,7 @@ func (b *BLE) readyToScan() bool { connect() var ret bool runInJVM(func(env *C.JNIEnv) { - ret = C.enabled(env, b.handle.BleConnect) == C.JNI_TRUE + ret = C.enabled(env, b.handle.BlessedConnect) == C.JNI_TRUE }) return ret } @@ -141,7 +140,7 @@ func (b *BLE) readyToScan() bool { func (b *BLE) scan() { connect() runInJVM(func(env *C.JNIEnv) { - C.scan(env, b.handle.BleConnect); + C.scan(env, b.handle.BlessedConnect); }) } @@ -149,15 +148,14 @@ func (b *BLE) scan() { func (b *BLE) stopScan() { connect() runInJVM(func(env *C.JNIEnv) { - C.stopScan(env, b.handle.BleConnect); + C.stopScan(env, b.handle.BlessedConnect); }) } //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) + C.connect(env, b.handle.BlessedConnect, x.peripheral) }) } @@ -165,7 +163,7 @@ func (b *BLE) connectPeripheral(x Peripheral) { func (b *BLE) cancelConnection(p Peripheral) { connect() runInJVM(func(env *C.JNIEnv) { - C.disconnect(env, b.handle.BleConnect, p.gatt) + C.disconnect(env, b.handle.BlessedConnect, p.peripheral) }) } @@ -178,42 +176,38 @@ func (b *BLE) knownPeripheral(p Peripheral) (Peripheral, bool) { //DiscoverServices asks a Peripheral for its Services func (p Peripheral) DiscoverServices() { - if p.gatt == 0 { - return - } - connect() - log.Printf("discovering services") - runInJVM(func(env *C.JNIEnv) { - C.discoverServices(env, gBLE.handle.BleConnect, p.gatt) - }) + //launch a goroutine because this function calls back directly + //from the same thread (the underlying Java call is synchronous) + go func() { + connect() + log.Printf("discovering services") + runInJVM(func(env *C.JNIEnv) { + C.discoverServices(env, gBLE.handle.BlessedConnect, p.peripheral) + }) + }() } //DiscoverCharacteristics asks a Peripheral for the Characteristics related //to a Service func (p Peripheral) DiscoverCharacteristics(serv Service) { - if p.gatt == 0 { - return - } //launch a goroutine because this function calls back directly - //from the same thread. + //from the same thread (the underlying Java call is synchronous) go func() { connect() log.Printf("discovering characteristics") runInJVM(func(env *C.JNIEnv) { - C.discoverCharacteristics(env, gBLE.handle.BleConnect, p.gatt, serv.service) + C.discoverCharacteristics(env, gBLE.handle.BlessedConnect, p.peripheral, serv.service) }) - log.Printf("discovering characteristics done") }() } //SetNotifyValue subscribes to a characteristic func (p Peripheral) SetNotifyValue(c Characteristic) { - if p.gatt == 0 { - return - } runInJVM(func(env *C.JNIEnv) { - log.Printf("setCharacteristicNotification: %s", c.UUID) - C.setCharacteristicNotification(env, gBLE.handle.BleConnect, p.gatt, c.characteristic) + result := (C.setCharacteristicNotification(env, gBLE.handle.BlessedConnect, p.peripheral, c.characteristic) == C.JNI_TRUE) + if (!result) { + log.Printf("setCharacteristicNotification: %s failed", c.UUID) + } }) } @@ -231,21 +225,21 @@ func NewBLE() *BLE { //Enable func (b *BLE) Enable(w *app.Window) { log.Printf("ble.Enable()") - w.RegisterFragment("st/wow/git/ble/BleConnect") + w.RegisterFragment("st/wow/git/ble/BlessedConnect") } // 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) { +//export Java_st_wow_git_ble_BlessedConnect_installComplete +func Java_st_wow_git_ble_BlessedConnect_installComplete(env *C.JNIEnv, class C.jclass, b C.jobject) { log.Printf("installComplete()") if (b == 0) { - log.Printf("BleConnect object is nil!") + log.Printf("BlessedConnect object is nil!") } - gBLE.handle.BleConnect = (C.NewGlobalRef)(env,b) + gBLE.handle.BlessedConnect = (C.NewGlobalRef)(env,b) h := app.PlatformHandle() setJVM(h.JVM) - if C.enabled(env, gBLE.handle.BleConnect) == C.JNI_TRUE { + if C.enabled(env, gBLE.handle.BlessedConnect) == C.JNI_TRUE { gBLE.handle.state = STATE_ON gBLE.ready = true gBLE.events <- UpdateStateEvent{State: gBLE.stringState()} @@ -256,8 +250,8 @@ func Java_st_wow_git_ble_BleConnect_installComplete(env *C.JNIEnv, class C.jclas 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) { +//export Java_st_wow_git_ble_BlessedConnect_updateState +func Java_st_wow_git_ble_BlessedConnect_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 { @@ -280,13 +274,13 @@ func goOnScan(cname, cid *C.char, rssi C.int, dev C.jobject) { } //export goOnConnect -func goOnConnect(gatt C.jobject, cid *C.char) { +func goOnConnect(p C.jobject, cid *C.char) { id := C.GoString(cid) peripheral := gBLE.retrievePeripheral(id, p) - if peripheral.gatt == 0 { - log.Printf("goOnConnect(): gatt == null") + if peripheral.peripheral == 0 { + log.Printf("goOnConnect(): peripheral == null") } gBLE.connections.UpdateState(peripheral, "connected") @@ -300,8 +294,8 @@ func goOnDisconnect(p C.jobject, cid *C.char) { peripheral := gBLE.retrievePeripheral(id) - if peripheral.device == 0 { - log.Printf("goOnDisconnect(): device == null") + if peripheral.peripheral == 0 { + log.Printf("goOnDisconnect(): peripheral == null") } go func() { @@ -360,8 +354,6 @@ func goOnDiscoverCharacteristic(cid, csuuid *C.char, serv C.jobject, ccuuid *C.c func goOnCharacteristicChanged(cid, ccuuid *C.char, char C.jobject, cvalue *C.char, length C.jint) { id := C.GoString(cid) cuuid := C.GoString(ccuuid) - log.Printf("goOnCharacteristicChanged: %s", cuuid) - log.Printf("goOnCharacteristicChanged: length = %d", length) peripheral := gBLE.retrievePeripheral(id) diff --git a/blessed-full.jar b/blessed-full.jar new file mode 100644 index 0000000..e6cd12a Binary files /dev/null and b/blessed-full.jar differ diff --git a/jni_android.c b/jni_android.c index d66a55b..e50c218 100644 --- a/jni_android.c +++ b/jni_android.c @@ -27,36 +27,43 @@ stopScan(JNIEnv *env, jobject b) { void connect(JNIEnv *env, jobject b, jobject d) { jclass cls = (*env)->GetObjectClass(env, b); - jmethodID mid = (*env)->GetMethodID(env, cls, "connect", "(Landroid/bluetooth/BluetoothDevice;)V"); + jmethodID mid = (*env)->GetMethodID(env, cls, "connect", "(Lcom/welie/blessed/BluetoothPeripheral;)V"); (*env)->CallObjectMethod(env, b, mid, d); } void disconnect(JNIEnv *env, jobject b, jobject g) { jclass cls = (*env)->GetObjectClass(env, b); - jmethodID mid = (*env)->GetMethodID(env, cls, "disconnect", "(Landroid/bluetooth/BluetoothGatt;)V"); + jmethodID mid = (*env)->GetMethodID(env, cls, "disconnect", "(Lcom/welie/blessed/BluetoothPeripheral;)V"); (*env)->CallVoidMethod(env, b, mid, g); } void discoverServices(JNIEnv *env, jobject b, jobject p) { jclass cls = (*env)->GetObjectClass(env, b); - jmethodID mid = (*env)->GetMethodID(env, cls, "discoverServices", "(Landroid/bluetooth/BluetoothGatt;)V"); + jmethodID mid = (*env)->GetMethodID(env, cls, "discoverServices", "(Lcom/welie/blessed/BluetoothPeripheral;)V"); (*env)->CallVoidMethod(env, b, mid, p); } void discoverCharacteristics(JNIEnv *env, jobject b, jobject g, jobject s) { jclass cls = (*env)->GetObjectClass(env, b); - jmethodID mid = (*env)->GetMethodID(env, cls, "discoverCharacteristics", "(Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattService;)V"); + jmethodID mid = (*env)->GetMethodID(env, cls, "discoverCharacteristics", "(Lcom/welie/blessed/BluetoothPeripheral;Landroid/bluetooth/BluetoothGattService;)V"); (*env)->CallVoidMethod(env, b, mid, g, s); } -void +jboolean setCharacteristicNotification(JNIEnv *env, jobject b, jobject g, jobject c) { jclass cls = (*env)->GetObjectClass(env, b); - jmethodID mid = (*env)->GetMethodID(env, cls, "setCharacteristicNotification", "(Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattCharacteristic;)V"); - (*env)->CallVoidMethod(env, b, mid, g, c); + jmethodID mid = (*env)->GetMethodID(env, cls, "setCharacteristicNotification", "(Lcom/welie/blessed/BluetoothPeripheral;Landroid/bluetooth/BluetoothGattCharacteristic;)Z"); + return (*env)->CallBooleanMethod(env, b, mid, g, c); +} + +void +readCharacteristic(JNIEnv *env, jobject b, jobject g, jobject c) { + jclass cls = (*env)->GetObjectClass(env, b); + jmethodID mid = (*env)->GetMethodID(env, cls, "readCharacteristic", "(Lcom/welie/blessed/BluetoothPeripheral;Landroid/bluetooth/BluetoothGattCharacteristic;)V"); + (*env)->CallBooleanMethod(env, b, mid, g, c); } jint @@ -80,7 +87,7 @@ NewGlobalRef(JNIEnv *env, jobject o) { } void -Java_st_wow_git_ble_BleConnect_onScan(JNIEnv *env, jclass class, jstring jname, jstring jid, jint jrssi, jobject dev) { +Java_st_wow_git_ble_BlessedConnect_onScan(JNIEnv *env, jclass class, jstring jname, jstring jid, jint jrssi, jobject dev) { const char* name = (*env)->GetStringUTFChars(env, jname, NULL); const char* id = (*env)->GetStringUTFChars(env, jid, NULL); jobject gdev = (*env)->NewGlobalRef(env, dev); @@ -90,22 +97,23 @@ Java_st_wow_git_ble_BleConnect_onScan(JNIEnv *env, jclass class, jstring jname, } void -Java_st_wow_git_ble_BleConnect_onConnect(JNIEnv *env, jclass class, jobject gatt, jstring jid) { +Java_st_wow_git_ble_BlessedConnect_onConnect(JNIEnv *env, jclass class, jobject peripheral, jstring jid) { const char* id = (*env)->GetStringUTFChars(env, jid, NULL); - jobject ggatt = (*env)->NewGlobalRef(env, gatt); - goOnConnect(ggatt, id); + jobject gperipheral = (*env)->NewGlobalRef(env, peripheral); + goOnConnect(gperipheral, id); (*env)->ReleaseStringUTFChars(env, jid, id); } void -Java_st_wow_git_ble_BleConnect_onDisconnect(JNIEnv *env, jclass class, jobject peripheral, jstring jid) { +Java_st_wow_git_ble_BlessedConnect_onDisconnect(JNIEnv *env, jclass class, jobject peripheral, jstring jid) { const char* id = (*env)->GetStringUTFChars(env, jid, NULL); jobject gperipheral = (*env)->NewGlobalRef(env, peripheral); goOnDisconnect(gperipheral, id); (*env)->ReleaseStringUTFChars(env, jid, id); } -Java_st_wow_git_ble_BleConnect_onDiscoverService(JNIEnv *env, jclass class, jstring jid, jstring juuid, jobject serv) { +void +Java_st_wow_git_ble_BlessedConnect_onDiscoverService(JNIEnv *env, jclass class, jstring jid, jstring juuid, jobject serv) { const char* id = (*env)->GetStringUTFChars(env, jid, NULL); const char* uuid = (*env)->GetStringUTFChars(env, juuid, NULL); jobject gserv = (*env)->NewGlobalRef(env, serv); @@ -115,7 +123,7 @@ Java_st_wow_git_ble_BleConnect_onDiscoverService(JNIEnv *env, jclass class, jstr } void -Java_st_wow_git_ble_BleConnect_onDiscoverCharacteristic(JNIEnv *env, jclass class, jstring jid, jstring jsuuid, jobject serv, jstring jcuuid, jobject chr) { +Java_st_wow_git_ble_BlessedConnect_onDiscoverCharacteristic(JNIEnv *env, jclass class, jstring jid, jstring jsuuid, jobject serv, jstring jcuuid, jobject chr) { const char* id = (*env)->GetStringUTFChars(env, jid, NULL); const char* suuid = (*env)->GetStringUTFChars(env, jsuuid, NULL); const char* cuuid = (*env)->GetStringUTFChars(env, jcuuid, NULL); @@ -128,7 +136,7 @@ Java_st_wow_git_ble_BleConnect_onDiscoverCharacteristic(JNIEnv *env, jclass clas } void -Java_st_wow_git_ble_BleConnect_characteristicChanged(JNIEnv *env, jclass class, jstring jid, jstring jcuuid, jobject chr, jbyteArray jvalue, jint len) { +Java_st_wow_git_ble_BlessedConnect_characteristicChanged(JNIEnv *env, jclass class, jstring jid, jstring jcuuid, jobject chr, jbyteArray jvalue, jint len) { const char* id = (*env)->GetStringUTFChars(env, jid, NULL); const char* cuuid = (*env)->GetStringUTFChars(env, jcuuid, NULL); jbyte* value = (*env)->GetByteArrayElements(env, jvalue, NULL); diff --git a/jni_android.h b/jni_android.h index da4f0ea..d8977ec 100644 --- a/jni_android.h +++ b/jni_android.h @@ -7,7 +7,8 @@ void connect(JNIEnv *env, jobject b, jobject d); void disconnect(JNIEnv *env, jobject b, jobject g); void discoverServices(JNIEnv *env, jobject b, jobject p); void discoverCharacteristics(JNIEnv *env, jobject b, jobject g, jobject s); -void setCharacteristicNotification(JNIEnv *env, jobject b, jobject g, jobject c); +void readCharacteristic(JNIEnv *env, jobject b, jobject g, jobject c); +jboolean setCharacteristicNotification(JNIEnv *env, jobject b, jobject g, jobject c); jint GetEnv(JavaVM *vm, JNIEnv **env, jint version); jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args); jint DetachCurrentThread(JavaVM *vm); diff --git a/timber.jar b/timber.jar new file mode 100644 index 0000000..7cf3aeb Binary files /dev/null and b/timber.jar differ