ble/BlessedConnect.java
Greg Pomerantz 876ea68622 Use commitNowAllowingStateLoss (API 24+) for fragment registration
The register() runnable can run after the activity has saved its
state (e.g. app started in the background), in which case commitNow
throws IllegalStateException and crashes the app. Rebuild Ble.jar.
2026-08-21 19:52:33 -04:00

300 lines
10 KiB
Java

package st.wow.git.ble;
import java.lang.String;
import java.lang.Runnable;
import java.util.List;
import java.util.UUID;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
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.os.Handler;
import android.util.Log;
import android.view.View;
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()");
}
public void register(View view) {
Log.d("gio", "BlessedConnect: register()");
Context ctx = view.getContext();
Handler handler = new Handler(ctx.getMainLooper());
BlessedConnect inst = this;
handler.post(new Runnable() {
public void run() {
Activity act = (Activity)ctx;
FragmentTransaction ft = act.getFragmentManager().beginTransaction();
ft.add(inst, "BlessedConnect");
if (android.os.Build.VERSION.SDK_INT >= 24) {
ft.commitNowAllowingStateLoss();
} else {
ft.commitNow();
}
}
});
}
@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);
System.loadLibrary("gio");
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<BluetoothGattService> 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<BluetoothGattCharacteristic> 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);
}