A few improvements to discovery

- display the right icon for found device candidates
- scan for specific LE services
This commit is contained in:
cpfeiffer 2016-11-27 01:09:20 +01:00
parent b2e86ca061
commit b9ff2cd468
11 changed files with 134 additions and 27 deletions

View File

@ -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<ScanFilter> getScanFilters() {
List<ScanFilter> allFilters = new ArrayList<>();
for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) {
allFilters.addAll(coordinator.createBLEScanFilters());
}
return allFilters;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)

View File

@ -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<? extends ScanFilter> createBLEScanFilters() {
return Collections.emptyList();
}
@Override
public GBDevice createDevice(GBDeviceCandidate candidate) {
return new GBDevice(candidate.getDevice().getAddress(), candidate.getName(), getDeviceType());

View File

@ -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<? extends ScanFilter> createBLEScanFilters();
GBDevice createDevice(GBDeviceCandidate candidate);
/**
@ -154,5 +180,4 @@ public interface DeviceCoordinator {
* @return
*/
Class<? extends Activity> getAppsManagementActivity();
}

View File

@ -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

View File

@ -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<? extends ScanFilter> 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;
}

View File

@ -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<? extends ScanFilter> 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

View File

@ -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<? extends Activity> getPrimaryActivity() {
return AppManagerActivity.class;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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) {

View File

@ -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<DeviceCoordinator> 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<Device> 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);
}
}