package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_KEY_DEVICE_BLE_API_DEVICE_NOTIFY;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_KEY_DEVICE_BLE_API_DEVICE_READ_WRITE;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_KEY_DEVICE_BLE_API_DEVICE_STATE;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_KEY_DEVICE_BLE_API_PACKAGE;
|
|
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattServer;
|
import android.bluetooth.BluetoothGattService;
|
import android.content.BroadcastReceiver;
|
import android.content.Context;
|
import android.content.Intent;
|
import android.content.IntentFilter;
|
import android.widget.Toast;
|
|
import androidx.core.content.ContextCompat;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
|
import java.util.HashMap;
|
import java.util.UUID;
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
|
public class BleIntentApi {
|
private Context context;
|
GBDevice device;
|
BtLEQueue queue;
|
Logger logger;
|
|
private boolean intentApiEnabledDeviceState = false;
|
private boolean intentApiEnabledReadWrite= false;
|
private boolean intentApiEnabledNotifications= false;
|
private String intentApiPackage = "";
|
private boolean intentApiCharacteristicReceiverRegistered = false;
|
private boolean intentApiDeviceStateReceiverRegistered = false;
|
private String lastReportedState = null;
|
|
private final HashMap<String, BluetoothGattCharacteristic> characteristics = new HashMap<>();
|
|
public static final String BLE_API_COMMAND_READ = "nodomain.freeyourgadget.gadgetbridge.ble_api.commands.CHARACTERISTIC_READ";
|
public static final String BLE_API_COMMAND_WRITE = "nodomain.freeyourgadget.gadgetbridge.ble_api.commands.CHARACTERISTIC_WRITE";
|
public static final String BLE_API_EVENT_CHARACTERISTIC_CHANGED = "nodomain.freeyourgadget.gadgetbridge.ble_api.events.CHARACTERISTIC_CHANGED";
|
|
|
BroadcastReceiver intentApiReceiver = new BroadcastReceiver() {
|
@Override
|
public void onReceive(Context context, Intent intent) {
|
String action = intent.getAction();
|
|
boolean isWrite = BLE_API_COMMAND_WRITE.equals(action);
|
|
boolean isRead = BLE_API_COMMAND_READ.equals(action);
|
|
if((!isWrite) && (!isRead)) {
|
return;
|
}
|
|
if (!concernsThisDevice(intent)) {
|
return;
|
}
|
|
if(!getDevice().getState().equalsOrHigherThan(GBDevice.State.INITIALIZED)) {
|
logger.error(String.format("BLE API: Device %s not initialized.", getDevice()));
|
return;
|
}
|
|
String uuid = intent.getStringExtra("EXTRA_CHARACTERISTIC_UUID");
|
if (StringUtils.isNullOrEmpty(uuid)) {
|
logger.error("BLE API: missing EXTRA_CHARACTERISTIC_UUID");
|
return;
|
}
|
|
String hexData = intent.getStringExtra("EXTRA_PAYLOAD");
|
if (isWrite && (hexData == null)) {
|
logger.error("BLE API: missing EXTRA_PAYLOAD");
|
return;
|
}
|
|
BluetoothGattCharacteristic characteristic = characteristics.get(uuid);
|
|
if(characteristic == null) {
|
logger.error("Characteristic {} not found", uuid);
|
return;
|
}
|
|
if(isWrite) {
|
new TransactionBuilder("BLE API write")
|
.write(characteristic, StringUtils.hexToBytes(hexData))
|
.queue(getQueue());
|
return;
|
}
|
|
if(isRead) {
|
new TransactionBuilder("BLE API read")
|
.read(characteristic)
|
.queue(getQueue());
|
return;
|
}
|
}
|
};
|
|
public static boolean isEnabled(GBDevice device) {
|
Prefs devicePrefs = GBApplication.getDevicePrefs(device);
|
|
boolean intentApiEnabledReadWrite = devicePrefs.getBoolean(PREFS_KEY_DEVICE_BLE_API_DEVICE_READ_WRITE, false);
|
boolean intentApiEnabledNotifications = devicePrefs.getBoolean(PREFS_KEY_DEVICE_BLE_API_DEVICE_NOTIFY, false);
|
boolean intentApiEnabledDeviceState = devicePrefs.getBoolean(PREFS_KEY_DEVICE_BLE_API_DEVICE_STATE, false);
|
|
return intentApiEnabledReadWrite | intentApiEnabledNotifications | intentApiEnabledDeviceState;
|
}
|
|
public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
|
if(!intentApiEnabledNotifications) {
|
return;
|
}
|
Intent intent = getBleApiIntent(BLE_API_EVENT_CHARACTERISTIC_CHANGED);
|
if(!StringUtils.isNullOrEmpty(intentApiPackage)) {
|
intent.setPackage(intentApiPackage);
|
}
|
intent.putExtra("EXTRA_CHARACTERISTIC", characteristic.getUuid().toString());
|
intent.putExtra("EXTRA_PAYLOAD", StringUtils.bytesToHex(characteristic.getValue()));
|
|
getContext().sendBroadcast(intent);
|
}
|
|
public void initializeDevice(TransactionBuilder builder) {
|
if(intentApiEnabledNotifications) {
|
for (BluetoothGattCharacteristic characteristic : characteristics.values()) {
|
builder.notify(characteristic, true);
|
}
|
}
|
}
|
|
public void dispose() {
|
registerBleApiCharacteristicReceivers(false);
|
}
|
|
public void onSendConfiguration(String config) {
|
if(StringUtils.isNullOrEmpty(config)) {
|
return;
|
}
|
if(config.startsWith("prefs_device_ble_api_")) {
|
// could subscribe here, but there is more setup to do than that...
|
// handleBLEApiPrefs();
|
GB.toast(
|
getContext().getString(R.string.toast_setting_requires_reconnect),
|
Toast.LENGTH_SHORT,
|
GB.INFO
|
);
|
};
|
}
|
|
public Context getContext() {
|
return context;
|
}
|
|
public BtLEQueue getQueue() {
|
return queue;
|
}
|
|
public void setQueue(BtLEQueue queue) {
|
this.queue = queue;
|
}
|
|
private void registerBleApiCharacteristicReceivers(boolean enable){
|
if(enable == intentApiCharacteristicReceiverRegistered) {
|
return;
|
}
|
|
if(enable){
|
IntentFilter filter = new IntentFilter();
|
filter.addAction(BLE_API_COMMAND_READ);
|
filter.addAction(BLE_API_COMMAND_WRITE);
|
|
ContextCompat.registerReceiver(
|
getContext(),
|
intentApiReceiver,
|
filter,
|
ContextCompat.RECEIVER_EXPORTED
|
);
|
}else{
|
getContext().unregisterReceiver(intentApiReceiver);
|
}
|
intentApiCharacteristicReceiverRegistered = intentApiEnabledReadWrite;
|
}
|
|
public void addService(BluetoothGattService service) {
|
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
|
this.characteristics.put(characteristic.getUuid().toString(), characteristic);
|
}
|
}
|
|
public void handleBLEApiPrefs(){
|
Prefs devicePrefs = GBApplication.getDevicePrefs(getDevice());
|
this.intentApiEnabledReadWrite = devicePrefs.getBoolean(PREFS_KEY_DEVICE_BLE_API_DEVICE_READ_WRITE, false);
|
this.intentApiEnabledNotifications = devicePrefs.getBoolean(PREFS_KEY_DEVICE_BLE_API_DEVICE_NOTIFY, false);
|
this.intentApiEnabledDeviceState = devicePrefs.getBoolean(PREFS_KEY_DEVICE_BLE_API_DEVICE_STATE, false);
|
this.intentApiPackage = devicePrefs.getString(PREFS_KEY_DEVICE_BLE_API_PACKAGE, "");
|
|
registerBleApiCharacteristicReceivers(this.intentApiEnabledReadWrite);
|
}
|
|
public static Intent getBleApiIntent(String deviceAddress, String action) {
|
Intent updateIntent = new Intent(action);
|
updateIntent.putExtra("EXTRA_DEVICE_ADDRESS", deviceAddress);
|
return updateIntent;
|
}
|
|
private Intent getBleApiIntent(String action) {
|
return getBleApiIntent(getDevice().getAddress(), action);
|
}
|
|
public BleIntentApi(Context context, GBDevice device) {
|
this.context = context;
|
this.device = device;
|
|
this.logger = LoggerFactory.getLogger(BleIntentApi.class);
|
}
|
|
public GBDevice getDevice() {
|
return device;
|
}
|
|
private boolean concernsThisDevice(Intent intent) {
|
String deviceAddress = intent.getStringExtra("EXTRA_DEVICE_ADDRESS");
|
if (StringUtils.isNullOrEmpty(deviceAddress)) {
|
logger.error("BLE API: missing EXTRA_DEVICE_ADDRESS");
|
return false;
|
}
|
return deviceAddress.equalsIgnoreCase(getDevice().getAddress());
|
}
|
}
|