Make automatic reconnect after connection loss configurable #293

Mi Band: automatically reconnect when the device is back in range

Also: #89
This commit is contained in:
cpfeiffer
2016-04-28 23:17:13 +02:00
parent eca5d40efe
commit 5e02724c4c
12 changed files with 103 additions and 14 deletions

View File

@@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@@ -57,6 +58,7 @@ public class GBApplication extends Application {
private static LimitedQueue mIDSenderLookup = new LimitedQueue(16); private static LimitedQueue mIDSenderLookup = new LimitedQueue(16);
private static Appender<ILoggingEvent> fileLogger; private static Appender<ILoggingEvent> fileLogger;
private static Prefs prefs; private static Prefs prefs;
private static GBPrefs gbPrefs;
public static final String ACTION_QUIT public static final String ACTION_QUIT
= "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit"; = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit";
@@ -91,6 +93,7 @@ public class GBApplication extends Application {
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs = new Prefs(sharedPrefs); prefs = new Prefs(sharedPrefs);
gbPrefs = new GBPrefs(prefs);
// don't do anything here before we set up logging, otherwise // don't do anything here before we set up logging, otherwise
// slf4j may be implicitly initialized before we properly configured it. // slf4j may be implicitly initialized before we properly configured it.
@@ -366,4 +369,8 @@ public class GBApplication extends Application {
public static Prefs getPrefs() { public static Prefs getPrefs() {
return prefs; return prefs;
} }
public static GBPrefs getGBPrefs() {
return gbPrefs;
}
} }

View File

@@ -59,6 +59,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
protected GBDevice gbDevice; protected GBDevice gbDevice;
private BluetoothAdapter btAdapter; private BluetoothAdapter btAdapter;
private Context context; private Context context;
private boolean autoReconnect;
public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) { public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) {
this.gbDevice = gbDevice; this.gbDevice = gbDevice;
@@ -81,6 +82,16 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
return gbDevice.isInitialized(); return gbDevice.isInitialized();
} }
@Override
public void setAutoReconnect(boolean enable) {
autoReconnect = enable;
}
@Override
public boolean getAutoReconnect() {
return autoReconnect;
}
@Override @Override
public GBDevice getDevice() { public GBDevice getDevice() {
return gbDevice; return gbDevice;

View File

@@ -6,6 +6,7 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.IBinder; import android.os.IBinder;
@@ -37,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE;
@@ -88,7 +90,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_PERFORM_PAIR; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_PERFORM_PAIR;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI;
public class DeviceCommunicationService extends Service { public class DeviceCommunicationService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class); private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
private boolean mStarted = false; private boolean mStarted = false;
@@ -130,6 +132,9 @@ public class DeviceCommunicationService extends Service {
super.onCreate(); super.onCreate();
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED)); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED));
mFactory = new DeviceSupportFactory(this); mFactory = new DeviceSupportFactory(this);
Prefs prefs = GBApplication.getPrefs();
prefs.getPreferences().registerOnSharedPreferenceChangeListener(this);
} }
@Override @Override
@@ -190,8 +195,10 @@ public class DeviceCommunicationService extends Service {
btDeviceAddress = gbDevice.getAddress(); btDeviceAddress = gbDevice.getAddress();
} }
boolean autoReconnect = GBPrefs.AUTO_RECONNECT_DEFAULT;
if (prefs != null) { if (prefs != null) {
prefs.getPreferences().edit().putString("last_device_address", btDeviceAddress).apply(); prefs.getPreferences().edit().putString("last_device_address", btDeviceAddress).apply();
autoReconnect = prefs.getPreferences().getBoolean(GBPrefs.AUTO_RECONNECT, GBPrefs.AUTO_RECONNECT_DEFAULT);
} }
if (gbDevice != null && !isConnecting() && !isConnected()) { if (gbDevice != null && !isConnecting() && !isConnected()) {
@@ -203,6 +210,7 @@ public class DeviceCommunicationService extends Service {
if (pair) { if (pair) {
deviceSupport.pair(); deviceSupport.pair();
} else { } else {
deviceSupport.setAutoReconnect(autoReconnect);
deviceSupport.connect(); deviceSupport.connect();
} }
} else { } else {
@@ -478,6 +486,8 @@ public class DeviceCommunicationService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
GBApplication.getPrefs().getPreferences().unregisterOnSharedPreferenceChangeListener(this);
LOG.debug("DeviceCommunicationService is being destroyed"); LOG.debug("DeviceCommunicationService is being destroyed");
super.onDestroy(); super.onDestroy();
@@ -514,4 +524,14 @@ public class DeviceCommunicationService extends Service {
return name; return name;
} }
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (GBPrefs.AUTO_RECONNECT.equals(key)) {
boolean autoReconnect = GBApplication.getGBPrefs().getAutoReconnect();
if (mDeviceSupport != null) {
mDeviceSupport.setAutoReconnect(autoReconnect);
}
}
}
} }

View File

@@ -62,6 +62,20 @@ public interface DeviceSupport extends EventHandler {
*/ */
boolean useAutoConnect(); boolean useAutoConnect();
/**
* Configures this instance to automatically attempt to reconnect after a connection loss.
* How, how long, or how often is up to the implementation.
* Note that tome implementations may not support automatic reconnection at all.
* @param enable
*/
void setAutoReconnect(boolean enable);
/**
* Returns whether this instance to configured to automatically attempt to reconnect after a
* connection loss.
*/
boolean getAutoReconnect();
/** /**
* Attempts to pair and connect this device with the gadget device. Success * Attempts to pair and connect this device with the gadget device. Success
* will be reported via a device change Intent. * will be reported via a device change Intent.

View File

@@ -56,6 +56,16 @@ public class ServiceDeviceSupport implements DeviceSupport {
return delegate.connect(); return delegate.connect();
} }
@Override
public void setAutoReconnect(boolean enable) {
delegate.setAutoReconnect(enable);
}
@Override
public boolean getAutoReconnect() {
return delegate.getAutoReconnect();
}
@Override @Override
public void dispose() { public void dispose() {
delegate.dispose(); delegate.dispose();

View File

@@ -42,10 +42,19 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
public boolean connect() { public boolean connect() {
if (mQueue == null) { if (mQueue == null) {
mQueue = new BtLEQueue(getBluetoothAdapter(), getDevice(), this, getContext()); mQueue = new BtLEQueue(getBluetoothAdapter(), getDevice(), this, getContext());
mQueue.setAutoReconnect(getAutoReconnect());
} }
return mQueue.connect(); return mQueue.connect();
} }
@Override
public void setAutoReconnect(boolean enable) {
super.setAutoReconnect(enable);
if (mQueue != null) {
mQueue.setAutoReconnect(enable);
}
}
/** /**
* Subclasses should populate the given builder to initialize the device (if necessary). * Subclasses should populate the given builder to initialize the device (if necessary).
* *

View File

@@ -34,12 +34,6 @@ public final class BtLEQueue {
private final GBDevice mGbDevice; private final GBDevice mGbDevice;
private final BluetoothAdapter mBluetoothAdapter; private final BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mBluetoothGatt; private BluetoothGatt mBluetoothGatt;
/**
* When an automatic reconnect was attempted after a connection breakdown (error)
*/
private long lastReconnectTime = System.currentTimeMillis();
private static final long MIN_MILLIS_BEFORE_RECONNECT = 1000 * 60 * 5; // 5 minutes
private final BlockingQueue<Transaction> mTransactions = new LinkedBlockingQueue<>(); private final BlockingQueue<Transaction> mTransactions = new LinkedBlockingQueue<>();
private volatile boolean mDisposed; private volatile boolean mDisposed;
@@ -51,6 +45,7 @@ public final class BtLEQueue {
private CountDownLatch mConnectionLatch; private CountDownLatch mConnectionLatch;
private BluetoothGattCharacteristic mWaitCharacteristic; private BluetoothGattCharacteristic mWaitCharacteristic;
private final InternalGattCallback internalGattCallback; private final InternalGattCallback internalGattCallback;
private boolean mAutoReconnect;
private Thread dispatchThread = new Thread("GadgetBridge GATT Dispatcher") { private Thread dispatchThread = new Thread("GadgetBridge GATT Dispatcher") {
@@ -130,6 +125,10 @@ public final class BtLEQueue {
dispatchThread.start(); dispatchThread.start();
} }
public void setAutoReconnect(boolean enable) {
mAutoReconnect = enable;
}
protected boolean isConnected() { protected boolean isConnected() {
return mGbDevice.isConnected(); return mGbDevice.isConnected();
} }
@@ -222,11 +221,9 @@ public final class BtLEQueue {
* @return true if a reconnection attempt was made, or false otherwise * @return true if a reconnection attempt was made, or false otherwise
*/ */
private boolean maybeReconnect() { private boolean maybeReconnect() {
long currentTime = System.currentTimeMillis(); if (mAutoReconnect && mBluetoothGatt != null) {
if (currentTime - lastReconnectTime >= MIN_MILLIS_BEFORE_RECONNECT) { LOG.info("Enabling automatic ble reconnect...");
LOG.info("Automatic reconnection attempt..."); return mBluetoothGatt.connect();
lastReconnectTime = currentTime;
return connect();
} }
return false; return false;
} }

View File

@@ -365,7 +365,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
LOG.info(e.getMessage()); LOG.info(e.getMessage());
mIsConnected = false; mIsConnected = false;
int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10); int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10);
if (reconnectAttempts > 0) { if (GBApplication.getGBPrefs().getAutoReconnect() && reconnectAttempts > 0) {
gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.setState(GBDevice.State.CONNECTING);
gbDevice.sendDeviceUpdateIntent(getContext()); gbDevice.sendDeviceUpdateIntent(getContext());
int delaySeconds = 1; int delaySeconds = 1;

View File

@@ -0,0 +1,16 @@
package nodomain.freeyourgadget.gadgetbridge.util;
public class GBPrefs {
public static final String AUTO_RECONNECT = "general_autocreconnect";
public static boolean AUTO_RECONNECT_DEFAULT = true;
private final Prefs mPrefs;
public GBPrefs(Prefs prefs) {
mPrefs = prefs;
}
public boolean getAutoReconnect() {
return mPrefs.getBoolean(AUTO_RECONNECT, AUTO_RECONNECT_DEFAULT);
}
}

View File

@@ -244,5 +244,6 @@
<string name="updatefirmwareoperation_firmware_not_sent">Firmware not sent</string> <string name="updatefirmwareoperation_firmware_not_sent">Firmware not sent</string>
<string name="charts_legend_heartrate">Heart Rate</string> <string name="charts_legend_heartrate">Heart Rate</string>
<string name="live_activity_heart_rate">Heart Rate</string> <string name="live_activity_heart_rate">Heart Rate</string>
<string name="pref_title_general_autocreonnect">Reconnect automatically</string>
</resources> </resources>

View File

@@ -7,6 +7,10 @@
android:defaultValue="false" android:defaultValue="false"
android:key="general_autoconnectonbluetooth" android:key="general_autoconnectonbluetooth"
android:title="@string/pref_title_general_autoconnectonbluetooth" /> android:title="@string/pref_title_general_autoconnectonbluetooth" />
<CheckBoxPreference
android:defaultValue="false"
android:key="general_autocreconnect"
android:title="@string/pref_title_general_autocreonnect" />
<ListPreference <ListPreference
android:defaultValue="default" android:defaultValue="default"
android:key="audio_player" android:key="audio_player"

View File

@@ -5,7 +5,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.0.0' classpath 'com.android.tools.build:gradle:2.1.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files