diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index 6dd5cbc9..bb07ad63 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -7,6 +7,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.BroadcastReceiver; @@ -32,6 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; @@ -39,9 +41,11 @@ import nodomain.freeyourgadget.gadgetbridge.adapter.DeviceCandidateAdapter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES; import static android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY; import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY; @@ -257,7 +261,9 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC } GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi); - if (DeviceHelper.getInstance().isSupported(candidate)) { + DeviceType deviceType = DeviceHelper.getInstance().getSupportedType(candidate); + if (deviceType.isSupported()) { + candidate.setDeviceType(deviceType); int index = deviceCandidates.indexOf(candidate); if (index >= 0) { deviceCandidates.set(index, candidate); // replace @@ -410,7 +416,15 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC LOG.info("Start New BTLE Discovery"); handler.removeMessages(0, stopRunnable); handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION); - adapter.getBluetoothLeScanner().startScan(null, getScanSettings(), getScanCallback()); + adapter.getBluetoothLeScanner().startScan(getScanFilters(), getScanSettings(), getScanCallback()); + } + + private List getScanFilters() { + List allFilters = new ArrayList<>(); + for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) { + allFilters.addAll(coordinator.createBLEScanFilters()); + } + return allFilters; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index c403e6ec..46a65e09 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -2,11 +2,15 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; @@ -21,12 +25,22 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviceCoordinator.class); + @Override + public final boolean supports(GBDeviceCandidate candidate) { + return getSupportedType(candidate).isSupported(); + } @Override public boolean supports(GBDevice device) { return getDeviceType().equals(device.getType()); } + @NonNull + @Override + public Collection createBLEScanFilters() { + return Collections.emptyList(); + } + @Override public GBDevice createDevice(GBDeviceCandidate candidate) { return new GBDevice(candidate.getDevice().getAddress(), candidate.getName(), getDeviceType()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index c2eace40..43f0e8d4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -1,8 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices; +import android.annotation.TargetApi; import android.app.Activity; +import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; +import android.os.Build; +import android.support.annotation.NonNull; + +import java.util.Collection; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; @@ -24,7 +30,18 @@ public interface DeviceCoordinator { String EXTRA_DEVICE_MAC_ADDRESS = "nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate.EXTRA_MAC_ADDRESS"; /** - * Checks whether this candidate handles the given candidate. + * Checks whether this coordinator handles the given candidate. + * Returns the supported device type for the given candidate or + * DeviceType.UNKNOWN + * + * @param candidate + * @return the supported device type for the given candidate. + */ + @NonNull + DeviceType getSupportedType(GBDeviceCandidate candidate); + + /** + * Checks whether this coordinator handles the given candidate. * * @param candidate * @return true if this coordinator handles the given candidate. @@ -39,6 +56,15 @@ public interface DeviceCoordinator { */ boolean supports(GBDevice device); + /** + * Returns a list of scan filters that shall be used to discover devices supported + * by this coordinator. + * @return the list of scan filters, may be empty + */ + @NonNull + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + Collection createBLEScanFilters(); + GBDevice createDevice(GBDeviceCandidate candidate); /** @@ -154,5 +180,4 @@ public interface DeviceCoordinator { * @return */ Class getAppsManagementActivity(); - } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index d7379c45..6a50ba0f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -82,8 +82,8 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { } @Override - public boolean supports(GBDeviceCandidate candidate) { - return false; + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + return DeviceType.UNKNOWN; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 84bc5d8a..5ce4eddf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -1,12 +1,20 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; +import android.annotation.TargetApi; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; @@ -23,19 +31,30 @@ public class MiBand2Coordinator extends MiBandCoordinator { return DeviceType.MIBAND2; } + @NonNull @Override - public boolean supports(GBDeviceCandidate candidate) { - // and a heuristic + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid mi2Service = new ParcelUuid(MiBandService.UUID_SERVICE_MIBAND2_SERVICE); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mi2Service).build(); + return Collections.singletonList(filter); + } + + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + // and a heuristic for now try { BluetoothDevice device = candidate.getDevice(); if (isHealthWearable(device)) { String name = device.getName(); - return name != null && name.equalsIgnoreCase(MiBandConst.MI_BAND2_NAME); + if (name != null && name.equalsIgnoreCase(MiBandConst.MI_BAND2_NAME)) { + return DeviceType.MIBAND2; + } } } catch (Exception ex) { LOG.error("unable to check device support", ex); } - return false; + return DeviceType.UNKNOWN; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index eee28a3e..c44a3321 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -1,13 +1,21 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; +import android.annotation.TargetApi; import android.app.Activity; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; import android.content.Context; import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Collections; + import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; @@ -32,28 +40,39 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { public MiBandCoordinator() { } + @NonNull @Override - public boolean supports(GBDeviceCandidate candidate) { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid mi1Service = new ParcelUuid(MiBandService.UUID_SERVICE_MIBAND_SERVICE); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mi1Service).build(); + return Collections.singletonList(filter); + } + + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { String macAddress = candidate.getMacAddress().toUpperCase(); if (macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1_1A) || macAddress.startsWith(MiBandService.MAC_ADDRESS_FILTER_1S)) { - return true; + return DeviceType.MIBAND; } if (candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND_SERVICE) && !candidate.supportsService(MiBandService.UUID_SERVICE_MIBAND2_SERVICE)) { - return true; + return DeviceType.MIBAND; } // and a heuristic try { BluetoothDevice device = candidate.getDevice(); if (isHealthWearable(device)) { String name = device.getName(); - return name != null && name.toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase()); + if (name != null && name.toUpperCase().startsWith(MiBandConst.MI_GENERAL_NAME_PREFIX.toUpperCase())) { + return DeviceType.MIBAND; + } } } catch (Exception ex) { LOG.error("unable to check device support", ex); } - return false; + return DeviceType.UNKNOWN; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index 34152eae..5953113d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -30,9 +30,12 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { } @Override - public boolean supports(GBDeviceCandidate candidate) { + public DeviceType getSupportedType(GBDeviceCandidate candidate) { String name = candidate.getDevice().getName(); - return name != null && name.startsWith("Pebble"); + if (name != null && name.startsWith("Pebble")) { + return DeviceType.PEBBLE; + } + return DeviceType.UNKNOWN; } @Override @@ -45,6 +48,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { return PebblePairingActivity.class; } + @Override public Class getPrimaryActivity() { return AppManagerActivity.class; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java index d36b0c40..8f71f234 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java @@ -20,9 +20,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; public class VibratissimoCoordinator extends AbstractDeviceCoordinator { @Override - public boolean supports(GBDeviceCandidate candidate) { + public DeviceType getSupportedType(GBDeviceCandidate candidate) { String name = candidate.getDevice().getName(); - return name != null && name.startsWith("Vibratissimo"); + if (name != null && name.startsWith("Vibratissimo")) { + return DeviceType.VIBRATISSIMO; + } + return DeviceType.UNKNOWN; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java index 7820bc2a..bb9d7fae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -4,6 +4,7 @@ import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +39,7 @@ public class GBDeviceCandidate implements Parcelable { rssi = (short) in.readInt(); deviceType = DeviceType.valueOf(in.readString()); - if (device == null || deviceType == null) { + if (device == null) { throw new IllegalStateException("Unable to read state from Parcel"); } } @@ -54,6 +55,10 @@ public class GBDeviceCandidate implements Parcelable { return device; } + public void setDeviceType(DeviceType type) { + deviceType = type; + } + public DeviceType getDeviceType() { return deviceType; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 43223637..2a1c859e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -24,6 +24,10 @@ public enum DeviceType { return key; } + public boolean isSupported() { + return this != UNKNOWN; + } + public static DeviceType fromKey(int key) { for (DeviceType type : values()) { if (type.key == key) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index a14aff80..19078571 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -8,7 +8,6 @@ import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; @@ -47,16 +46,17 @@ public class DeviceHelper { // lazily created private List coordinators; - public boolean isSupported(GBDeviceCandidate candidate) { + public DeviceType getSupportedType(GBDeviceCandidate candidate) { for (DeviceCoordinator coordinator : getAllCoordinators()) { - if (coordinator.supports(candidate)) { - return true; + DeviceType deviceType = coordinator.getSupportedType(candidate); + if (deviceType.isSupported()) { + return deviceType; } } - return false; + return DeviceType.UNKNOWN; } - public boolean isSupported(GBDevice device) { + public boolean getSupportedType(GBDevice device) { for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(device)) { return true; @@ -174,7 +174,7 @@ public class DeviceHelper { List activeDevices = DBHelper.getActiveDevices(lockHandler.getDaoSession()); for (Device dbDevice : activeDevices) { GBDevice gbDevice = toGBDevice(dbDevice); - if (gbDevice != null && DeviceHelper.getInstance().isSupported(gbDevice)) { + if (gbDevice != null && DeviceHelper.getInstance().getSupportedType(gbDevice)) { result.add(gbDevice); } }