package io.trygvis.soilmoisture; import android.app.Service; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.TreeSet; import io.trygvis.android.LocalBinder; import io.trygvis.android.bt.BtActionExecutor; import io.trygvis.android.bt.BtDevice; import io.trygvis.android.bt.BtService; import io.trygvis.android.bt.DefaultBtService; import io.trygvis.bluetooth.TrygvisIoUuids; import static io.trygvis.android.bt.BtService.BtServiceListenerBroadcastReceiver; public class DefaultSmDevicesManager extends Service implements SmDevicesManager { private final static String TAG = DefaultSmDevicesManager.class.getSimpleName(); private final IBinder binder = new LocalBinder<>(this); @SuppressWarnings("UnusedDeclaration") private final DefaultSmDevicesManager context = DefaultSmDevicesManager.this; private ServiceConnection serviceConnection; private BtService btService; @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onCreate() { Log.i(TAG, "onCreate"); serviceConnection = new ServiceConnection() { @SuppressWarnings("unchecked") @Override public void onServiceConnected(ComponentName componentName, IBinder service) { btService = ((LocalBinder>) service).getService(); boolean ok = btService.initialize(DefaultSmDevicesManager.this::onNewDevice); sendBroadcast(createReady(ok)); } @Override public void onServiceDisconnected(ComponentName componentName) { btService = null; } }; bindService(new Intent(this, DefaultBtService.class), serviceConnection, BIND_AUTO_CREATE); registerReceiver(btServiceListener, BtServiceListenerBroadcastReceiver.INTENT_FILTER); } @Override public void onDestroy() { unregisterReceiver(btServiceListener); Log.i(TAG, "onDestroy" + btService); if (serviceConnection != null) { unbindService(serviceConnection); } } private final BtServiceListenerBroadcastReceiver btServiceListener = new BtServiceListenerBroadcastReceiver() { @Override public void onScanStarted() { sendBroadcast(createScanStarted()); } @Override public void onScanStopped() { sendBroadcast(createScanStopped()); } @Override public void onNewDevice(String address) { BtDevice btDevice = btService.getDevice(address); SmDevice smDevice = btDevice.getTag(); if (!smDevice.isProbed()) { Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName()); BtActionExecutor executor = new BtActionExecutor(). onConnectionStateChange((gatt, newState) -> { //noinspection SimplifiableIfStatement if (newState == BluetoothGatt.STATE_CONNECTED) { Log.i(TAG, "Connected to " + address + ", getting services"); return gatt.discoverServices(); } Log.i(TAG, "Could not connect to to " + address); smDevice.setIsUseful(false); return false; }). onServicesDiscovered(gatt -> { Log.i(TAG, "Services discovered"); BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); if (service == null) { smDevice.setIsUseful(false); return false; } BluetoothGattCharacteristic characteristic = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE); if (characteristic == null) { smDevice.setIsUseful(false); return false; } smDevice.setIsUseful(true); sendBroadcast(createNewDevice(address)); gatt.disconnect(); return true; }); btDevice.connect(executor); } else { sendBroadcast(createNewDevice(address)); } } }; // ----------------------------------------------------------------------- // SmDevicesManager Implementation // ----------------------------------------------------------------------- @Override public List getDevices(Comparator comparator) { Set devices = new TreeSet<>(comparator); for (BtDevice btDevice : btService.getDevices()) { devices.add(btDevice.getTag()); } return new ArrayList<>(devices); } @Override public SmDevice getDevice(String address) { return btService.getTag(address); } @Override public boolean isScanning() { return btService.isScanning(); } @Override public boolean startScanning(long scanPeriod) { return btService.startScanning(scanPeriod); } @Override public void stopScanning() { btService.stopScanning(); } // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- private SmDevice onNewDevice(BtDevice btDevice) { return new SmDevice(btDevice); } private Intent createReady(boolean success) { return new Intent(SmDeviceListener.INTENT_NAME). putExtra("event", "ready"). putExtra("success", success); } private Intent createScanStarted() { return new Intent(SmDeviceListener.INTENT_NAME). putExtra("event", "scanStarted"); } private Intent createScanStopped() { return new Intent(SmDeviceListener.INTENT_NAME). putExtra("event", "scanStopped"); } private Intent createNewDevice(String address) { return new Intent(SmDeviceListener.INTENT_NAME). putExtra("event", "newDevice"). putExtra("address", address); } public static void dispatchEvent(Intent intent, SmDeviceListener listener) { String event = intent.getStringExtra("event"); Log.i(TAG, "Dispatching event " + intent.getAction() + "/" + event); switch (event) { case "ready": listener.onReady(intent.getBooleanExtra("success", false)); break; case "newDevice": listener.onNewDevice(intent.getStringExtra("address")); break; case "scanStarted": listener.onScanStarted(); break; case "scanStopped": listener.onScanStopped(); break; default: break; } } }