multi-device-support (#2526)

this PR aims to add device for multiple connected devices at once.

A lot of stuff already works, some things need to be done:

- [x] change DeviceCommunicationService to hold multiple devices and supports
- [x] implement connect / disconnect logic
- [x] widgets, not really suited for multiple devices, so far
- [x] change the notification to show multiple devices
- [ ] change GBDeviceService#onFindDevice and similar API functions to target individual devices, not all connected.
- [x] move auto-reconnect setting to device settings
- [x] fix music event crash
- [x] work out behaviour when pressing "connect" from notification
- [ ] handle service crashes
- [ ] suit coordinator methods for multiple devices of same kind
- [x] change ACL_CONNECTED receiver to connect to devices that are not currently registered in DeviceCommunicationService
- [ ] adjust after-boot auto-connection logic
- [ ] fix hanging device support. Device says disconnected, GB says connected
- [x] firmware updater doesn't work

My attempt to make onFindDevice work was to change the arguments to ```EventHandler#onFindDevice(GBDevice device, boolean start)```.
The Problem is that this forces the device-specific implementations to also accept GBDevice as an argument.

Co-authored-by: Daniel Dakhno <dakhnod@gmail.com>
Co-authored-by: Andreas Shimokawa <shimokawa@fsfe.org>
Co-authored-by: dakhnod <dakhnod@gmail.com>
Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2526
Co-authored-by: dakhnod <dakhnod@noreply.codeberg.org>
Co-committed-by: dakhnod <dakhnod@noreply.codeberg.org>
This commit is contained in:
dakhnod 2022-06-14 18:05:41 +02:00 committed by Andreas Shimokawa
parent e683a5bc56
commit 4a8523f790
40 changed files with 1036 additions and 561 deletions

View File

@ -68,6 +68,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@ -272,26 +273,28 @@ public class DebugActivity extends AbstractGBActivity {
if (context instanceof GBApplication) {
GBApplication gbApp = (GBApplication) context;
final GBDevice device = gbApp.getDeviceManager().getSelectedDevice();
if (device != null) {
new DatePickerDialog(DebugActivity.this, new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
Calendar date = Calendar.getInstance();
date.set(year, monthOfYear, dayOfMonth);
final List<GBDevice> devices = gbApp.getDeviceManager().getSelectedDevices();
if(devices.size() == 0){
GB.toast("Device not selected/connected", Toast.LENGTH_LONG, GB.INFO);
return;
}
new DatePickerDialog(DebugActivity.this, new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
Calendar date = Calendar.getInstance();
date.set(year, monthOfYear, dayOfMonth);
long timestamp = date.getTimeInMillis() - 1000;
GB.toast("Setting lastSyncTimeMillis: " + timestamp, Toast.LENGTH_LONG, GB.INFO);
long timestamp = date.getTimeInMillis() - 1000;
GB.toast("Setting lastSyncTimeMillis: " + timestamp, Toast.LENGTH_LONG, GB.INFO);
for(GBDevice device : devices){
SharedPreferences.Editor editor = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()).edit();
editor.remove("lastSyncTimeMillis"); //FIXME: key reconstruction is BAD
editor.putLong("lastSyncTimeMillis", timestamp);
editor.apply();
}
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
} else {
GB.toast("Device not selected/connected", Toast.LENGTH_LONG, GB.INFO);
}
}
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
}
@ -411,8 +414,8 @@ public class DebugActivity extends AbstractGBActivity {
public void onClick(View v) {
Context context = getApplicationContext();
GBApplication gbApp = (GBApplication) context;
final GBDevice device = gbApp.getDeviceManager().getSelectedDevice();
if (device != null) {
List<GBDevice> devices = gbApp.getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
GBApplication.deleteDeviceSpecificSharedPrefs(device.getAddress());
}
}

View File

@ -214,6 +214,19 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
} else {
setInfoText(getString(R.string.installer_activity_wait_while_determining_status));
List<GBDevice> selectedDevices = GBApplication.app().getDeviceManager().getSelectedDevices();
if(selectedDevices.size() == 0){
GB.toast("please connect the device you want to send to", Toast.LENGTH_LONG, GB.ERROR);
finish();
return;
}
if(selectedDevices.size() != 1){
GB.toast("please connect ONLY the device you want to send to", Toast.LENGTH_LONG, GB.ERROR);
finish();
return;
}
device = selectedDevices.get(0);
// needed to get the device
if (device == null || !device.isConnected()) {
connect();
@ -245,14 +258,17 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
List<DeviceCoordinator> allCoordinators = DeviceHelper.getInstance().getAllCoordinators();
List<DeviceCoordinator> sortedCoordinators = new ArrayList<>(allCoordinators.size());
GBDevice connectedDevice = deviceManager.getSelectedDevice();
if (connectedDevice != null && connectedDevice.isConnected()) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(connectedDevice);
if (coordinator != null) {
connectedCoordinators.add(coordinator);
List<GBDevice> devices = deviceManager.getSelectedDevices();
for(GBDevice connectedDevice : devices){
if (connectedDevice.isConnected()) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(connectedDevice);
if (coordinator != null) {
connectedCoordinators.add(coordinator);
}
}
}
sortedCoordinators.addAll(connectedCoordinators);
for (DeviceCoordinator coordinator : allCoordinators) {
if (!connectedCoordinators.contains(coordinator)) {

View File

@ -811,6 +811,8 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
int[] supportedSettings = coordinator.getSupportedDeviceSpecificSettings(device);
String[] supportedLanguages = coordinator.getSupportedLanguageSettings(device);
supportedSettings = ArrayUtils.insert(0, supportedSettings, coordinator.getSupportedDeviceSpecificConnectionSettings());
if (supportedLanguages != null) {
supportedSettings = ArrayUtils.insert(0, supportedSettings, R.xml.devicesettings_language_generic);
}

View File

@ -154,7 +154,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
public boolean onLongClick(View v) {
if (device.getState() != GBDevice.State.NOT_CONNECTED) {
showTransientSnackbar(R.string.controlcenter_snackbar_disconnecting);
GBApplication.deviceService().disconnect();
GBApplication.deviceService().disconnect(device);
}
return true;
}
@ -261,7 +261,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
);
//device specific settings
holder.deviceSpecificSettingsView.setVisibility(coordinator.getSupportedDeviceSpecificSettings(device) != null ? View.VISIBLE : View.GONE);
holder.deviceSpecificSettingsView.setVisibility(coordinator.getSupportedDeviceSpecificConnectionSettings() != null ? View.VISIBLE : View.GONE);
holder.deviceSpecificSettingsView.setOnClickListener(new View.OnClickListener()
{

View File

@ -0,0 +1,8 @@
package nodomain.freeyourgadget.gadgetbridge.devices;
public abstract class AbstractBLClassicDeviceCoordinator extends AbstractDeviceCoordinator {
@Override
public ConnectionType getConnectionType() {
return ConnectionType.BL_CLASSIC;
}
}

View File

@ -0,0 +1,8 @@
package nodomain.freeyourgadget.gadgetbridge.devices;
public abstract class AbstractBLEDeviceCoordinator extends AbstractDeviceCoordinator{
@Override
public ConnectionType getConnectionType() {
return ConnectionType.BLE;
}
}

View File

@ -26,6 +26,7 @@ import android.bluetooth.le.ScanFilter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,6 +38,7 @@ import java.util.Collections;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
@ -61,6 +63,11 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return getSupportedType(candidate).isSupported();
}
@Override
public ConnectionType getConnectionType() {
return ConnectionType.BOTH;
}
@Override
public boolean supports(GBDevice device) {
return getDeviceType().equals(device.getType());
@ -86,7 +93,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
public void deleteDevice(final GBDevice gbDevice) throws GBException {
LOG.info("will try to delete device: " + gbDevice.getName());
if (gbDevice.isConnected() || gbDevice.isConnecting()) {
GBApplication.deviceService().disconnect();
GBApplication.deviceService().disconnect(gbDevice);
}
Prefs prefs = getPrefs();
@ -258,9 +265,24 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return false;
}
@Override
public int[] getSupportedDeviceSpecificConnectionSettings() {
int[] settings = new int[0];
ConnectionType connectionType = getConnectionType();
if(connectionType.usesBluetoothLE()){
settings = ArrayUtils.insert(0, settings, R.xml.devicesettings_reconnect_ble);
}
if(connectionType.usesBluetoothClassic()){
settings = ArrayUtils.insert(0, settings, R.xml.devicesettings_reconnect_bl_classic);
}
return settings;
}
@Override
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
return null;
return new int[0];
}
@Override

View File

@ -77,6 +77,27 @@ public interface DeviceCoordinator {
*/
int BONDING_STYLE_LAZY = 4;
enum ConnectionType{
BLE(false, true),
BL_CLASSIC(true, false),
BOTH(true, true)
;
boolean usesBluetoothClassic, usesBluetoothLE;
ConnectionType(boolean usesBluetoothClassic, boolean usesBluetoothLE) {
this.usesBluetoothClassic = usesBluetoothClassic;
this.usesBluetoothLE = usesBluetoothLE;
}
public boolean usesBluetoothLE(){
return usesBluetoothLE;
}
public boolean usesBluetoothClassic(){
return usesBluetoothClassic;
}
}
/**
* Checks whether this coordinator handles the given candidate.
* Returns the supported device type for the given candidate or
@ -88,6 +109,13 @@ public interface DeviceCoordinator {
@NonNull
DeviceType getSupportedType(GBDeviceCandidate candidate);
/**
* Returns the type of connection, Classic of BLE
*
* @return ConnectionType
*/
ConnectionType getConnectionType();
/**
* Checks whether this coordinator handles the given candidate.
*
@ -363,6 +391,13 @@ public interface DeviceCoordinator {
*/
boolean supportsUnicodeEmojis();
/**
* Returns device specific settings related to connection
*
* @return int[]
*/
int[] getSupportedDeviceSpecificConnectionSettings();
/**
* Indicates which device specific settings the device supports (not per device type or family, but unique per device).
*/

View File

@ -67,7 +67,7 @@ public class DeviceManager {
* This allows direct access to the list from ListAdapters.
*/
private final List<GBDevice> deviceList = new ArrayList<>();
private GBDevice selectedDevice = null;
private List<GBDevice> selectedDevices = new ArrayList<>();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@ -137,22 +137,13 @@ public class DeviceManager {
}
private void updateSelectedDevice(GBDevice dev) {
if (selectedDevice == null) {
selectedDevice = dev;
} else {
if (selectedDevice.equals(dev)) {
selectedDevice = dev; // equality vs identity!
} else {
if (selectedDevice.isConnected() && dev.isConnected()) {
LOG.warn("multiple connected devices -- this is currently not really supported");
selectedDevice = dev; // use the last one that changed
} else if (!selectedDevice.isConnected()) {
selectedDevice = dev; // use the last one that changed
}
selectedDevices.clear();
for(GBDevice device : deviceList){
if(device.isInitialized()){
selectedDevices.add(device);
}
}
GB.updateNotification(selectedDevice, context);
GB.updateNotification(selectedDevices, context);
}
private void refreshPairedDevices() {
@ -184,9 +175,8 @@ public class DeviceManager {
return Collections.unmodifiableList(deviceList);
}
@Nullable
public GBDevice getSelectedDevice() {
return selectedDevice;
public List<GBDevice> getSelectedDevices() {
return selectedDevices;
}
private void notifyDevicesChanged() {

View File

@ -25,6 +25,7 @@ import androidx.annotation.NonNull;
import java.io.File;
import java.io.IOException;
import java.util.List;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@ -168,18 +169,27 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
@Override
public boolean supportsAppListFetching() {
GBDevice mGBDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
if (mGBDevice != null && mGBDevice.getFirmwareVersion() != null) {
return PebbleUtils.getFwMajor(mGBDevice.getFirmwareVersion()) < 3;
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
if(device.getType() == DeviceType.PEBBLE){
if (device.getFirmwareVersion() != null) {
return PebbleUtils.getFwMajor(device.getFirmwareVersion()) < 3;
}
}
}
return false;
}
@Override
public boolean supportsAppReordering() {
GBDevice mGBDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
if (mGBDevice != null && mGBDevice.getFirmwareVersion() != null) {
return PebbleUtils.getFwMajor(mGBDevice.getFirmwareVersion()) >= 3;
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
if(device.getType() == DeviceType.PEBBLE){
if (device.getFirmwareVersion() != null) {
return PebbleUtils.getFwMajor(device.getFirmwareVersion()) >= 3;
}
}
}
return false;
}

View File

@ -25,7 +25,7 @@ import androidx.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLClassicDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
@ -35,7 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class QC35Coordinator extends AbstractDeviceCoordinator {
public class QC35Coordinator extends AbstractBLClassicDeviceCoordinator {
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {

View File

@ -54,25 +54,34 @@ public class AppsManagementActivity extends AbstractGBActivity {
private void refreshInstalledApps() {
try {
GBDevice selected = GBApplication.app().getDeviceManager().getSelectedDevice();
if (selected.getType() != DeviceType.FOSSILQHYBRID || !selected.isConnected() || !selected.getModel().startsWith("DN") || selected.getState() != GBDevice.State.INITIALIZED) {
throw new RuntimeException("Device not connected");
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
boolean deviceFound = false;
for(GBDevice device : devices){
if (
device.getType() == DeviceType.FOSSILQHYBRID &&
device.isConnected() &&
device.getModel().startsWith("DN") &&
device.getState() == GBDevice.State.INITIALIZED
) {
String installedAppsJson = device.getDeviceInfo("INSTALLED_APPS").getDetails();
if (installedAppsJson == null || installedAppsJson.isEmpty()) {
throw new RuntimeException("can't get installed apps");
}
JSONArray apps = new JSONArray(installedAppsJson);
appNames = new String[apps.length()];
for (int i = 0; i < apps.length(); i++) {
appNames[i] = apps.getString(i);
}
appsListView.setAdapter(new AppsListAdapter(this, appNames));
}
return;
}
String installedAppsJson = selected.getDeviceInfo("INSTALLED_APPS").getDetails();
if (installedAppsJson == null || installedAppsJson.isEmpty()) {
throw new RuntimeException("cant get installed apps");
}
JSONArray apps = new JSONArray(installedAppsJson);
appNames = new String[apps.length()];
for (int i = 0; i < apps.length(); i++) {
appNames[i] = apps.getString(i);
}
appsListView.setAdapter(new AppsListAdapter(this, appNames));
} catch (Exception e) {
} catch (JSONException e) {
toast(e.getMessage());
finish();
return;
}
throw new RuntimeException("Device not connected");
}
class AppsListAdapter extends ArrayAdapter<String> {

View File

@ -29,6 +29,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
@ -85,9 +87,16 @@ public class CalibrationActivity extends AbstractGBActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qhybrid_calibration);
GBDevice device = GBApplication.app().getDeviceManager().getSelectedDevice();
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
boolean atLeastOneConnected = false;
for(GBDevice device : devices){
if(device.getType() == DeviceType.FOSSILQHYBRID){
atLeastOneConnected = true;
break;
}
}
if(device == null || device.getType() != DeviceType.FOSSILQHYBRID){
if(!atLeastOneConnected){
Toast.makeText(this, R.string.watch_not_connected, Toast.LENGTH_LONG).show();
finish();
return;

View File

@ -303,12 +303,14 @@ public class ConfigActivity extends AbstractGBActivity {
}
});
device = GBApplication.app().getDeviceManager().getSelectedDevice();
if (device == null || device.getType() != DeviceType.FOSSILQHYBRID || device.getFirmwareVersion().charAt(2) != '0') {
setSettingsError(getString(R.string.watch_not_connected));
} else {
updateSettings();
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
if (device.getType() == DeviceType.FOSSILQHYBRID && device.getFirmwareVersion().charAt(2) == '0') {
updateSettings();
return;
}
}
setSettingsError(getString(R.string.watch_not_connected));
}
private void updateTimeOffset() {

View File

@ -46,6 +46,8 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.CustomBackgroundWidgetElement;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.CustomTextWidgetElement;
@ -168,19 +170,25 @@ public class HRConfigActivity extends AbstractGBActivity {
}
// Disable some functions on watches with too new firmware (from official app 4.6.0 and higher)
String fwVersion_str = GBApplication.app().getDeviceManager().getSelectedDevice().getFirmwareVersion();
fwVersion_str = fwVersion_str.replaceFirst("^DN", "").replaceFirst("r\\.v.*", "");
Version fwVersion = new Version(fwVersion_str);
if (fwVersion.compareTo(new Version("1.0.2.20")) >= 0) {
findViewById(R.id.qhybrid_widget_add).setEnabled(false);
for (int i = 0; i < widgetButtonsMapping.size(); i++) {
final int widgetButtonId = widgetButtonsMapping.keyAt(i);
findViewById(widgetButtonId).setEnabled(false);
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
if(device.getType() == DeviceType.FOSSILQHYBRID){
String fwVersion_str = device.getFirmwareVersion();
fwVersion_str = fwVersion_str.replaceFirst("^DN", "").replaceFirst("r\\.v.*", "");
Version fwVersion = new Version(fwVersion_str);
if (fwVersion.compareTo(new Version("1.0.2.20")) >= 0) {
findViewById(R.id.qhybrid_widget_add).setEnabled(false);
for (int i = 0; i < widgetButtonsMapping.size(); i++) {
final int widgetButtonId = widgetButtonsMapping.keyAt(i);
findViewById(widgetButtonId).setEnabled(false);
}
findViewById(R.id.qhybrid_set_background).setEnabled(false);
findViewById(R.id.qhybrid_unset_background).setEnabled(false);
GB.toast(getString(R.string.fossil_hr_warning_firmware_too_new), Toast.LENGTH_LONG, GB.INFO);
}
}
findViewById(R.id.qhybrid_set_background).setEnabled(false);
findViewById(R.id.qhybrid_unset_background).setEnabled(false);
GB.toast(getString(R.string.fossil_hr_warning_firmware_too_new), Toast.LENGTH_LONG, GB.INFO);
}
}
@Override

View File

@ -35,6 +35,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -42,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -54,7 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Version;
public class QHybridCoordinator extends AbstractDeviceCoordinator {
public class QHybridCoordinator extends AbstractBLEDeviceCoordinator {
private static final Logger LOG = LoggerFactory.getLogger(QHybridCoordinator.class);
@NonNull
@ -89,8 +91,13 @@ public class QHybridCoordinator extends AbstractDeviceCoordinator {
@Override
public boolean supportsActivityDataFetching() {
GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
return connectedDevice != null && connectedDevice.getType() == DeviceType.FOSSILQHYBRID && connectedDevice.getState() == GBDevice.State.INITIALIZED;
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
if(isFossilHybrid(device) && device.getState() == GBDevice.State.INITIALIZED){
return true;
}
}
return false;
}
@Override
@ -129,11 +136,14 @@ public class QHybridCoordinator extends AbstractDeviceCoordinator {
}
private boolean supportsAlarmConfiguration() {
GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
if(connectedDevice == null || connectedDevice.getType() != DeviceType.FOSSILQHYBRID || connectedDevice.getState() != GBDevice.State.INITIALIZED){
return false;
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
LOG.debug("devices count: " + devices.size());
for(GBDevice device : devices){
if(isFossilHybrid(device) && device.getState() == GBDevice.State.INITIALIZED){
return true;
}
}
return true;
return false;
}
@Override
@ -268,22 +278,34 @@ public class QHybridCoordinator extends AbstractDeviceCoordinator {
}
private boolean isHybridHR() {
GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
if (connectedDevice != null) {
return connectedDevice.getName().startsWith("Hybrid HR");
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
if(isFossilHybrid(device) && device.getName().startsWith("Hybrid HR")){
return true;
}
}
return false;
}
private Version getFirmwareVersion() {
String firmware = GBApplication.app().getDeviceManager().getSelectedDevice().getFirmwareVersion();
if (firmware != null) {
Matcher matcher = Pattern.compile("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+").matcher(firmware); // DN1.0.2.19r.v5
if (matcher.find()) {
firmware = matcher.group(0);
return new Version(firmware);
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
if(isFossilHybrid(device)){
String firmware = device.getFirmwareVersion();
if (firmware != null) {
Matcher matcher = Pattern.compile("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+").matcher(firmware); // DN1.0.2.19r.v5
if (matcher.find()) {
firmware = matcher.group(0);
return new Version(firmware);
}
}
}
}
return null;
}
private boolean isFossilHybrid(GBDevice device){
return device.getType() == DeviceType.FOSSILQHYBRID;
}
}

View File

@ -14,6 +14,7 @@ import java.util.Collections;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -26,7 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support.UM25Support;
public class UM25Coordinator extends AbstractDeviceCoordinator {
public class UM25Coordinator extends AbstractBLEDeviceCoordinator {
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {

View File

@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -36,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class VescCoordinator extends AbstractDeviceCoordinator {
public class VescCoordinator extends AbstractBLEDeviceCoordinator {
public final static String UUID_SERVICE_SERIAL_HM10 = "0000ffe0-0000-1000-8000-00805f9b34fb";
public final static String UUID_CHARACTERISTIC_SERIAL_TX_HM10 = "0000ffe1-0000-1000-8000-00805f9b34fb";
public final static String UUID_CHARACTERISTIC_SERIAL_RX_HM10 = "0000ffe1-0000-1000-8000-00805f9b34fb";

View File

@ -20,13 +20,17 @@ import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
public class BluetoothConnectReceiver extends BroadcastReceiver {
@ -46,14 +50,30 @@ public class BluetoothConnectReceiver extends BroadcastReceiver {
}
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
LOG.info("connection attempt detected from or to " + device.getAddress() + "(" + device.getName() + ")");
LOG.info("connection attempt detected from " + device.getAddress() + "(" + device.getName() + ")");
GBDevice gbDevice = service.getGBDevice();
if (gbDevice != null) {
if (device.getAddress().equals(gbDevice.getAddress()) && gbDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
LOG.info("Will re-connect to " + gbDevice.getAddress() + "(" + gbDevice.getName() + ")");
GBApplication.deviceService().connect();
GBDevice gbDevice = getKnownDeviceByAddressOrNull(device.getAddress());
if(gbDevice == null){
LOG.info("connected device {} unknown", device.getAddress());
return;
}
SharedPreferences deviceSpecificPreferences = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress());
boolean reactToConnection = deviceSpecificPreferences.getBoolean(GBPrefs.DEVICE_CONNECT_BACK, false);
reactToConnection |= gbDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT;
if(!reactToConnection){
return;
}
LOG.info("Will re-connect to " + gbDevice.getAddress() + "(" + gbDevice.getName() + ")");
GBApplication.deviceService().connect(gbDevice);
}
private GBDevice getKnownDeviceByAddressOrNull(String deviceAddress){
List<GBDevice> knownDevices = GBApplication.app().getDeviceManager().getDevices();
for(GBDevice device : knownDevices){
if(device.getAddress().equals(deviceAddress)){
return device;
}
}
return null;
}
}

View File

@ -47,21 +47,26 @@ public class BluetoothPairingRequestReceiver extends BroadcastReceiver {
if (!action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
return;
}
GBDevice gbDevice = service.getGBDevice();
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (gbDevice == null || device == null) {
if (device == null) {
return;
}
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
GBDevice gbDevice = null;
try {
if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_NONE) {
LOG.info("Aborting unwanted pairing request");
abortBroadcast();
gbDevice = service.getDeviceByAddress(device.getAddress());
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
try {
if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_NONE) {
LOG.info("Aborting unwanted pairing request");
abortBroadcast();
}
} catch (Exception e) {
LOG.warn("Could not abort pairing request process");
}
} catch (Exception e) {
LOG.warn("Could not abort pairing request process");
} catch (DeviceCommunicationService.DeviceNotFoundException e) {
e.printStackTrace();
}
}
}

View File

@ -767,9 +767,9 @@ public class NotificationListener extends NotificationListenerService {
notificationsActive.removeAll(notificationsToRemove);
// Send notification remove request to device
GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
if (connectedDevice != null) {
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(connectedDevice.getAddress()));
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()));
if (prefs.getBoolean("autoremove_notifications", true)) {
for (int id : notificationsToRemove) {
LOG.info("Notification " + id + " removed, will ask device to delete it");

View File

@ -118,6 +118,13 @@ public class GBDeviceService implements DeviceService {
invokeService(intent);
}
@Override
public void disconnect(@Nullable GBDevice device) {
Intent intent = createIntent().setAction(ACTION_DISCONNECT)
.putExtra(GBDevice.EXTRA_DEVICE, device);
invokeService(intent);
}
@Override
public void disconnect() {
Intent intent = createIntent().setAction(ACTION_DISCONNECT);

View File

@ -154,6 +154,8 @@ public interface DeviceService extends EventHandler {
void connect(@Nullable GBDevice device, boolean firstTime);
void disconnect(@Nullable GBDevice device);
void disconnect();
void quit();

View File

@ -44,9 +44,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
@ -189,6 +191,108 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEA
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WORLD_CLOCKS;
public class DeviceCommunicationService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
public static class DeviceStruct{
private GBDevice device;
private DeviceCoordinator coordinator;
private DeviceSupport deviceSupport;
public GBDevice getDevice() {
return device;
}
public void setDevice(GBDevice device) {
this.device = device;
}
public DeviceCoordinator getCoordinator() {
return coordinator;
}
public void setCoordinator(DeviceCoordinator coordinator) {
this.coordinator = coordinator;
}
public DeviceSupport getDeviceSupport() {
return deviceSupport;
}
public void setDeviceSupport(DeviceSupport deviceSupport) {
this.deviceSupport = deviceSupport;
}
}
private class FeatureSet{
private boolean supportsWeather = false;
private boolean supportsActivityDataFetching = false;
private boolean supportsCalendarEvents = false;
private boolean supportsMusicInfo = false;
public boolean supportsWeather() {
return supportsWeather;
}
public void setSupportsWeather(boolean supportsWeather) {
this.supportsWeather = supportsWeather;
}
public boolean supportsActivityDataFetching() {
return supportsActivityDataFetching;
}
public void setSupportsActivityDataFetching(boolean supportsActivityDataFetching) {
this.supportsActivityDataFetching = supportsActivityDataFetching;
}
public boolean supportsCalendarEvents() {
return supportsCalendarEvents;
}
public void setSupportsCalendarEvents(boolean supportsCalendarEvents) {
this.supportsCalendarEvents = supportsCalendarEvents;
}
public boolean supportsMusicInfo() {
return supportsMusicInfo;
}
public void setSupportsMusicInfo(boolean supportsMusicInfo) {
this.supportsMusicInfo = supportsMusicInfo;
}
public void logicalOr(DeviceCoordinator operand){
if(operand.supportsCalendarEvents()){
setSupportsCalendarEvents(true);
}
if(operand.supportsWeather()){
setSupportsWeather(true);
}
if(operand.supportsActivityDataFetching()){
setSupportsActivityDataFetching(true);
}
if(operand.supportsMusicInfo()){
setSupportsMusicInfo(true);
}
}
}
public static class DeviceNotFoundException extends GBException{
private final String address;
public DeviceNotFoundException(GBDevice device) {
this.address = device.getAddress();
}
public DeviceNotFoundException(String address) {
this.address = address;
}
@Nullable
@Override
public String getMessage() {
return String.format("device %s not found cached", address);
}
}
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
@SuppressLint("StaticFieldLeak") // only used for test cases
private static DeviceSupportFactory DEVICE_SUPPORT_FACTORY = null;
@ -196,9 +300,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
private boolean mStarted = false;
private DeviceSupportFactory mFactory;
private GBDevice mGBDevice = null;
private DeviceSupport mDeviceSupport;
private DeviceCoordinator mCoordinator = null;
private final ArrayList<DeviceStruct> deviceStructs = new ArrayList<>(1);
private PhoneCallReceiver mPhoneCallReceiver = null;
private SMSReceiver mSMSReceiver = null;
@ -236,6 +338,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
*
* @param factory
*/
@SuppressWarnings("JavaDoc")
public static void setDeviceSupportFactory(DeviceSupportFactory factory) {
DEVICE_SUPPORT_FACTORY = factory;
}
@ -248,20 +351,45 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (GBDevice.ACTION_DEVICE_CHANGED.equals(action)) {
if(GBDevice.ACTION_DEVICE_CHANGED.equals(action)){
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
if (mGBDevice != null && mGBDevice.equals(device)) {
mGBDevice = device;
mCoordinator = DeviceHelper.getInstance().getCoordinator(device);
boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized());
setReceiversEnableState(enableReceivers, mGBDevice.isInitialized(), mCoordinator);
} else {
LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + device);
// create a new instance of the changed devices coordinator, in case it's capabilities changed
DeviceStruct cachedStruct = getDeviceStructOrNull(device);
if(cachedStruct != null) {
cachedStruct.setDevice(device);
DeviceCoordinator newCoordinator = DeviceHelper.getInstance().getCoordinator(device);
cachedStruct.setCoordinator(newCoordinator);
}
updateReceiversState();
}
}
};
private void updateReceiversState(){
boolean enableReceivers = false;
boolean anyDeviceInitialized = false;
FeatureSet features = new FeatureSet();
for(DeviceStruct struct: deviceStructs){
DeviceSupport deviceSupport = struct.getDeviceSupport();
if((deviceSupport != null && deviceSupport.useAutoConnect()) || isDeviceInitialized(struct.getDevice())){
enableReceivers = true;
}
if(isDeviceInitialized(struct.getDevice())){
anyDeviceInitialized = true;
}
DeviceCoordinator coordinator = struct.getCoordinator();
if(coordinator != null){
features.logicalOr(coordinator);
}
}
setReceiversEnableState(enableReceivers, anyDeviceInitialized, features);
}
@Override
public void onCreate() {
LOG.debug("DeviceCommunicationService is being created");
@ -269,6 +397,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED));
mFactory = getDeviceSupportFactory();
mBlueToothConnectReceiver = new BluetoothConnectReceiver(this);
registerReceiver(mBlueToothConnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));
if (hasPrefs()) {
getPrefs().getPreferences().registerOnSharedPreferenceChangeListener(this);
}
@ -306,14 +437,15 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
return START_NOT_STICKY;
}
if (mDeviceSupport == null || (!isInitialized() && !action.equals(ACTION_DISCONNECT) && (!mDeviceSupport.useAutoConnect() || isConnected()))) {
// TODO
/*if (mDeviceSupport == null || (!isInitialized() && !action.equals(ACTION_DISCONNECT) && (!mDeviceSupport.useAutoConnect() || isConnected()))) {
// trying to send notification without valid Bluetooth connection
if (mGBDevice != null) {
// at least send back the current device state
mGBDevice.sendDeviceUpdateIntent(this);
}
return START_STICKY;
}
}*/
}
// when we get past this, we should have valid mDeviceSupport and mGBDevice instances
@ -338,41 +470,73 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
btDeviceAddress = gbDevice.getAddress();
}
if(gbDevice == null){
return START_NOT_STICKY;
}
boolean autoReconnect = GBPrefs.AUTO_RECONNECT_DEFAULT;
if (prefs != null && prefs.getPreferences() != null) {
prefs.getPreferences().edit().putString("last_device_address", btDeviceAddress).apply();
autoReconnect = getGBPrefs().getAutoReconnect();
autoReconnect = getGBPrefs().getAutoReconnect(gbDevice);
}
if (gbDevice != null && !isConnecting() && !isConnected()) {
setDeviceSupport(null);
try {
DeviceSupport deviceSupport = mFactory.createDeviceSupport(gbDevice);
if (deviceSupport != null) {
setDeviceSupport(deviceSupport);
if (firstTime) {
deviceSupport.connectFirstTime();
} else {
deviceSupport.setAutoReconnect(autoReconnect);
deviceSupport.connect();
}
} else {
GB.toast(this, getString(R.string.cannot_connect, "Can't create device support"), Toast.LENGTH_SHORT, GB.ERROR);
}
} catch (Exception e) {
GB.toast(this, getString(R.string.cannot_connect, e.getMessage()), Toast.LENGTH_SHORT, GB.ERROR, e);
setDeviceSupport(null);
DeviceStruct registeredStruct = getDeviceStructOrNull(gbDevice);
if(registeredStruct != null){
boolean deviceAlreadyConnected = isDeviceConnecting(registeredStruct.getDevice()) || isDeviceConnected(registeredStruct.getDevice());
if(deviceAlreadyConnected){
break;
}
} else if (mGBDevice != null) {
// send an update at least
mGBDevice.sendDeviceUpdateIntent(this);
try {
removeDeviceSupport(gbDevice);
} catch (DeviceNotFoundException e) {
e.printStackTrace();
}
}else{
registeredStruct = new DeviceStruct();
registeredStruct.setDevice(gbDevice);
registeredStruct.setCoordinator(DeviceHelper.getInstance().getCoordinator(gbDevice));
deviceStructs.add(registeredStruct);
}
try {
DeviceSupport deviceSupport = mFactory.createDeviceSupport(gbDevice);
if (deviceSupport != null) {
setDeviceSupport(gbDevice, deviceSupport);
if (firstTime) {
deviceSupport.connectFirstTime();
} else {
deviceSupport.setAutoReconnect(autoReconnect);
deviceSupport.connect();
}
} else {
GB.toast(this, getString(R.string.cannot_connect, "Can't create device support"), Toast.LENGTH_SHORT, GB.ERROR);
}
} catch (Exception e) {
GB.toast(this, getString(R.string.cannot_connect, e.getMessage()), Toast.LENGTH_SHORT, GB.ERROR, e);
}
for(DeviceStruct struct2 : deviceStructs){
struct2.getDevice().sendDeviceUpdateIntent(this);
}
break;
default:
if (mDeviceSupport == null || mGBDevice == null) {
LOG.warn("device support:" + mDeviceSupport + ", device: " + mGBDevice + ", aborting");
} else {
handleAction(intent, action, prefs);
GBDevice targetedDevice = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
ArrayList<GBDevice> targetedDevices = new ArrayList<>();
if(targetedDevice != null){
targetedDevices.add(targetedDevice);
}else{
for(GBDevice device : getGBDevices()){
if(isDeviceInitialized(device)){
targetedDevices.add(device);
}
}
}
for (GBDevice device1 : targetedDevices) {
try {
handleAction(intent, action, device1);
} catch (DeviceNotFoundException e) {
e.printStackTrace();
}
}
break;
}
@ -383,21 +547,35 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
* @param text original text
* @return 'text' or a new String without non supported chars like emoticons, etc.
*/
private String sanitizeNotifText(String text) {
private String sanitizeNotifText(String text, GBDevice device) throws DeviceNotFoundException {
if (text == null || text.length() == 0)
return text;
text = mDeviceSupport.customStringFilter(text);
text = getDeviceSupport(device).customStringFilter(text);
if (!mCoordinator.supportsUnicodeEmojis()) {
if (!getDeviceCoordinator(device).supportsUnicodeEmojis()) {
return EmojiConverter.convertUnicodeEmojiToAscii(text, getApplicationContext());
}
return text;
}
private void handleAction(Intent intent, String action, Prefs prefs) {
Prefs devicePrefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(mGBDevice.getAddress()));
private DeviceCoordinator getDeviceCoordinator(GBDevice device) throws DeviceNotFoundException {
if(device == null){
throw new DeviceNotFoundException("null");
}
for(DeviceStruct struct : deviceStructs){
if(struct.getDevice().equals(device)){
return struct.getCoordinator();
}
}
throw new DeviceNotFoundException(device);
}
private void handleAction(Intent intent, String action, GBDevice device) throws DeviceNotFoundException {
DeviceSupport deviceSupport = getDeviceSupport(device);
Prefs devicePrefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()));
boolean transliterate = devicePrefs.getBoolean(PREF_TRANSLITERATION_ENABLED, false);
if (transliterate) {
@ -410,16 +588,16 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
switch (action) {
case ACTION_REQUEST_DEVICEINFO:
mGBDevice.sendDeviceUpdateIntent(this);
device.sendDeviceUpdateIntent(this);
break;
case ACTION_NOTIFICATION: {
int desiredId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
NotificationSpec notificationSpec = new NotificationSpec(desiredId);
notificationSpec.phoneNumber = intent.getStringExtra(EXTRA_NOTIFICATION_PHONENUMBER);
notificationSpec.sender = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_SENDER));
notificationSpec.subject = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_SUBJECT));
notificationSpec.title = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE));
notificationSpec.body = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_BODY));
notificationSpec.sender = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_SENDER), device);
notificationSpec.subject = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_SUBJECT), device);
notificationSpec.title = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE), device);
notificationSpec.body = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_BODY), device);
notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME);
notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE);
notificationSpec.attachedActions = (ArrayList<NotificationSpec.Action>) intent.getSerializableExtra(EXTRA_NOTIFICATION_ACTIONS);
@ -449,11 +627,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
notificationSpec.cannedReplies = replies.toArray(new String[0]);
}
mDeviceSupport.onNotification(notificationSpec);
deviceSupport.onNotification(notificationSpec);
break;
}
case ACTION_DELETE_NOTIFICATION: {
mDeviceSupport.onDeleteNotification(intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1));
deviceSupport.onDeleteNotification(intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1));
break;
}
case ACTION_ADD_CALENDAREVENT: {
@ -462,61 +640,62 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
calendarEventSpec.type = intent.getByteExtra(EXTRA_CALENDAREVENT_TYPE, (byte) -1);
calendarEventSpec.timestamp = intent.getIntExtra(EXTRA_CALENDAREVENT_TIMESTAMP, -1);
calendarEventSpec.durationInSeconds = intent.getIntExtra(EXTRA_CALENDAREVENT_DURATION, -1);
calendarEventSpec.title = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE));
calendarEventSpec.description = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION));
calendarEventSpec.location = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_LOCATION));
mDeviceSupport.onAddCalendarEvent(calendarEventSpec);
calendarEventSpec.title = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE), device);
calendarEventSpec.description = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION), device);
calendarEventSpec.location = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_LOCATION), device);
deviceSupport.onAddCalendarEvent(calendarEventSpec);
break;
}
case ACTION_DELETE_CALENDAREVENT: {
long id = intent.getLongExtra(EXTRA_CALENDAREVENT_ID, -1);
byte type = intent.getByteExtra(EXTRA_CALENDAREVENT_TYPE, (byte) -1);
mDeviceSupport.onDeleteCalendarEvent(type, id);
deviceSupport.onDeleteCalendarEvent(type, id);
break;
}
case ACTION_RESET: {
int flags = intent.getIntExtra(EXTRA_RESET_FLAGS, 0);
mDeviceSupport.onReset(flags);
deviceSupport.onReset(flags);
break;
}
case ACTION_HEARTRATE_TEST: {
mDeviceSupport.onHeartRateTest();
deviceSupport.onHeartRateTest();
break;
}
case ACTION_FETCH_RECORDED_DATA: {
int dataTypes = intent.getIntExtra(EXTRA_RECORDED_DATA_TYPES, 0);
mDeviceSupport.onFetchRecordedData(dataTypes);
break;
}
case ACTION_DISCONNECT: {
mDeviceSupport.dispose();
if (mGBDevice != null) {
mGBDevice.setState(GBDevice.State.NOT_CONNECTED);
mGBDevice.sendDeviceUpdateIntent(this);
if(!getDeviceCoordinator(device).supportsActivityDataFetching()){
break;
}
setReceiversEnableState(false, false, null);
mGBDevice = null;
mDeviceSupport = null;
mCoordinator = null;
int dataTypes = intent.getIntExtra(EXTRA_RECORDED_DATA_TYPES, 0);
deviceSupport.onFetchRecordedData(dataTypes);
break;
}
case ACTION_DISCONNECT:
try {
removeDeviceSupport(device);
} catch (DeviceNotFoundException e) {
e.printStackTrace();
}
device.setState(GBDevice.State.NOT_CONNECTED);
device.sendDeviceUpdateIntent(this);
updateReceiversState();
break;
case ACTION_FIND_DEVICE: {
boolean start = intent.getBooleanExtra(EXTRA_FIND_START, false);
mDeviceSupport.onFindDevice(start);
deviceSupport.onFindDevice(start);
break;
}
case ACTION_SET_CONSTANT_VIBRATION: {
int intensity = intent.getIntExtra(EXTRA_VIBRATION_INTENSITY, 0);
mDeviceSupport.onSetConstantVibration(intensity);
deviceSupport.onSetConstantVibration(intensity);
break;
}
case ACTION_CALLSTATE:
CallSpec callSpec = new CallSpec();
callSpec.command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED);
callSpec.number = intent.getStringExtra(EXTRA_CALL_PHONENUMBER);
callSpec.name = sanitizeNotifText(intent.getStringExtra(EXTRA_CALL_DISPLAYNAME));
callSpec.name = sanitizeNotifText(intent.getStringExtra(EXTRA_CALL_DISPLAYNAME), device);
callSpec.dndSuppressed = intent.getIntExtra(EXTRA_CALL_DNDSUPPRESSED, 0);
mDeviceSupport.onSetCallState(callSpec);
deviceSupport.onSetCallState(callSpec);
break;
case ACTION_SETCANNEDMESSAGES:
int type = intent.getIntExtra(EXTRA_CANNEDMESSAGES_TYPE, -1);
@ -525,24 +704,24 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
CannedMessagesSpec cannedMessagesSpec = new CannedMessagesSpec();
cannedMessagesSpec.type = type;
cannedMessagesSpec.cannedMessages = cannedMessages;
mDeviceSupport.onSetCannedMessages(cannedMessagesSpec);
deviceSupport.onSetCannedMessages(cannedMessagesSpec);
break;
case ACTION_SETTIME:
mDeviceSupport.onSetTime();
deviceSupport.onSetTime();
break;
case ACTION_SETMUSICINFO:
MusicSpec musicSpec = new MusicSpec();
musicSpec.artist = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_ARTIST));
musicSpec.album = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_ALBUM));
musicSpec.track = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_TRACK));
musicSpec.artist = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_ARTIST), device);
musicSpec.album = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_ALBUM), device);
musicSpec.track = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_TRACK), device);
musicSpec.duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0);
musicSpec.trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0);
musicSpec.trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0);
mDeviceSupport.onSetMusicInfo(musicSpec);
deviceSupport.onSetMusicInfo(musicSpec);
break;
case ACTION_SET_PHONE_VOLUME:
float phoneVolume = intent.getFloatExtra(EXTRA_PHONE_VOLUME, 0);
mDeviceSupport.onSetPhoneVolume(phoneVolume);
deviceSupport.onSetPhoneVolume(phoneVolume);
break;
case ACTION_SETMUSICSTATE:
MusicStateSpec stateSpec = new MusicStateSpec();
@ -551,23 +730,23 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
stateSpec.position = intent.getIntExtra(EXTRA_MUSIC_POSITION, 0);
stateSpec.playRate = intent.getIntExtra(EXTRA_MUSIC_RATE, 0);
stateSpec.state = intent.getByteExtra(EXTRA_MUSIC_STATE, (byte) 0);
mDeviceSupport.onSetMusicState(stateSpec);
deviceSupport.onSetMusicState(stateSpec);
break;
case ACTION_REQUEST_APPINFO:
mDeviceSupport.onAppInfoReq();
deviceSupport.onAppInfoReq();
break;
case ACTION_REQUEST_SCREENSHOT:
mDeviceSupport.onScreenshotReq();
deviceSupport.onScreenshotReq();
break;
case ACTION_STARTAPP: {
UUID uuid = (UUID) intent.getSerializableExtra(EXTRA_APP_UUID);
boolean start = intent.getBooleanExtra(EXTRA_APP_START, true);
mDeviceSupport.onAppStart(uuid, start);
deviceSupport.onAppStart(uuid, start);
break;
}
case ACTION_DELETEAPP: {
UUID uuid = (UUID) intent.getSerializableExtra(EXTRA_APP_UUID);
mDeviceSupport.onAppDelete(uuid);
deviceSupport.onAppDelete(uuid);
break;
}
case ACTION_APP_CONFIGURE: {
@ -577,87 +756,87 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
if (intent.hasExtra(EXTRA_APP_CONFIG_ID)) {
id = intent.getIntExtra(EXTRA_APP_CONFIG_ID, 0);
}
mDeviceSupport.onAppConfiguration(uuid, config, id);
deviceSupport.onAppConfiguration(uuid, config, id);
break;
}
case ACTION_APP_REORDER: {
UUID[] uuids = (UUID[]) intent.getSerializableExtra(EXTRA_APP_UUID);
mDeviceSupport.onAppReorder(uuids);
deviceSupport.onAppReorder(uuids);
break;
}
case ACTION_INSTALL:
Uri uri = intent.getParcelableExtra(EXTRA_URI);
if (uri != null) {
LOG.info("will try to install app/fw");
mDeviceSupport.onInstallApp(uri);
deviceSupport.onInstallApp(uri);
}
break;
case ACTION_SET_ALARMS:
ArrayList<? extends Alarm> alarms = (ArrayList<? extends Alarm>) intent.getSerializableExtra(EXTRA_ALARMS);
mDeviceSupport.onSetAlarms(alarms);
deviceSupport.onSetAlarms(alarms);
break;
case ACTION_SET_REMINDERS:
ArrayList<? extends Reminder> reminders = (ArrayList<? extends Reminder>) intent.getSerializableExtra(EXTRA_REMINDERS);
mDeviceSupport.onSetReminders(reminders);
deviceSupport.onSetReminders(reminders);
break;
case ACTION_SET_WORLD_CLOCKS:
ArrayList<? extends WorldClock> clocks = (ArrayList<? extends WorldClock>) intent.getSerializableExtra(EXTRA_WORLD_CLOCKS);
mDeviceSupport.onSetWorldClocks(clocks);
deviceSupport.onSetWorldClocks(clocks);
break;
case ACTION_ENABLE_REALTIME_STEPS: {
boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false);
mDeviceSupport.onEnableRealtimeSteps(enable);
deviceSupport.onEnableRealtimeSteps(enable);
break;
}
case ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT: {
boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false);
mDeviceSupport.onEnableHeartRateSleepSupport(enable);
deviceSupport.onEnableHeartRateSleepSupport(enable);
break;
}
case ACTION_SET_HEARTRATE_MEASUREMENT_INTERVAL: {
int seconds = intent.getIntExtra(EXTRA_INTERVAL_SECONDS, 0);
mDeviceSupport.onSetHeartRateMeasurementInterval(seconds);
deviceSupport.onSetHeartRateMeasurementInterval(seconds);
break;
}
case ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT: {
boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false);
mDeviceSupport.onEnableRealtimeHeartRateMeasurement(enable);
deviceSupport.onEnableRealtimeHeartRateMeasurement(enable);
break;
}
case ACTION_SEND_CONFIGURATION: {
String config = intent.getStringExtra(EXTRA_CONFIG);
mDeviceSupport.onSendConfiguration(config);
deviceSupport.onSendConfiguration(config);
break;
}
case ACTION_READ_CONFIGURATION: {
String config = intent.getStringExtra(EXTRA_CONFIG);
mDeviceSupport.onReadConfiguration(config);
deviceSupport.onReadConfiguration(config);
break;
}
case ACTION_TEST_NEW_FUNCTION: {
mDeviceSupport.onTestNewFunction();
deviceSupport.onTestNewFunction();
break;
}
case ACTION_SEND_WEATHER: {
WeatherSpec weatherSpec = intent.getParcelableExtra(EXTRA_WEATHER);
if (weatherSpec != null) {
mDeviceSupport.onSendWeather(weatherSpec);
deviceSupport.onSendWeather(weatherSpec);
}
break;
}
case ACTION_SET_LED_COLOR:
int color = intent.getIntExtra(EXTRA_LED_COLOR, 0);
if (color != 0) {
mDeviceSupport.onSetLedColor(color);
deviceSupport.onSetLedColor(color);
}
break;
case ACTION_POWER_OFF:
mDeviceSupport.onPowerOff();
deviceSupport.onPowerOff();
break;
case ACTION_SET_FM_FREQUENCY:
float frequency = intent.getFloatExtra(EXTRA_FM_FREQUENCY, -1);
if (frequency != -1) {
mDeviceSupport.onSetFmFrequency(frequency);
deviceSupport.onSetFmFrequency(frequency);
}
break;
case ACTION_SET_GPS_LOCATION:
@ -671,18 +850,79 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
* Disposes the current DeviceSupport instance (if any) and sets a new device support instance
* (if not null).
*
* @param deviceSupport
* @param deviceSupport deviceSupport to reokace/add
*/
private void setDeviceSupport(@Nullable DeviceSupport deviceSupport) {
if (deviceSupport != mDeviceSupport && mDeviceSupport != null) {
mDeviceSupport.dispose();
mDeviceSupport = null;
mGBDevice = null;
mCoordinator = null;
private void setDeviceSupport(GBDevice device, DeviceSupport deviceSupport) throws DeviceNotFoundException {
DeviceStruct deviceStruct = getDeviceStruct(device);
DeviceSupport cachedDeviceSupport = deviceStruct.getDeviceSupport();
if (deviceSupport != cachedDeviceSupport && cachedDeviceSupport != null) {
cachedDeviceSupport.dispose();
}
deviceStruct.setDeviceSupport(deviceSupport);
}
private void removeDeviceSupport(GBDevice device) throws DeviceNotFoundException {
DeviceStruct struct = getDeviceStruct(device);
if(struct.getDeviceSupport() != null){
struct.getDeviceSupport().dispose();
}
mDeviceSupport = deviceSupport;
mGBDevice = mDeviceSupport != null ? mDeviceSupport.getDevice() : null;
mCoordinator = mGBDevice != null ? DeviceHelper.getInstance().getCoordinator(mGBDevice) : null;
struct.setDeviceSupport(null);
}
private DeviceStruct getDeviceStructOrNull(GBDevice device){
DeviceStruct deviceStruct = null;
try {
deviceStruct = getDeviceStruct(device);
} catch (DeviceNotFoundException e) {
e.printStackTrace();
}
return deviceStruct;
}
public DeviceStruct getDeviceStruct(GBDevice device) throws DeviceNotFoundException {
if(device == null){
throw new DeviceNotFoundException("null");
}
for(DeviceStruct struct : deviceStructs){
if(struct.getDevice().equals(device)){
return struct;
}
}
throw new DeviceNotFoundException(device);
}
public GBDevice getDeviceByAddress(String deviceAddress) throws DeviceNotFoundException {
if(deviceAddress == null){
throw new DeviceNotFoundException(deviceAddress);
}
for(DeviceStruct struct : deviceStructs){
if(struct.getDevice().getAddress().equals(deviceAddress)){
return struct.getDevice();
}
}
throw new DeviceNotFoundException(deviceAddress);
}
public GBDevice getDeviceByAddressOrNull(String deviceAddress){
GBDevice device = null;
try {
device = getDeviceByAddress(deviceAddress);
} catch (DeviceNotFoundException e) {
e.printStackTrace();
}
return device;
}
private DeviceSupport getDeviceSupport(GBDevice device) throws DeviceNotFoundException {
if(device == null){
throw new DeviceNotFoundException("null");
}
for(DeviceStruct struct : deviceStructs){
if(struct.getDevice().equals(device)){
return struct.getDeviceSupport();
}
}
throw new DeviceNotFoundException(device);
}
private void start() {
@ -697,30 +937,49 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
return mStarted;
}
private boolean isConnected() {
return mGBDevice != null && mGBDevice.isConnected();
private boolean isDeviceConnected(GBDevice device) {
for(DeviceStruct struct : deviceStructs){
if(struct.getDevice().equals(device) ){
return struct.getDevice().isConnected();
}
}
return false;
}
private boolean isConnecting() {
return mGBDevice != null && mGBDevice.isConnecting();
private boolean isDeviceConnecting(GBDevice device) {
for(DeviceStruct struct : deviceStructs){
if(struct.getDevice().equals(device) ){
return struct.getDevice().isConnecting();
}
}
return false;
}
private boolean isInitialized() {
return mGBDevice != null && mGBDevice.isInitialized();
private boolean isDeviceInitialized(GBDevice device) {
for(DeviceStruct struct : deviceStructs){
if(struct.getDevice().equals(device) ){
return struct.getDevice().isInitialized();
}
}
return false;
}
private void setReceiversEnableState(boolean enable, boolean initialized, DeviceCoordinator coordinator) {
private void setReceiversEnableState(boolean enable, boolean initialized, FeatureSet features) {
LOG.info("Setting broadcast receivers to: " + enable);
if (enable && initialized && coordinator != null && coordinator.supportsCalendarEvents()) {
if(enable && features == null){
throw new RuntimeException("features cannot be null when enabling receivers");
}
if (enable && initialized && features.supportsCalendarEvents()) {
if (mCalendarReceiver == null && getPrefs().getBoolean("enable_calendar_sync", true)) {
if (!(GBApplication.isRunningMarshmallowOrLater() && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)) {
IntentFilter calendarIntentFilter = new IntentFilter();
calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED");
calendarIntentFilter.addDataScheme("content");
calendarIntentFilter.addDataAuthority("com.android.calendar", null);
mCalendarReceiver = new CalendarReceiver(mGBDevice);
mCalendarReceiver = new CalendarReceiver(null);
registerReceiver(mCalendarReceiver, calendarIntentFilter);
}
}
@ -756,7 +1015,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
mPebbleReceiver = new PebbleReceiver();
registerReceiver(mPebbleReceiver, new IntentFilter("com.getpebble.action.SEND_NOTIFICATION"));
}
if (mMusicPlaybackReceiver == null && coordinator != null && coordinator.supportsMusicInfo()) {
if (mMusicPlaybackReceiver == null && features.supportsMusicInfo()) {
mMusicPlaybackReceiver = new MusicPlaybackReceiver();
IntentFilter filter = new IntentFilter();
for (String action : mMusicActions) {
@ -771,10 +1030,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
filter.addAction("android.intent.action.TIMEZONE_CHANGED");
registerReceiver(mTimeChangeReceiver, filter);
}
if (mBlueToothConnectReceiver == null) {
mBlueToothConnectReceiver = new BluetoothConnectReceiver(this);
registerReceiver(mBlueToothConnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));
}
if (mBlueToothPairingRequestReceiver == null) {
mBlueToothPairingRequestReceiver = new BluetoothPairingRequestReceiver(this);
registerReceiver(mBlueToothPairingRequestReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST));
@ -790,7 +1045,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
}
// Weather receivers
if ( coordinator != null && coordinator.supportsWeather()) {
if (features.supportsWeather()) {
if (GBApplication.isRunningOreoOrLater()) {
if (mLineageOsWeatherReceiver == null) {
mLineageOsWeatherReceiver = new LineageOsWeatherReceiver();
@ -818,7 +1073,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
}
if (GBApplication.getPrefs().getBoolean("auto_fetch_enabled", false) &&
coordinator != null && coordinator.supportsActivityDataFetching() && mGBAutoFetchReceiver == null) {
features.supportsActivityDataFetching() && mGBAutoFetchReceiver == null) {
mGBAutoFetchReceiver = new GBAutoFetchReceiver();
registerReceiver(mGBAutoFetchReceiver, new IntentFilter("android.intent.action.USER_PRESENT"));
}
@ -847,10 +1102,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
unregisterReceiver(mTimeChangeReceiver);
mTimeChangeReceiver = null;
}
if (mBlueToothConnectReceiver != null) {
unregisterReceiver(mBlueToothConnectReceiver);
mBlueToothConnectReceiver = null;
}
if (mBlueToothPairingRequestReceiver != null) {
unregisterReceiver(mBlueToothPairingRequestReceiver);
@ -900,7 +1151,15 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
setReceiversEnableState(false, false, null); // disable BroadcastReceivers
setDeviceSupport(null);
unregisterReceiver(mBlueToothConnectReceiver);
for(GBDevice device : getGBDevices()){
try {
removeDeviceSupport(device);
} catch (DeviceNotFoundException e) {
e.printStackTrace();
}
}
GB.removeNotification(GB.NOTIFICATION_ID, this); // need to do this because the updated notification won't be cancelled when service stops
}
@ -911,10 +1170,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (GBPrefs.AUTO_RECONNECT.equals(key)) {
boolean autoReconnect = getGBPrefs().getAutoReconnect();
if (mDeviceSupport != null) {
mDeviceSupport.setAutoReconnect(autoReconnect);
if (GBPrefs.DEVICE_AUTO_RECONNECT.equals(key)) {
for(DeviceStruct deviceStruct : deviceStructs){
boolean autoReconnect = getGBPrefs().getAutoReconnect(deviceStruct.getDevice());
deviceStruct.getDeviceSupport().setAutoReconnect(autoReconnect);
}
}
if (GBPrefs.CHART_MAX_HEART_RATE.equals(key) || GBPrefs.CHART_MIN_HEART_RATE.equals(key)) {
@ -934,7 +1193,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
return GBApplication.getGBPrefs();
}
public GBDevice getGBDevice() {
return mGBDevice;
public GBDevice[] getGBDevices() {
GBDevice[] devices = new GBDevice[deviceStructs.size()];
for(int i = 0; i < devices.length; i++){
devices[i] = deviceStructs.get(i).getDevice();
}
return devices;
}
}

View File

@ -161,248 +161,170 @@ public class DeviceSupportFactory {
}
}
private ServiceDeviceSupport createServiceDeviceSupport(GBDevice device){
switch (device.getType()) {
case PEBBLE:
return new ServiceDeviceSupport(new PebbleSupport());
case MIBAND:
return new ServiceDeviceSupport(new MiBandSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case MIBAND2:
return new ServiceDeviceSupport(new HuamiSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case MIBAND3:
return new ServiceDeviceSupport(new MiBand3Support());
case MIBAND4:
return new ServiceDeviceSupport(new MiBand4Support());
case MIBAND5:
return new ServiceDeviceSupport(new MiBand5Support());
case MIBAND6:
return new ServiceDeviceSupport(new MiBand6Support());
case AMAZFITBIP:
return new ServiceDeviceSupport(new AmazfitBipSupport());
case AMAZFITBIP_LITE:
return new ServiceDeviceSupport(new AmazfitBipLiteSupport());
case AMAZFITBIPS:
return new ServiceDeviceSupport(new AmazfitBipSSupport());
case AMAZFITBIPS_LITE:
return new ServiceDeviceSupport(new AmazfitBipSLiteSupport());
case AMAZFITBIPU:
return new ServiceDeviceSupport(new AmazfitBipUSupport());
case AMAZFITBIPUPRO:
return new ServiceDeviceSupport(new AmazfitBipUProSupport());
case AMAZFITPOP:
return new ServiceDeviceSupport(new AmazfitPopSupport());
case AMAZFITPOPPRO:
return new ServiceDeviceSupport(new AmazfitPopProSupport());
case AMAZFITGTR:
return new ServiceDeviceSupport(new AmazfitGTRSupport());
case AMAZFITGTR_LITE:
return new ServiceDeviceSupport(new AmazfitGTRLiteSupport());
case AMAZFITGTR2:
return new ServiceDeviceSupport(new AmazfitGTR2Support());
case ZEPP_E:
return new ServiceDeviceSupport(new ZeppESupport());
case AMAZFITGTR2E:
return new ServiceDeviceSupport(new AmazfitGTR2eSupport());
case AMAZFITTREX:
return new ServiceDeviceSupport(new AmazfitTRexSupport());
case AMAZFITTREXPRO:
return new ServiceDeviceSupport(new AmazfitTRexProSupport());
case AMAZFITGTS:
return new ServiceDeviceSupport(new AmazfitGTSSupport());
case AMAZFITVERGEL:
return new ServiceDeviceSupport(new AmazfitVergeLSupport());
case AMAZFITGTS2:
return new ServiceDeviceSupport(new AmazfitGTS2Support());
case AMAZFITGTS2_MINI:
return new ServiceDeviceSupport(new AmazfitGTS2MiniSupport());
case AMAZFITGTS2E:
return new ServiceDeviceSupport(new AmazfitGTS2eSupport());
case AMAZFITCOR:
return new ServiceDeviceSupport(new AmazfitCorSupport());
case AMAZFITCOR2:
return new ServiceDeviceSupport(new AmazfitCor2Support());
case AMAZFITBAND5:
return new ServiceDeviceSupport(new AmazfitBand5Support());
case AMAZFITX:
return new ServiceDeviceSupport(new AmazfitXSupport());
case AMAZFITNEO:
return new ServiceDeviceSupport(new AmazfitNeoSupport());
case VIBRATISSIMO:
return new ServiceDeviceSupport(new VibratissimoSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case LIVEVIEW:
return new ServiceDeviceSupport(new LiveviewSupport());
case HPLUS:
case MAKIBESF68:
case EXRIZUK8:
case Q8:
return new ServiceDeviceSupport(new HPlusSupport(device.getType()));
case NO1F1:
return new ServiceDeviceSupport(new No1F1Support());
case TECLASTH30:
return new ServiceDeviceSupport(new TeclastH30Support());
case XWATCH:
return new ServiceDeviceSupport(new XWatchSupport());
case FOSSILQHYBRID:
return new ServiceDeviceSupport(new QHybridSupport());
case ZETIME:
return new ServiceDeviceSupport(new ZeTimeDeviceSupport());
case ID115:
return new ServiceDeviceSupport(new ID115Support());
case WATCH9:
return new ServiceDeviceSupport(new Watch9DeviceSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case WATCHXPLUS:
return new ServiceDeviceSupport(new WatchXPlusDeviceSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case ROIDMI:
return new ServiceDeviceSupport(new RoidmiSupport());
case ROIDMI3:
return new ServiceDeviceSupport(new RoidmiSupport());
case Y5:
return new ServiceDeviceSupport(new Y5Support());
case CASIOGB6900:
return new ServiceDeviceSupport(new CasioGB6900DeviceSupport());
case CASIOGBX100:
return new ServiceDeviceSupport(new CasioGBX100DeviceSupport());
case MISCALE2:
return new ServiceDeviceSupport(new MiScale2DeviceSupport());
case BFH16:
return new ServiceDeviceSupport(new BFH16DeviceSupport());
case MIJIA_LYWSD02:
return new ServiceDeviceSupport(new MijiaLywsd02Support());
case MAKIBESHR3:
return new ServiceDeviceSupport(new MakibesHR3DeviceSupport());
case ITAG:
return new ServiceDeviceSupport(new ITagSupport());
case NUTMINI:
return new ServiceDeviceSupport(new NutSupport());
case BANGLEJS:
return new ServiceDeviceSupport(new BangleJSDeviceSupport());
case TLW64:
return new ServiceDeviceSupport(new TLW64Support());
case PINETIME_JF:
return new ServiceDeviceSupport(new PineTimeJFSupport());
case SG2:
return new ServiceDeviceSupport(new HPlusSupport(DeviceType.SG2));
case LEFUN:
return new ServiceDeviceSupport(new LefunDeviceSupport());
case SONY_SWR12:
return new ServiceDeviceSupport(new SonySWR12DeviceSupport());
case WASPOS:
return new ServiceDeviceSupport(new WaspOSDeviceSupport());
case SMAQ2OSS:
return new ServiceDeviceSupport(new SMAQ2OSSSupport());
case UM25:
return new ServiceDeviceSupport(new UM25Support());
case DOMYOS_T540:
return new ServiceDeviceSupport(new DomyosT540Support(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case FITPRO:
return new ServiceDeviceSupport(new FitProDeviceSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case NOTHING_EAR1:
return new ServiceDeviceSupport(new Ear1Support());
case GALAXY_BUDS:
return new ServiceDeviceSupport(new GalaxyBudsDeviceSupport());
case GALAXY_BUDS_LIVE:
return new ServiceDeviceSupport(new GalaxyBudsDeviceSupport());
case GALAXY_BUDS_PRO:
return new ServiceDeviceSupport(new GalaxyBudsDeviceSupport(), ServiceDeviceSupport.Flags.BUSY_CHECKING);
case SONY_WH_1000XM3:
return new ServiceDeviceSupport(new SonyHeadphonesSupport());
case SONY_WH_1000XM4:
return new ServiceDeviceSupport(new SonyHeadphonesSupport());
case SONY_WF_SP800N:
return new ServiceDeviceSupport(new SonyHeadphonesSupport(), ServiceDeviceSupport.Flags.BUSY_CHECKING);
case SONY_WF_1000XM3:
return new ServiceDeviceSupport(new SonyHeadphonesSupport());
case VESC_NRF:
case VESC_HM10:
return new ServiceDeviceSupport(new VescDeviceSupport(device.getType()));
case BOSE_QC35:
return new ServiceDeviceSupport(new QC35BaseSupport());
}
return null;
}
private DeviceSupport createBTDeviceSupport(GBDevice gbDevice) throws GBException {
if (mBtAdapter != null && mBtAdapter.isEnabled()) {
DeviceSupport deviceSupport = null;
try {
switch (gbDevice.getType()) {
case PEBBLE:
deviceSupport = new ServiceDeviceSupport(new PebbleSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MIBAND:
deviceSupport = new ServiceDeviceSupport(new MiBandSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MIBAND2:
deviceSupport = new ServiceDeviceSupport(new HuamiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MIBAND3:
deviceSupport = new ServiceDeviceSupport(new MiBand3Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MIBAND4:
deviceSupport = new ServiceDeviceSupport(new MiBand4Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MIBAND5:
deviceSupport = new ServiceDeviceSupport(new MiBand5Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MIBAND6:
deviceSupport = new ServiceDeviceSupport(new MiBand6Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITBIP:
deviceSupport = new ServiceDeviceSupport(new AmazfitBipSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITBIP_LITE:
deviceSupport = new ServiceDeviceSupport(new AmazfitBipLiteSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITBIPS:
deviceSupport = new ServiceDeviceSupport(new AmazfitBipSSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITBIPS_LITE:
deviceSupport = new ServiceDeviceSupport(new AmazfitBipSLiteSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITBIPU:
deviceSupport = new ServiceDeviceSupport(new AmazfitBipUSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITBIPUPRO:
deviceSupport = new ServiceDeviceSupport(new AmazfitBipUProSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITPOP:
deviceSupport = new ServiceDeviceSupport(new AmazfitPopSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITPOPPRO:
deviceSupport = new ServiceDeviceSupport(new AmazfitPopProSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTR:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTRSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTR_LITE:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTRLiteSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTR2:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTR2Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case ZEPP_E:
deviceSupport = new ServiceDeviceSupport(new ZeppESupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTR2E:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTR2eSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITTREX:
deviceSupport = new ServiceDeviceSupport(new AmazfitTRexSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITTREXPRO:
deviceSupport = new ServiceDeviceSupport(new AmazfitTRexProSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTS:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTSSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITVERGEL:
deviceSupport = new ServiceDeviceSupport(new AmazfitVergeLSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTS2:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTS2Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTS2_MINI:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTS2MiniSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTS2E:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTS2eSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITCOR:
deviceSupport = new ServiceDeviceSupport(new AmazfitCorSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITCOR2:
deviceSupport = new ServiceDeviceSupport(new AmazfitCor2Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITBAND5:
deviceSupport = new ServiceDeviceSupport(new AmazfitBand5Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITX:
deviceSupport = new ServiceDeviceSupport(new AmazfitXSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITNEO:
deviceSupport = new ServiceDeviceSupport(new AmazfitNeoSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case VIBRATISSIMO:
deviceSupport = new ServiceDeviceSupport(new VibratissimoSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case LIVEVIEW:
deviceSupport = new ServiceDeviceSupport(new LiveviewSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case HPLUS:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.HPLUS), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MAKIBESF68:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.MAKIBESF68), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case EXRIZUK8:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.EXRIZUK8), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case Q8:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.Q8), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case NO1F1:
deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case TECLASTH30:
deviceSupport = new ServiceDeviceSupport(new TeclastH30Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case XWATCH:
deviceSupport = new ServiceDeviceSupport(new XWatchSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case FOSSILQHYBRID:
deviceSupport = new ServiceDeviceSupport(new QHybridSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case ZETIME:
deviceSupport = new ServiceDeviceSupport(new ZeTimeDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case ID115:
deviceSupport = new ServiceDeviceSupport(new ID115Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case WATCH9:
deviceSupport = new ServiceDeviceSupport(new Watch9DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case WATCHXPLUS:
deviceSupport = new ServiceDeviceSupport(new WatchXPlusDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case ROIDMI:
deviceSupport = new ServiceDeviceSupport(new RoidmiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case ROIDMI3:
deviceSupport = new ServiceDeviceSupport(new RoidmiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case Y5:
deviceSupport = new ServiceDeviceSupport(new Y5Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case CASIOGB6900:
deviceSupport = new ServiceDeviceSupport(new CasioGB6900DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case CASIOGBX100:
deviceSupport = new ServiceDeviceSupport(new CasioGBX100DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MISCALE2:
deviceSupport = new ServiceDeviceSupport(new MiScale2DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case BFH16:
deviceSupport = new ServiceDeviceSupport(new BFH16DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MIJIA_LYWSD02:
deviceSupport = new ServiceDeviceSupport(new MijiaLywsd02Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case MAKIBESHR3:
deviceSupport = new ServiceDeviceSupport(new MakibesHR3DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case ITAG:
deviceSupport = new ServiceDeviceSupport(new ITagSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case NUTMINI:
deviceSupport = new ServiceDeviceSupport(new NutSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case BANGLEJS:
deviceSupport = new ServiceDeviceSupport(new BangleJSDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case TLW64:
deviceSupport = new ServiceDeviceSupport(new TLW64Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case PINETIME_JF:
deviceSupport = new ServiceDeviceSupport(new PineTimeJFSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SG2:
deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.SG2), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case LEFUN:
deviceSupport = new ServiceDeviceSupport(new LefunDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SONY_SWR12:
deviceSupport = new ServiceDeviceSupport(new SonySWR12DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case WASPOS:
deviceSupport = new ServiceDeviceSupport(new WaspOSDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SMAQ2OSS:
deviceSupport = new ServiceDeviceSupport(new SMAQ2OSSSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case UM25:
deviceSupport = new ServiceDeviceSupport(new UM25Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case DOMYOS_T540:
deviceSupport = new ServiceDeviceSupport(new DomyosT540Support(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case FITPRO:
deviceSupport = new ServiceDeviceSupport(new FitProDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case NOTHING_EAR1:
deviceSupport = new ServiceDeviceSupport(new Ear1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case GALAXY_BUDS:
deviceSupport = new ServiceDeviceSupport(new GalaxyBudsDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case GALAXY_BUDS_LIVE:
deviceSupport = new ServiceDeviceSupport(new GalaxyBudsDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case GALAXY_BUDS_PRO:
deviceSupport = new ServiceDeviceSupport(new GalaxyBudsDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SONY_WH_1000XM3:
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SONY_WH_1000XM4:
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SONY_WF_SP800N:
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SONY_WF_1000XM3:
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case VESC_NRF:
case VESC_HM10:
deviceSupport = new ServiceDeviceSupport(new VescDeviceSupport(gbDevice.getType()), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case BOSE_QC35:
deviceSupport = new ServiceDeviceSupport(new QC35BaseSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
}
DeviceSupport deviceSupport = createServiceDeviceSupport(gbDevice);
if (deviceSupport != null) {
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);
return deviceSupport;
@ -416,7 +338,7 @@ public class DeviceSupportFactory {
private DeviceSupport createTCPDeviceSupport(GBDevice gbDevice) throws GBException {
try {
DeviceSupport deviceSupport = new ServiceDeviceSupport(new PebbleSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
DeviceSupport deviceSupport = new ServiceDeviceSupport(new PebbleSupport(), ServiceDeviceSupport.Flags.BUSY_CHECKING);
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);
return deviceSupport;
} catch (Exception e) {

View File

@ -27,6 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.UUID;
@ -61,9 +62,14 @@ public class ServiceDeviceSupport implements DeviceSupport {
private String lastNotificationKind;
private final EnumSet<Flags> flags;
public ServiceDeviceSupport(DeviceSupport delegate, EnumSet<Flags> flags) {
public ServiceDeviceSupport(DeviceSupport delegate, Flags... flags) {
this.delegate = delegate;
this.flags = flags;
this.flags = EnumSet.noneOf(Flags.class);
this.flags.addAll(Arrays.asList(flags));
}
public ServiceDeviceSupport(DeviceSupport delegate){
this(delegate, Flags.BUSY_CHECKING);
}
@Override

View File

@ -17,6 +17,7 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@ -69,8 +70,8 @@ public class AmazfitBipLiteFirmwareInfo extends HuamiFirmwareInfo {
if (searchString32BitAligned(bytes, "Amazfit Bip Lite")) {
return HuamiFirmwareType.FIRMWARE;
}
GBDevice device = GBApplication.app().getDeviceManager().getSelectedDevice();
if (device != null) {
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()));
if (prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_RELAX_FIRMWARE_CHECKS, false)) {
if (searchString32BitAligned(bytes, "Amazfit Bip")) {

View File

@ -17,6 +17,7 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbips;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@ -66,10 +67,8 @@ public class AmazfitBipSFirmwareInfo extends HuamiFirmwareInfo {
@Override
protected HuamiFirmwareType determineFirmwareType(byte[] bytes) {
GBDevice device = GBApplication.app().getDeviceManager().getSelectedDevice();
if (device != null) {
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for (GBDevice device : devices) {
if (device.getFirmwareVersion().startsWith("2.")) {
//For devices on firmware 2.x it is a tonleasp device and needs a header which looks like Mi Band 4
if (ArrayUtils.equals(bytes, MiBand4FirmwareInfo.FW_HEADER, MiBand4FirmwareInfo.FW_HEADER_OFFSET)) {

View File

@ -242,7 +242,7 @@ class PebbleIoThread extends GBDeviceIoThread {
public void run() {
mIsConnected = connect();
if (!mIsConnected) {
if (GBApplication.getGBPrefs().getAutoReconnect() && !mQuit) {
if (GBApplication.getGBPrefs().getAutoReconnect(getDevice()) && !mQuit) {
gbDevice.setState(GBDevice.State.WAITING_FOR_RECONNECT);
gbDevice.sendDeviceUpdateIntent(getContext());
}
@ -406,7 +406,7 @@ class PebbleIoThread extends GBDeviceIoThread {
enablePebbleKitSupport(false);
if (mQuit || !GBApplication.getGBPrefs().getAutoReconnect()) {
if (mQuit || !GBApplication.getGBPrefs().getAutoReconnect(getDevice())) {
gbDevice.setState(GBDevice.State.NOT_CONNECTED);
} else {
gbDevice.setState(GBDevice.State.WAITING_FOR_RECONNECT);

View File

@ -69,7 +69,7 @@ public class QC35BaseSupport extends AbstractSerialDeviceSupport {
public boolean connect() {
getDeviceProtocol();
getDeviceIOThread().start();
getDevice().setBatteryThresholdPercent((short)15);
getDevice().setBatteryThresholdPercent((short)25);
return true;
}

View File

@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
@ -57,6 +58,7 @@ public class QC35Protocol extends GBDeviceProtocol {
if(third == 0x03){
GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo();
batteryInfo.level = data[0];
batteryInfo.state = BatteryState.BATTERY_NORMAL;
events.add(batteryInfo);
}
}

View File

@ -58,24 +58,31 @@ public class AutoConnectIntervalReceiver extends BroadcastReceiver {
return;
}
GBDevice gbDevice = service.getGBDevice();
if (gbDevice == null) {
return;
}
if (action.equals(DeviceManager.ACTION_DEVICES_CHANGED)) {
if (gbDevice.isInitialized()) {
LOG.info("will reset connection delay, device is initialized!");
mDelay = 4;
GBDevice[] devices = service.getGBDevices();
if (action.equals(DeviceManager.ACTION_DEVICES_CHANGED)){
boolean scheduleAutoConnect = false;
boolean allDevicesInitialized = true;
for(GBDevice device : devices){
if(!device.isInitialized()){
allDevicesInitialized = false;
}else if(device.getState() == GBDevice.State.WAITING_FOR_RECONNECT){
scheduleAutoConnect = true;
}
}
else if (gbDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
if(allDevicesInitialized){
LOG.info("will reset connection delay, all devices are initialized!");
return;
}
if(scheduleAutoConnect){
scheduleReconnect();
}
}
else if (action.equals("GB_RECONNECT")) {
if (gbDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
LOG.info("Will re-connect to " + gbDevice.getAddress() + "(" + gbDevice.getName() + ")");
GBApplication.deviceService().connect();
}else if (action.equals("GB_RECONNECT")){
for(GBDevice device : devices){
if(device.getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
LOG.info("Will re-connect to " + device.getAddress() + "(" + device.getName() + ")");
GBApplication.deviceService().connect(device);
}
}
}
}

View File

@ -180,7 +180,7 @@ public class BondingUtil {
public static void connectThenComplete(BondingInterface bondingInterface, GBDevice device) {
toast(bondingInterface.getContext(), bondingInterface.getContext().getString(R.string.discovery_trying_to_connect_to, device.getName()), Toast.LENGTH_SHORT, GB.INFO);
// Disconnect when LE Pebble so that the user can manually initiate a connection
GBApplication.deviceService().disconnect();
GBApplication.deviceService().disconnect(device);
GBApplication.deviceService().connect(device, true);
bondingInterface.onBondingComplete(true);
}

View File

@ -29,6 +29,8 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.text.SpannableString;
import android.widget.Toast;
import androidx.annotation.NonNull;
@ -44,6 +46,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
@ -145,44 +148,102 @@ public class GB {
return pendingIntent;
}
public static Notification createNotification(GBDevice device, Context context) {
String deviceName = device.getAliasOrName();
String text = device.getStateString();
if (device.getBatteryLevel() != GBDevice.BATTERY_UNKNOWN) {
text += ": " + context.getString(R.string.battery) + " " + device.getBatteryLevel() + "%";
}
boolean connected = device.isInitialized();
public static Notification createNotification(List<GBDevice> devices, Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
builder.setContentTitle(deviceName)
.setTicker(deviceName + " - " + text)
.setContentText(text)
.setSmallIcon(connected ? device.getNotificationIconConnected() : device.getNotificationIconDisconnected())
.setContentIntent(getContentIntent(context))
.setShowWhen(false)
.setOngoing(true);
if(devices.size() == 0){
builder.setContentTitle(context.getString(R.string.info_no_devices_connected))
.setSmallIcon(R.drawable.ic_notification_disconnected)
.setContentIntent(getContentIntent(context))
.setShowWhen(false)
.setOngoing(true);
if (!GBApplication.isRunningTwelveOrLater()) {
builder.setColor(context.getResources().getColor(R.color.accent));
}
if (!GBApplication.isRunningTwelveOrLater()) {
builder.setColor(context.getResources().getColor(R.color.accent));
}
}else if(devices.size() == 1) {
GBDevice device = devices.get(0);
String deviceName = device.getAliasOrName();
String text = device.getStateString();
if (device.getBatteryLevel() != GBDevice.BATTERY_UNKNOWN) {
text += ": " + context.getString(R.string.battery) + " " + device.getBatteryLevel() + "%";
}
Intent deviceCommunicationServiceIntent = new Intent(context, DeviceCommunicationService.class);
if (connected) {
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_DISCONNECT);
PendingIntent disconnectPendingIntent = PendingIntent.getService(context, 0, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
builder.addAction(R.drawable.ic_notification_disconnected, context.getString(R.string.controlcenter_disconnect), disconnectPendingIntent);
if (GBApplication.isRunningLollipopOrLater() && DeviceHelper.getInstance().getCoordinator(device).supportsActivityDataFetching()) { //for some reason this fails on KK
boolean connected = device.isInitialized();
builder.setContentTitle(deviceName)
.setTicker(deviceName + " - " + text)
.setContentText(text)
.setSmallIcon(connected ? device.getNotificationIconConnected() : device.getNotificationIconDisconnected())
.setContentIntent(getContentIntent(context))
.setShowWhen(false)
.setOngoing(true);
if (!GBApplication.isRunningTwelveOrLater()) {
builder.setColor(context.getResources().getColor(R.color.accent));
}
Intent deviceCommunicationServiceIntent = new Intent(context, DeviceCommunicationService.class);
if (connected) {
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_DISCONNECT);
PendingIntent disconnectPendingIntent = PendingIntent.getService(context, 0, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
builder.addAction(R.drawable.ic_notification_disconnected, context.getString(R.string.controlcenter_disconnect), disconnectPendingIntent);
if (GBApplication.isRunningLollipopOrLater() && DeviceHelper.getInstance().getCoordinator(device).supportsActivityDataFetching()) { //for some reason this fails on KK
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_FETCH_RECORDED_DATA);
deviceCommunicationServiceIntent.putExtra(EXTRA_RECORDED_DATA_TYPES, ActivityKind.TYPE_ACTIVITY);
PendingIntent fetchPendingIntent = PendingIntent.getService(context, 1, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
builder.addAction(R.drawable.ic_refresh, context.getString(R.string.controlcenter_fetch_activity_data), fetchPendingIntent);
}
} else if (device.getState().equals(GBDevice.State.WAITING_FOR_RECONNECT) || device.getState().equals(GBDevice.State.NOT_CONNECTED)) {
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_CONNECT);
deviceCommunicationServiceIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
PendingIntent reconnectPendingIntent = PendingIntent.getService(context, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.addAction(R.drawable.ic_notification, context.getString(R.string.controlcenter_connect), reconnectPendingIntent);
}
}else{
StringBuilder contentText = new StringBuilder();
boolean isConnected = true;
boolean anyDeviceSupportesActivityDataFetching = false;
for(GBDevice device : devices){
if(!device.isInitialized()){
isConnected = false;
}
anyDeviceSupportesActivityDataFetching |= DeviceHelper.getInstance().getCoordinator(device).supportsActivityDataFetching();
String deviceName = device.getAliasOrName();
String text = device.getStateString();
if (device.getBatteryLevel() != GBDevice.BATTERY_UNKNOWN) {
text += ": " + context.getString(R.string.battery) + " " + device.getBatteryLevel() + "%";
}
contentText.append(deviceName).append(" (").append(text).append(")<br>");
}
SpannableString formated = new SpannableString(
Html.fromHtml(contentText.substring(0, contentText.length() - 4)) // cut away last <br>
);
String title = context.getString(R.string.info_connected_count, devices.size());
builder.setContentTitle(title)
.setContentText(formated)
.setSmallIcon(isConnected ? R.drawable.ic_notification : R.drawable.ic_notification_disconnected)
.setContentIntent(getContentIntent(context))
.setStyle(new NotificationCompat.BigTextStyle().bigText(formated).setBigContentTitle(title))
.setShowWhen(false)
.setOngoing(true);
if (!GBApplication.isRunningTwelveOrLater()) {
builder.setColor(context.getResources().getColor(R.color.accent));
}
if (GBApplication.isRunningLollipopOrLater() && anyDeviceSupportesActivityDataFetching) { //for some reason this fails on KK
Intent deviceCommunicationServiceIntent = new Intent(context, DeviceCommunicationService.class);
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_FETCH_RECORDED_DATA);
deviceCommunicationServiceIntent.putExtra(EXTRA_RECORDED_DATA_TYPES, ActivityKind.TYPE_ACTIVITY);
PendingIntent fetchPendingIntent = PendingIntent.getService(context, 1, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
builder.addAction(R.drawable.ic_refresh, context.getString(R.string.controlcenter_fetch_activity_data), fetchPendingIntent);
}
} else if (device.getState().equals(GBDevice.State.WAITING_FOR_RECONNECT) || device.getState().equals(GBDevice.State.NOT_CONNECTED)) {
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_CONNECT);
deviceCommunicationServiceIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
PendingIntent reconnectPendingIntent = PendingIntent.getService(context, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.addAction(R.drawable.ic_notification, context.getString(R.string.controlcenter_connect), reconnectPendingIntent);
}
if (GBApplication.isRunningLollipopOrLater()) {
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
@ -220,8 +281,8 @@ public class GB {
return builder.build();
}
public static void updateNotification(GBDevice device, Context context) {
Notification notification = createNotification(device, context);
public static void updateNotification(List<GBDevice> devices, Context context) {
Notification notification = createNotification(devices, context);
notify(NOTIFICATION_ID, notification, context);
}

View File

@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.util;
import android.Manifest;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
@ -33,6 +34,7 @@ import java.util.Date;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public class GBPrefs {
// Since this class must not log to slf4j, we use plain android.util.Log
@ -41,7 +43,8 @@ public class GBPrefs {
public static final String PACKAGE_BLACKLIST = "package_blacklist";
public static final String PACKAGE_PEBBLEMSG_BLACKLIST = "package_pebblemsg_blacklist";
public static final String CALENDAR_BLACKLIST = "calendar_blacklist";
public static final String AUTO_RECONNECT = "general_autocreconnect";
public static final String DEVICE_AUTO_RECONNECT = "prefs_key_device_auto_reconnect";
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";
public static final String AUTO_EXPORT_LOCATION = "auto_export_location";
@ -67,8 +70,9 @@ public class GBPrefs {
mPrefs = prefs;
}
public boolean getAutoReconnect() {
return mPrefs.getBoolean(AUTO_RECONNECT, AUTO_RECONNECT_DEFAULT);
public boolean getAutoReconnect(GBDevice device) {
SharedPreferences deviceSpecificPreferences = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress());
return deviceSpecificPreferences.getBoolean(DEVICE_AUTO_RECONNECT, AUTO_RECONNECT_DEFAULT);
}
public boolean getAutoStart() {

View File

@ -1628,4 +1628,7 @@
<string name="about_activity_title_banglejs_nopebble">Über Bangle.js Gadgetbridge</string>
<string name="pref_device_action_fitness_app_control_toggle">Fitness-App-Tracking umschalten</string>
<string name="pref_title_banglejs_text_bitmap">Text als Bitmaps</string>
<string name="info_connected_count">%d Geräte verbunden</string>
<string name="info_no_devices_connected">Keine Geräte verbunden</string>
</resources>

View File

@ -1666,4 +1666,7 @@
<string name="pref_summary_opentracks_packagename">Used for starting/stopping GPS track recording in external fitness app.</string>
<string name="watchface_dialog_pre_setting_position">pre-setting position to %s</string>
<string name="watchface_setting_light_up_on_notification">Light up on new notification</string>
<string name="info_no_devices_connected">no devices connected</string>
<string name="info_connected_count">%d devices connected</string>
</resources>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Connection over Bluetooth classic" />
<CheckBoxPreference
android:key="prefs_key_device_reconnect_on_acl"
android:title="connect on connection from device"
android:summary="establish a connection when connection is initiated by device, like headphones"
android:layout="@layout/preference_checkbox" />
</PreferenceScreen>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Connection over BLE" />
<CheckBoxPreference
android:key="prefs_key_device_auto_reconnect"
android:title="auto-reconnect to device"
android:summary="proactively try to connect to device periodically"
android:layout="@layout/preference_checkbox" />
</PreferenceScreen>

View File

@ -17,7 +17,9 @@
android:layout="@layout/preference_checkbox"
android:defaultValue="false"
android:key="general_autocreconnect"
android:title="@string/pref_title_general_autoreconnect" />
android:title="@string/pref_title_general_autoreconnect"
android:enabled="false"
android:summary="setting has been moved to device specific settings"/>
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="true"