diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fe0f9b349..f0f5e3113 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -431,6 +431,12 @@ android:name=".devices.pinetime.PineTimeDFUService" android:label="PineTime Nordic DFU service" /> + + + { + GB.toast("Please restart GB in order to take effect.", Toast.LENGTH_LONG, GB.INFO); + return true; + }); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 2b254f334..367695c3e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -431,34 +431,15 @@ public class GBDevice implements Parcelable { * Set simple to true to get this behavior. */ private String getStateString(boolean simple) { - switch (mState) { - case NOT_CONNECTED: - return GBApplication.getContext().getString(R.string.not_connected); - case WAITING_FOR_RECONNECT: - return GBApplication.getContext().getString(R.string.waiting_for_reconnect); - case CONNECTING: - return GBApplication.getContext().getString(R.string.connecting); - case CONNECTED: - if (simple) { - return GBApplication.getContext().getString(R.string.connecting); - } - return GBApplication.getContext().getString(R.string.connected); - case INITIALIZING: - if (simple) { - return GBApplication.getContext().getString(R.string.connecting); - } - return GBApplication.getContext().getString(R.string.initializing); - case AUTHENTICATION_REQUIRED: - return GBApplication.getContext().getString(R.string.authentication_required); - case AUTHENTICATING: - return GBApplication.getContext().getString(R.string.authenticating); - case INITIALIZED: - if (simple) { - return GBApplication.getContext().getString(R.string.connected); - } - return GBApplication.getContext().getString(R.string.initialized); - } - return GBApplication.getContext().getString(R.string.unknown_state); + try{ + // TODO: not sure if this is really neccessary... + if(simple){ + return GBApplication.getContext().getString(mState.getSimpleStringId()); + } + return GBApplication.getContext().getString(mState.getStringId()); + }catch (Exception e){} + + return "Unknown state"; } /** @@ -744,20 +725,42 @@ public class GBDevice implements Parcelable { } public enum State { - // Note: the order is important! - NOT_CONNECTED, - WAITING_FOR_RECONNECT, - CONNECTING, - CONNECTED, - INITIALIZING, - AUTHENTICATION_REQUIRED, // some kind of pairing is required by the device - AUTHENTICATING, // some kind of pairing is requested by the device + NOT_CONNECTED(R.string.not_connected), + WAITING_FOR_RECONNECT(R.string.waiting_for_reconnect), + WAITING_FOR_SCAN(R.string.device_state_waiting_scan), + CONNECTING(R.string.connecting), + CONNECTED(R.string.connected, R.string.connecting), + INITIALIZING(R.string.initializing, R.string.connecting), + AUTHENTICATION_REQUIRED(R.string.authentication_required), // some kind of pairing is required by the device + AUTHENTICATING(R.string.authenticating), // some kind of pairing is requested by the device /** * Means that the device is connected AND all the necessary initialization steps * have been performed. At the very least, this means that basic information like * device name, firmware version, hardware revision (as applicable) is available * in the GBDevice. */ - INITIALIZED, + INITIALIZED(R.string.initialized, R.string.connected); + + + private int stringId, simpleStringId = -1; + + private State(int stringId, int simpleStringId) { + this.stringId = stringId; + this.simpleStringId = simpleStringId; + } + + private State(int stringId) { + this.stringId = stringId; + this.simpleStringId = -1; + } + + public int getStringId() { + return stringId; + } + + public int getSimpleStringId() { + if(simpleStringId == -1) return stringId; + return simpleStringId; + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index f701ce681..51d00f795 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -127,7 +127,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { protected GBDevice gbDevice; private BluetoothAdapter btAdapter; private Context context; - private boolean autoReconnect; + private boolean autoReconnect, scanReconnect; @@ -171,6 +171,16 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { return autoReconnect; } + @Override + public void setScanReconnect(boolean scanReconnect) { + this.scanReconnect = scanReconnect; + } + + @Override + public boolean getScanReconnect(){ + return this.scanReconnect; + } + @Override public boolean getImplicitCallbackModify() { return true; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 6e28e1809..f1322f87a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -97,6 +97,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.Reminder; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.model.WorldClock; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLEScanService; import nodomain.freeyourgadget.gadgetbridge.service.receivers.AutoConnectIntervalReceiver; import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBAutoFetchReceiver; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; @@ -273,6 +274,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private final String ACTION_DEVICE_CONNECTED = "nodomain.freeyourgadget.gadgetbridge.BLUETOOTH_CONNECTED"; private final int NOTIFICATIONS_CACHE_MAX = 10; // maximum amount of notifications to cache per device while disconnected private boolean allowBluetoothIntentApi = false; + private boolean reconnectViaScan = GBPrefs.RECONNECT_SCAN_DEFAULT; private void sendDeviceConnectedBroadcast(String address){ if(!allowBluetoothIntentApi){ @@ -368,6 +370,20 @@ public class DeviceCommunicationService extends Service implements SharedPrefere LOG.debug("device state update reason"); sendDeviceConnectedBroadcast(device.getAddress()); sendCachedNotifications(device); + }else if(BLEScanService.EVENT_DEVICE_FOUND.equals(action)){ + String deviceAddress = intent.getStringExtra(BLEScanService.EXTRA_DEVICE_ADDRESS); + + GBDevice target = GBApplication + .app() + .getDeviceManager() + .getDeviceByAddress(deviceAddress); + + if(target == null){ + Log.e("DeviceCommService", "onReceive: device not found"); + return; + } + + connectToDevice(target); } } } @@ -400,24 +416,20 @@ public class DeviceCommunicationService extends Service implements SharedPrefere setReceiversEnableState(enableReceivers, anyDeviceInitialized, features, devicesWithCalendar); } - @Override - public void onCreate() { - LOG.debug("DeviceCommunicationService is being created"); - super.onCreate(); - LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED)); - mFactory = getDeviceSupportFactory(); + private void registerInternalReceivers(){ + IntentFilter localFilter = new IntentFilter(); + localFilter.addAction(GBDevice.ACTION_DEVICE_CHANGED); + localFilter.addAction(BLEScanService.EVENT_DEVICE_FOUND); + LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, localFilter); + } + private void registerExternalReceivers(){ mBlueToothConnectReceiver = new BluetoothConnectReceiver(this); registerReceiver(mBlueToothConnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)); mAutoConnectInvervalReceiver= new AutoConnectIntervalReceiver(this); registerReceiver(mAutoConnectInvervalReceiver, new IntentFilter("GB_RECONNECT")); - if (hasPrefs()) { - getPrefs().getPreferences().registerOnSharedPreferenceChangeListener(this); - allowBluetoothIntentApi = getPrefs().getBoolean(GBPrefs.PREF_ALLOW_INTENT_API, false); - } - IntentFilter bluetoothCommandFilter = new IntentFilter(); bluetoothCommandFilter.addAction(COMMAND_BLUETOOTH_CONNECT); registerReceiver(bluetoothCommandReceiver, bluetoothCommandFilter); @@ -429,6 +441,22 @@ public class DeviceCommunicationService extends Service implements SharedPrefere registerReceiver(intentApiReceiver, intentApiReceiver.buildFilter()); } + @Override + public void onCreate() { + LOG.debug("DeviceCommunicationService is being created"); + super.onCreate(); + mFactory = getDeviceSupportFactory(); + + registerInternalReceivers(); + registerExternalReceivers(); + + if (hasPrefs()) { + getPrefs().getPreferences().registerOnSharedPreferenceChangeListener(this); + allowBluetoothIntentApi = getPrefs().getBoolean(GBPrefs.PREF_ALLOW_INTENT_API, false); + reconnectViaScan = getGBPrefs().getAutoReconnectByScan(); + } + } + private DeviceSupportFactory getDeviceSupportFactory() { if (DEVICE_SUPPORT_FACTORY != null) { return DEVICE_SUPPORT_FACTORY; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java index 95b5d06d6..a27165535 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java @@ -114,6 +114,10 @@ public interface DeviceSupport extends EventHandler { */ boolean getAutoReconnect(); + void setScanReconnect(boolean enable); + + boolean getScanReconnect(); + /** * Returns whether the gatt callback should be implicitly set to the one on the transaction, * even if it was not set directly on the transaction. If true, the gatt callback will always diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index 70effe0c3..78c17c0ef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -99,6 +99,16 @@ public class ServiceDeviceSupport implements DeviceSupport { return delegate.getAutoReconnect(); } + @Override + public void setScanReconnect(boolean enable) { + delegate.setScanReconnect(enable); + } + + @Override + public boolean getScanReconnect(){ + return delegate.getScanReconnect(); + } + @Override public boolean getImplicitCallbackModify() { return delegate.getImplicitCallbackModify(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java index 7ecd3ce26..834b65fd4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java @@ -78,6 +78,7 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im if (mQueue == null) { mQueue = new BtLEQueue(getBluetoothAdapter(), getDevice(), this, this, getContext(), mSupportedServerServices); mQueue.setAutoReconnect(getAutoReconnect()); + mQueue.setScanReconnect(getScanReconnect()); mQueue.setImplicitGattCallbackModify(getImplicitCallbackModify()); mQueue.setSendWriteRequestResponse(getSendWriteRequestResponse()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLEScanService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLEScanService.java new file mode 100644 index 000000000..db3823adb --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLEScanService.java @@ -0,0 +1,407 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.IBinder; +import android.widget.RemoteViews; + +import androidx.core.app.NotificationCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class BLEScanService extends Service { + public static final String COMMAND_SCAN_DEVICE = "nodomain.freeyourgadget.gadgetbridge.service.ble.scan.command.START_SCAN_FOR_DEVICE"; + public static final String COMMAND_START_SCAN_ALL = "nodomain.freeyourgadget.gadgetbridge.service.ble.scan.command.START_SCAN_ALL"; + public static final String COMMAND_STOP_SCAN_ALL = "nodomain.freeyourgadget.gadgetbridge.service.ble.scan.command.STOP_SCAN_ALL"; + + public static final String EVENT_DEVICE_FOUND = "nodomain.freeyourgadget.gadgetbridge.service.ble.scan.event.DEVICE_FOUND"; + + public static final String EXTRA_DEVICE = "EXTRA_DEVICE"; + public static final String EXTRA_DEVICE_ADDRESS = "EXTRA_DEVICE_ADDRESS"; + + // 5 minutes scan restart interval + private final int DELAY_SCAN_RESTART = 5 * 60 * 1000; + + private LocalBroadcastManager localBroadcastManager; + private NotificationManager notificationManager; + private BluetoothManager bluetoothManager; + private BluetoothLeScanner scanner; + + private Logger LOG = LoggerFactory.getLogger(getClass()); + // private final ArrayList currentFilters = new ArrayList<>(); + + private enum ScanningState { + NOT_SCANNING, + SCANNING_WITHOUT_FILTERS, + SCANNING_WITH_FILTERS; + + public boolean isDoingAnyScan(){ + return ordinal() > NOT_SCANNING.ordinal(); + } + + public boolean shouldDiscardAfterFirstMatch(){ + return this == SCANNING_WITH_FILTERS; + } + }; + private ScanningState currentState = ScanningState.NOT_SCANNING; + + private final ScanCallback scanCallback = new ScanCallback() { + @Override + public void onScanResult(int callbackType, ScanResult result) { + super.onScanResult(callbackType, result); + BluetoothDevice device = result.getDevice(); + + LOG.debug("onScanResult: " + result); + + Intent intent = new Intent(EVENT_DEVICE_FOUND); + intent.putExtra(EXTRA_DEVICE_ADDRESS, device.getAddress()); + localBroadcastManager.sendBroadcast(intent); + + // device found, attempt connection + // stop scanning for device for now + // will restart when connection attempt fails + if(currentState.shouldDiscardAfterFirstMatch()) { + // stopScanningForDevice(device.getAddress()); + } + } + + @Override + public void onScanFailed(int errorCode) { + super.onScanFailed(errorCode); + + LOG.error("onScanFailed: " + errorCode); + + updateNotification("Scan failed: " + errorCode); + } + }; + + @Override + public void onCreate() { + super.onCreate(); + + bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); + scanner = bluetoothManager.getAdapter().getBluetoothLeScanner(); + + localBroadcastManager = LocalBroadcastManager.getInstance(this); + + notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + + registerReceivers(); + + this.startForeground(); + + if(scanner == null){ + updateNotification("Waiting for bluetooth..."); + }else{ + restartScan(true); + } + + // schedule after 5 seconds to fix weird timing of both services + scheduleRestartScan(5000); + } + + private void scheduleRestartScan(){ + scheduleRestartScan(DELAY_SCAN_RESTART); + } + + private void scheduleRestartScan(long millis){ + Handler handler = new Handler(); + handler.postDelayed(() -> { + LOG.debug("restarting scan..."); + try { + restartScan(true); + }catch (Exception e){ + LOG.error("error during scheduled scan restart", e); + } + scheduleRestartScan(); + }, millis); + } + + @Override + public void onDestroy() { + super.onDestroy(); + unregisterReceivers(); + } + + private void updateNotification(boolean isScanning, int scannedDeviceCount){ + notificationManager.notify( + GB.NOTIFICATION_ID_SCAN, + createNotification(isScanning, scannedDeviceCount) + ); + } + + private void updateNotification(String content){ + notificationManager.notify( + GB.NOTIFICATION_ID_SCAN, + createNotification(content, R.drawable.ic_bluetooth) + ); + } + + private Notification createNotification(boolean isScanning, int scannedDevicesCount){ + int icon = R.drawable.ic_bluetooth; + String content = "Not scanning"; + if(isScanning){ + icon = R.drawable.ic_bluetooth_searching; + if(scannedDevicesCount == 1) { + content = String.format("Scanning %d device", scannedDevicesCount); + }else if(scannedDevicesCount > 1){ + content = String.format("Scanning %d devices", scannedDevicesCount); + }else{ + content = "Scanning all devices"; + } + } + + return createNotification(content, icon); + } + + private Notification createNotification(String content, int icon){ + + return new NotificationCompat + .Builder(this, GB.NOTIFICATION_CHANNEL_ID) + .setContentTitle("Scan service") + .setContentText(content) + .setSmallIcon(icon) + .build(); + } + + private void startForeground(){ + Notification serviceNotification = createNotification(false, 0); + + super.startForeground(GB.NOTIFICATION_ID_SCAN, serviceNotification); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if(intent == null){ + return START_STICKY; + } + String action = intent.getAction(); + if(action == null){ + return START_STICKY; + } + switch (action) { + case COMMAND_SCAN_DEVICE: + handleScanDevice(intent); + break; + case COMMAND_START_SCAN_ALL: + handleScanAll(intent); + break; + case COMMAND_STOP_SCAN_ALL: + handleStopScanAll(intent); + break; + default: + return START_STICKY; + } + return START_STICKY; + } + + private void handleStopScanAll(Intent intent){ + restartScan(true); + } + + private void handleScanAll(Intent intent){ + if(currentState != ScanningState.SCANNING_WITHOUT_FILTERS){ + restartScan(false); + } + } + + private void handleScanDevice(Intent intent){ + /* + GBDevice device = intent.getParcelableExtra(EXTRA_DEVICE); + if(device == null){ + return; + } + scanForDevice(device); + */ + restartScan(true); + } + + + /*private boolean isDeviceIncludedInCurrentFilters(GBDevice device){ + for(ScanFilter currentFilter : currentFilters){ + if(device.getAddress().equals(currentFilter.getDeviceAddress())){ + return true; + } + } + return false; + } + */ + + /* + private void stopScanningForDevice(GBDevice device){ + this.stopScanningForDevice(device.getAddress()); + } + */ + + /* + private void stopScanningForDevice(String deviceAddress){ + currentFilters.removeIf(scanFilter -> scanFilter + .getDeviceAddress() + .equals(deviceAddress) + ); + + restartScan(true); + } + */ + + /* + private void scanForDevice(GBDevice device){ + if(isDeviceIncludedInCurrentFilters(device)){ + // already scanning for device + return; + } + ScanFilter deviceFilter = new ScanFilter.Builder() + .setDeviceAddress(device.getAddress()) + .build(); + + currentFilters.add(deviceFilter); + + // restart scan here + restartScan(true); + } + */ + + BroadcastReceiver deviceStateUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + GBDevice.DeviceUpdateSubject subject = + (GBDevice.DeviceUpdateSubject) + intent.getSerializableExtra(GBDevice.EXTRA_UPDATE_SUBJECT); + + if(subject != GBDevice.DeviceUpdateSubject.CONNECTION_STATE){ + return; + } + restartScan(true); + } + }; + + BroadcastReceiver bluetoothStateChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if(intent == null){ + return; + } + final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); + switch(state) { + case BluetoothAdapter.STATE_OFF: + case BluetoothAdapter.STATE_TURNING_OFF: + updateNotification("Waiting for bluetooth..."); + break; + case BluetoothAdapter.STATE_ON: + restartScan(true); + break; + } + } + }; + + private void registerReceivers(){ + localBroadcastManager.registerReceiver( + deviceStateUpdateReceiver, + new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED) + ); + + registerReceiver( + bluetoothStateChangedReceiver, + new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) + ); + } + + private void unregisterReceivers(){ + localBroadcastManager.unregisterReceiver(deviceStateUpdateReceiver); + + unregisterReceiver(bluetoothStateChangedReceiver); + } + + private void restartScan(boolean applyFilters){ + if(scanner == null){ + scanner = bluetoothManager.getAdapter().getBluetoothLeScanner(); + } + if(scanner == null){ + // at this point we should already be waiting for bluetooth to turn back on + LOG.debug("cannot enable scan since bluetooth seems off (scanner == null)"); + return; + } + if(bluetoothManager.getAdapter().getState() != BluetoothAdapter.STATE_ON){ + // again, we should be waiting for the adapter to turn on again + LOG.debug("Bluetooth adapter state off"); + return; + } + if(currentState.isDoingAnyScan()){ + scanner.stopScan(scanCallback); + } + ArrayList scanFilters = null; + + if(applyFilters) { + List devices = GBApplication.app().getDeviceManager().getDevices(); + + scanFilters = new ArrayList<>(devices.size()); + + for (GBDevice device : devices) { + if (device.getState() == GBDevice.State.WAITING_FOR_SCAN) { + scanFilters.add(new ScanFilter.Builder() + .setDeviceAddress(device.getAddress()) + .build() + ); + } + } + + if(scanFilters.size() == 0){ + // no need to start scanning + LOG.debug("restartScan: stopping BLE scan, no devices"); + currentState = ScanningState.NOT_SCANNING; + updateNotification(false, 0); + return; + } + } + + ScanSettings scanSettings = new ScanSettings.Builder() + .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) // enforced anyway in background + .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) + .setMatchMode(ScanSettings.MATCH_MODE_STICKY) + .setLegacy(false) + .build(); + + scanner.startScan(scanFilters, scanSettings, scanCallback); + if(applyFilters) { + LOG.debug("restartScan: started scan for " + scanFilters.size() + " devices"); + updateNotification(true, scanFilters.size()); + currentState = ScanningState.SCANNING_WITH_FILTERS; + }else{ + LOG.debug("restartScan: started scan for all devices"); + updateNotification(true, 0); + currentState = ScanningState.SCANNING_WITHOUT_FILTERS; + } + + } + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return the communication channel to the service. + throw new UnsupportedOperationException("Not yet implemented"); + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index 83bdb5695..8654086cc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -81,6 +81,7 @@ public final class BtLEQueue { private final InternalGattCallback internalGattCallback; private final InternalGattServerCallback internalGattServerCallback; private boolean mAutoReconnect; + private boolean scanReconnect; private boolean mImplicitGattCallbackModify = true; private boolean mSendWriteRequestResponse = false; @@ -218,6 +219,10 @@ public final class BtLEQueue { mAutoReconnect = enable; } + public void setScanReconnect(boolean enable){ + this.scanReconnect = enable; + } + public void setImplicitGattCallbackModify(final boolean enable) { mImplicitGattCallbackModify = enable; } @@ -355,6 +360,12 @@ public final class BtLEQueue { */ private boolean maybeReconnect() { if (mAutoReconnect && mBluetoothGatt != null) { + if(scanReconnect){ + LOG.info("Waiting for BLE scan before attempting reconnection..."); + setDeviceConnectionState(State.WAITING_FOR_SCAN); + return true; + } + LOG.info("Enabling automatic ble reconnect..."); boolean result = mBluetoothGatt.connect(); mPauseTransaction = false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java index 820a93f66..f36a68e4c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -84,6 +84,7 @@ public class GB { public static final int NOTIFICATION_ID_EXPORT_FAILED = 5; public static final int NOTIFICATION_ID_PHONE_FIND = 6; public static final int NOTIFICATION_ID_GPS = 7; + public static final int NOTIFICATION_ID_SCAN = 8; public static final int NOTIFICATION_ID_ERROR = 42; private static final Logger LOG = LoggerFactory.getLogger(GB.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java index c1799e886..1c50ab9a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java @@ -45,6 +45,7 @@ public class GBPrefs { public static final String PACKAGE_PEBBLEMSG_BLACKLIST = "package_pebblemsg_blacklist"; public static final String CALENDAR_BLACKLIST = "calendar_blacklist"; public static final String DEVICE_AUTO_RECONNECT = "prefs_key_device_auto_reconnect"; + public static final String DEVICE_AUTO_RECONNECT_SCAN = "prefs_key_device_auto_reconnect_scan"; public static final String DEVICE_CONNECT_BACK = "prefs_key_device_reconnect_on_acl"; private static final String AUTO_START = "general_autostartonboot"; public static final String AUTO_EXPORT_ENABLED = "auto_export_enabled"; @@ -59,6 +60,9 @@ public class GBPrefs { public static boolean AUTO_RECONNECT_DEFAULT = true; public static final String PREF_ALLOW_INTENT_API = "prefs_key_allow_bluetooth_intent_api"; + public static final String RECONNECT_SCAN_KEY = "prefs_general_key_auto_reconnect_scan"; + public static final boolean RECONNECT_SCAN_DEFAULT = false; + public static final String USER_NAME = "mi_user_alias"; public static final String USER_NAME_DEFAULT = "gadgetbridge-user"; private static final String USER_BIRTHDAY = ""; @@ -80,6 +84,10 @@ public class GBPrefs { return deviceSpecificPreferences.getBoolean(DEVICE_AUTO_RECONNECT, AUTO_RECONNECT_DEFAULT); } + public boolean getAutoReconnectByScan() { + return mPrefs.getBoolean(RECONNECT_SCAN_KEY, RECONNECT_SCAN_DEFAULT); + } + public boolean getAutoStart() { return mPrefs.getBoolean(AUTO_START, AUTO_START_DEFAULT); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e147a5b3f..6829778d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2677,4 +2677,7 @@ BLE BT_CLASSIC Activity info + Waiting for device scan + Reconnect by BLE scan + Wait for device scan instead of blind connection attempts diff --git a/app/src/main/res/xml/discovery_pairing_preferences.xml b/app/src/main/res/xml/discovery_pairing_preferences.xml index c21b4f7a5..9c427ae3d 100644 --- a/app/src/main/res/xml/discovery_pairing_preferences.xml +++ b/app/src/main/res/xml/discovery_pairing_preferences.xml @@ -23,4 +23,11 @@ android:summary="@string/discover_unsupported_devices_description" android:title="@string/discover_unsupported_devices" app:iconSpaceReserved="false" /> +