diff options
Diffstat (limited to 'app/src/main/java/io/trygvis/soilmoisture')
6 files changed, 396 insertions, 112 deletions
diff --git a/app/src/main/java/io/trygvis/soilmoisture/Constants.java b/app/src/main/java/io/trygvis/soilmoisture/Constants.java deleted file mode 100644 index 0b891b7..0000000 --- a/app/src/main/java/io/trygvis/soilmoisture/Constants.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.trygvis.soilmoisture; - -import java.util.UUID; - -public interface Constants { - String TRYGVIS_IO_BASE_UUID = "32D0xxxx-035D-59C5-70D3-BC8E4A1FD83F"; - UUID TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0001")); - UUID TRYGVIS_IO_GAUGE_DATA_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0002")); - UUID TRYGVIS_IO_GAUGE_CTRL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0004")); - UUID TRYGVIS_IO_LED_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0003")); - - UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); -} diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSmDevicesManager.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSmDevicesManager.java new file mode 100644 index 0000000..301bfc7 --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSmDevicesManager.java @@ -0,0 +1,224 @@ +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<SmDevice> 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<BtService<SmDevice>>) 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<SmDevice> 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<SmDevice> getDevices(Comparator<SmDevice> comparator) { + Set<SmDevice> devices = new TreeSet<>(comparator); + for (BtDevice<SmDevice> 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<SmDevice> 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; + } + } +} diff --git a/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java b/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java index faa95e1..ff70b0f 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java +++ b/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java @@ -11,12 +11,6 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread thread, Throwable ex) { Log.e(TAG, "Uncaught", ex); - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - if (ex instanceof Error) { - throw (Error) ex; - } - throw new RuntimeException(ex); + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(thread, ex); } } diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java index 18c96b8..6adc96c 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java +++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java @@ -2,8 +2,7 @@ package io.trygvis.soilmoisture; import android.app.ActionBar; import android.app.ListActivity; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattService; +import android.app.ProgressDialog; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; @@ -20,20 +19,19 @@ import android.widget.Button; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; import com.crashlytics.android.Crashlytics; +import java.util.ArrayList; +import java.util.List; + import io.fabric.sdk.android.Fabric; -import io.trygvis.android.bt.BtActionExecutor; +import io.trygvis.android.LocalBinder; import io.trygvis.android.bt.BtActivitySupport; -import io.trygvis.android.bt.BtDevice; -import io.trygvis.android.bt.BtDeviceListener; -import io.trygvis.android.bt.BtService; -import io.trygvis.android.bt.DefaultBtService; -import io.trygvis.bluetooth.TrygvisIoUuids; -import static io.trygvis.android.bt.BtService.BtServiceListener; import static io.trygvis.soilmoisture.ExceptionHandler.EXCEPTION_HANDLER; +import static io.trygvis.soilmoisture.SmDevicesManager.SmDeviceListener; import static java.lang.String.valueOf; public class MainActivity extends ListActivity { @@ -44,11 +42,14 @@ public class MainActivity extends ListActivity { private static final int REQUEST_ENABLE_BT = 1; private final BtActivitySupport btActivitySupport = new BtActivitySupport(this, REQUEST_ENABLE_BT); + private final SmDeviceListener serviceListener = new MySmDeviceListener(); + private final MainActivity context = this; private DeviceListAdapter deviceList; - private BtService<SmDevice> btService; - private ServiceConnection serviceConnection; + private SmDevicesManager smDevicesManager; + private ProgressDialog initializing; + private boolean ready; @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,26 +72,23 @@ public class MainActivity extends ListActivity { @SuppressWarnings("unchecked") @Override public void onServiceConnected(ComponentName componentName, IBinder service) { - btService = ((BtService.LocalBinder<SmDevice>) service).getService(); - if (!btService.initialize(serviceListener, SmDevice::new)) { - finish(); - } - - deviceList = new DeviceListAdapter(); - deviceList.notifyDataSetChanged(); - setListAdapter(deviceList); - - startScan(); + Log.i(TAG, "onServiceConnected"); + smDevicesManager = ((LocalBinder<SmDevicesManager>) service).getService(); + registerReceiver(serviceListener, SmDeviceListener.INTENT_FILTER); } @Override public void onServiceDisconnected(ComponentName componentName) { - btService = null; + Log.i(TAG, "onServiceDisconnected"); + smDevicesManager = null; stopScan(); } }; - bindService(new Intent(this, DefaultBtService.class), serviceConnection, BIND_AUTO_CREATE); + bindService(new Intent(this, DefaultSmDevicesManager.class), serviceConnection, BIND_AUTO_CREATE); + + initializing = ProgressDialog. + show(this, "Initializing", "Connecting to Bluetooth system.", true); } @Override @@ -114,7 +112,7 @@ public class MainActivity extends ListActivity { return; } - // registerReceiver(btServiceBroadcastReceiver, IntentAction.ALL_FILTER); + registerReceiver(serviceListener, SmDeviceListener.INTENT_FILTER); } @Override @@ -123,7 +121,7 @@ public class MainActivity extends ListActivity { super.onPause(); stopScan(); - // unregisterReceiver(ntServiceBroadcastReceiver); + unregisterReceiver(serviceListener); } @Override @@ -143,14 +141,20 @@ public class MainActivity extends ListActivity { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); - if (!btService.isScanning()) { + if (ready) { + if (!smDevicesManager.isScanning()) { + menu.findItem(R.id.menu_stop).setVisible(false); + menu.findItem(R.id.menu_scan).setVisible(true); + menu.findItem(R.id.menu_refresh).setActionView(null); + } else { + menu.findItem(R.id.menu_stop).setVisible(true); + menu.findItem(R.id.menu_scan).setVisible(false); + menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress); + } + } else { menu.findItem(R.id.menu_stop).setVisible(false); menu.findItem(R.id.menu_scan).setVisible(true); menu.findItem(R.id.menu_refresh).setActionView(null); - } else { - menu.findItem(R.id.menu_stop).setVisible(true); - menu.findItem(R.id.menu_scan).setVisible(false); - menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress); } return true; } @@ -171,12 +175,12 @@ public class MainActivity extends ListActivity { } private void startScan() { - btService.startScanning(SCAN_PERIOD); + smDevicesManager.startScanning(SCAN_PERIOD); } private void stopScan() { - if (btService != null) { - btService.stopScanning(); + if (smDevicesManager != null) { + smDevicesManager.stopScanning(); } } @@ -184,54 +188,9 @@ public class MainActivity extends ListActivity { protected void onListItemClick(ListView l, View v, int position, long id) { stopScan(); - BtDevice<SmDevice> state = btService.getDevices().get(position); - - BtActionExecutor executor = new BtActionExecutor(). - onConnectionStateChange((gatt, newState) -> { - if (newState == BluetoothGatt.STATE_CONNECTED) { - Intent intent = new Intent(this, SoilActivity.class); - startActivity(intent); - return true; - } - return false; - }). - onServicesDiscovered(gatt -> false); - - state.connect(executor); +// SmDevice state = smDevicesManager.getDevices(SmDevice.deviceComparator).get(position); } - BtServiceListener<SmDevice> serviceListener = new BtServiceListener<SmDevice>() { - @Override - public void onScanStarted() { - invalidateOptionsMenu(); - } - - @Override - public void onNewDevice(BtDevice<SmDevice> device) { - device.addListener(deviceListener); - - BtActionExecutor executor = new BtActionExecutor(). - onConnectionStateChange((gatt, newState) -> gatt.discoverServices()). - onServicesDiscovered(gatt -> { - BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); - - boolean useful = service != null; - device.getTag().setIsUseful(useful); - runOnUiThread(deviceList::notifyDataSetChanged); - return useful; - }); - device.connect(executor); - } - - @Override - public void onScanStopped() { - invalidateOptionsMenu(); - } - }; - - BtDeviceListener deviceListener = new BtDeviceListener() { - }; - // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- @@ -253,16 +212,17 @@ public class MainActivity extends ListActivity { } private class DeviceListAdapter extends BaseAdapter { + private List<SmDevice> devices = new ArrayList<>(); private LayoutInflater inflater = MainActivity.this.getLayoutInflater(); @Override public int getCount() { - return btService.getDevices().size(); + return devices.size(); } @Override - public Object getItem(int i) { - return btService.getDevices().get(i); + public SmDevice getItem(int i) { + return devices.get(i); } @Override @@ -283,18 +243,16 @@ public class MainActivity extends ListActivity { item = (DeviceListItem) view.getTag(); } - BtDevice<SmDevice> btDevice = btService.getDevices().get(i); - if (btDevice.getName() != null && btDevice.getName().length() > 0) { - item.deviceName.setText(btDevice.getName()); + SmDevice smDevice = getItem(i); + if (smDevice.getName() != null) { + item.deviceName.setText(smDevice.getName()); } else { item.deviceName.setText(R.string.unknown_device); } - item.deviceAddress.setText(btDevice.getAddress()); + item.deviceAddress.setText(smDevice.getBtDevice().getAddress()); item.rssi.setText(getText(R.string.rssi) + ": " + - (btDevice.getRssi() != 0 ? valueOf(btDevice.getRssi()) : getText(R.string.unknown))); - - SmDevice smDevice = btDevice.getTag(); + (smDevice.getBtDevice().getRssi() != 0 ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown))); boolean useful = smDevice.isUseful(); item.spinner.setVisibility(useful ? View.GONE : View.VISIBLE); @@ -304,4 +262,46 @@ public class MainActivity extends ListActivity { return view; } } + + private class MySmDeviceListener extends SmDeviceListener { + @Override + public void onReady(boolean ok) { + if (!ok) { + Toast.makeText(context, + "Could not initialize services.", + Toast.LENGTH_SHORT). + show(); + + finish(); + } else { + ready = true; + deviceList = new DeviceListAdapter(); + setListAdapter(deviceList); + + deviceList.notifyDataSetChanged(); + startScan(); + + initializing.dismiss(); + } + } + + @Override + public void onScanStarted() { + invalidateOptionsMenu(); + } + + @Override + public void onScanStopped() { + invalidateOptionsMenu(); + } + + @Override + public void onNewDevice(String address) { + SmDevice device = smDevicesManager.getDevice(address); + deviceList.devices.add(device); + deviceList.notifyDataSetInvalidated(); + + Log.i(TAG, "deviceList.devices.size() = " + deviceList.devices.size()); + } + } } diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java index 6bc522d..1ed7ecb 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java @@ -2,14 +2,35 @@ package io.trygvis.soilmoisture; import android.util.Log; -class SmDevice { +import java.util.Comparator; + +import io.trygvis.android.bt.BtDevice; + +class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> { + public static final Comparator<SmDevice> deviceComparator = (a, b) -> a.getBtDevice().getAddress().compareTo(b.getBtDevice().getAddress()); + private final static String TAG = SmDevice.class.getSimpleName(); - public SmDevice() { + private final BtDevice<SmDevice> btDevice; + + private String name; + + private Boolean isUseful; + + public SmDevice(BtDevice<SmDevice> btDevice) { + this.btDevice = btDevice; Log.i(TAG, "new device"); + + name = btDevice.getName(); + + if (name != null && name.trim().length() == 0) { + name = null; + } } - private Boolean isUseful; + public BtDevice<SmDevice> getBtDevice() { + return btDevice; + } public boolean isUseful() { return isUseful != null && isUseful; @@ -19,8 +40,16 @@ class SmDevice { return isUseful; } + public boolean isProbed() { + return isUseful != null; + } + public void setIsUseful(Boolean isUseful) { Log.i(TAG, "useful=" + isUseful); this.isUseful = isUseful; } + + public String getName() { + return name; + } } diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevicesManager.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevicesManager.java new file mode 100644 index 0000000..531061a --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevicesManager.java @@ -0,0 +1,50 @@ +package io.trygvis.soilmoisture; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import java.util.Comparator; +import java.util.List; + +import io.trygvis.android.bt.BtDevice; + +public interface SmDevicesManager { + List<SmDevice> getDevices(Comparator<SmDevice> comparator); + + SmDevice getDevice(String address); + + boolean isScanning(); + + boolean startScanning(long scanPeriod); + + void stopScanning(); + + public abstract static class SmDeviceListener extends BroadcastReceiver { + + public static final String INTENT_NAME = SmDeviceListener.class.getName(); + + public static final IntentFilter INTENT_FILTER = new IntentFilter(INTENT_NAME); + + public void onReceive(Context context, Intent intent) { + if (!intent.getAction().equals(INTENT_NAME)) { + return; + } + + DefaultSmDevicesManager.dispatchEvent(intent, this); + } + + public void onReady(boolean ok) { + } + + public void onScanStarted() { + } + + public void onNewDevice(String address) { + } + + public void onScanStopped() { + } + } +} |