From b0ed617072a7cae3b3ea433aaa1e28c93fc523d6 Mon Sep 17 00:00:00 2001 From: dakhnod Date: Mon, 27 Dec 2021 15:37:04 +0100 Subject: [PATCH] device-bose-qc35 (#2520) This PR adds not only the device Bose QC35, it also adds the following autop-reconnect feature: When the headphones are turned on, the initiate a connection with the phone. With this change, GB is notified about said change, and tries to establish a connection to the newly connected device, if the appropriate device setting is set. The QC35 headpones always have NC turned on after boot, thus the main feature of this implementation is to turn off NC as soon as the headphones are turned on and connected to the phone. I am open for discussion regarding the implementation, but this seems like a good first proposal. What is missing is the ability to connect to multiple devices, since in many cases headphones can be connected to the watch simultaniously to a smartwatch or other gadget. Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2520 Co-authored-by: dakhnod Co-committed-by: dakhnod --- .../DeviceSettingsPreferenceConst.java | 2 + .../DeviceSpecificSettingsFragment.java | 3 + .../devices/qc35/QC35Coordinator.java | 145 ++++++++++++++++++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 4 + .../service/devices/qc35/QC35BaseSupport.java | 89 +++++++++++ .../service/devices/qc35/QC35IOThread.java | 78 ++++++++++ .../service/devices/qc35/QC35Protocol.java | 90 +++++++++++ .../gadgetbridge/util/DeviceHelper.java | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/devicesettings_qc35.xml | 9 ++ 11 files changed, 425 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qc35/QC35Coordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35BaseSupport.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35IOThread.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35Protocol.java create mode 100644 app/src/main/res/xml/devicesettings_qc35.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java index 231912ae6..a7318161c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java @@ -133,6 +133,8 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_SONY_AUTOMATIC_POWER_OFF = "pref_sony_automatic_power_off"; public static final String PREF_SONY_NOTIFICATION_VOICE_GUIDE = "pref_sony_notification_voice_guide"; + public static final String PREF_QC35_NOISE_CANCELLING_LEVEL = "qc35_noise_cancelling_level"; + public static final String PREFS_ACTIVITY_IN_DEVICE_CARD = "prefs_activity_in_device_card"; public static final String PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS = "prefs_activity_in_device_card_steps"; public static final String PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP = "prefs_activity_in_device_card_sleep"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index e0371502e..92f04e4cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -152,6 +152,7 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_DEVICE_CHARTS_TABS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST; @@ -569,6 +570,8 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp addPreferenceHandlerFor(PREF_SONY_AUTOMATIC_POWER_OFF); addPreferenceHandlerFor(PREF_SONY_NOTIFICATION_VOICE_GUIDE); + addPreferenceHandlerFor(PREF_QC35_NOISE_CANCELLING_LEVEL); + String sleepTimeState = prefs.getString(PREF_SLEEP_TIME, PREF_DO_NOT_DISTURB_OFF); boolean sleepTimeScheduled = sleepTimeState.equals(PREF_DO_NOT_DISTURB_SCHEDULED); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qc35/QC35Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qc35/QC35Coordinator.java new file mode 100644 index 000000000..ff144f3e2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qc35/QC35Coordinator.java @@ -0,0 +1,145 @@ +/* Copyright (C) 2021 Daniel Dakhno + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.qc35; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; +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.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class QC35Coordinator extends AbstractDeviceCoordinator { + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + if (candidate.getName().startsWith("Bose QC 35")) { + return DeviceType.BOSE_QC35; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.BOSE_QC35; + } + + @Nullable + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_qc35 + }; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public int getAlarmSlotCount() { + return 0; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public String getManufacturer() { + return "Bose"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + public boolean supportsCalendarEvents() { + return false; + } + + @Override + public boolean supportsRealtimeData() { + return false; + } + + @Override + public boolean supportsWeather() { + return false; + } + + @Override + public boolean supportsFindDevice() { + return false; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index ff3dcc3c8..83fc145ef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -105,6 +105,7 @@ public enum DeviceType { GALAXY_BUDS(420, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_galaxybuds), SONY_WH_1000XM3(430, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_sony_wh_1000xm3), SONY_WF_SP800N(431, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_wf_sp800n), + BOSE_QC35(440, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_bose_qc35), TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index bef134867..eda89f9b9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -85,6 +85,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.nothing.Ear1Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.nut.NutSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.pinetime.PineTimeJFSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qc35.QC35BaseSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.smaq2oss.SMAQ2OSSSupport; @@ -376,6 +377,9 @@ public class DeviceSupportFactory { case SONY_WF_SP800N: deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case BOSE_QC35: + deviceSupport = new ServiceDeviceSupport(new QC35BaseSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35BaseSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35BaseSupport.java new file mode 100644 index 000000000..5efee2a84 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35BaseSupport.java @@ -0,0 +1,89 @@ +/* Copyright (C) 2021 Daniel Dakhno + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qc35; + +import android.net.Uri; + +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; + +public class QC35BaseSupport extends AbstractSerialDeviceSupport { + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config, Integer id) { + + } + + @Override + public void onHeartRateTest() { + + } + + @Override + public void onSetConstantVibration(int integer) { + + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + + } + + @Override + public void onReadConfiguration(String config) { + + } + + + + @Override + public boolean connect() { + getDeviceProtocol(); + getDeviceIOThread().start(); + return true; + } + + @Override + public boolean useAutoConnect() { + return false; + } + + @Override + protected GBDeviceProtocol createDeviceProtocol() { + return new QC35Protocol(getDevice()); + } + + @Override + protected GBDeviceIoThread createDeviceIOThread() { + return new QC35IOThread(getDevice(), getContext(), (QC35Protocol) createDeviceProtocol(), this, getBluetoothAdapter()); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35IOThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35IOThread.java new file mode 100644 index 000000000..964791bd1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35IOThread.java @@ -0,0 +1,78 @@ +/* Copyright (C) 2021 Daniel Dakhno + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qc35; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.os.ParcelUuid; + +import androidx.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread; +import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; + +public class QC35IOThread extends BtClassicIoThread { + QC35Protocol protocol; + byte[] buffer = new byte[1024]; + + private Logger logger = LoggerFactory.getLogger(getClass()); + + public QC35IOThread(GBDevice gbDevice, Context context, QC35Protocol deviceProtocol, AbstractSerialDeviceSupport deviceSupport, BluetoothAdapter btAdapter) { + super(gbDevice, context, deviceProtocol, deviceSupport, btAdapter); + this.protocol = deviceProtocol; + } + + @NonNull + @Override + protected UUID getUuidToConnect(@NonNull ParcelUuid[] uuids) { + return UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); + } + + @Override + protected void initialize() { + super.initialize(); + + byte[] connectPayload = new byte[]{0x00, 0x01, 0x01, 0x00}; + byte[] ncPayload = protocol.encodeSendConfiguration(DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL); + byte[] batteryPayload = new byte[]{0x02, 0x02, 0x01, 0x00}; + byte[] packet = new byte[connectPayload.length + ncPayload.length + batteryPayload.length]; + System.arraycopy(connectPayload, 0, packet, 0, connectPayload.length); + System.arraycopy(ncPayload, 0, packet, connectPayload.length, ncPayload.length); + System.arraycopy(batteryPayload, 0, packet, ncPayload.length + connectPayload.length, batteryPayload.length); + + getDevice().setFirmwareVersion("0"); + + write(packet); + } + @Override + protected byte[] parseIncoming(InputStream inStream) throws IOException { + int size = inStream.read(buffer); + logger.debug("read bytes: {}", size); + byte[] actual = new byte[size]; + System.arraycopy(buffer, 0, actual, 0, size); + return actual; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35Protocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35Protocol.java new file mode 100644 index 000000000..c7f026a46 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qc35/QC35Protocol.java @@ -0,0 +1,90 @@ +/* Copyright (C) 2021 Daniel Dakhno + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qc35; + +import android.content.SharedPreferences; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; + +public class QC35Protocol extends GBDeviceProtocol { + Logger logger = LoggerFactory.getLogger(getClass()); + protected QC35Protocol(GBDevice device) { + super(device); + } + + @Override + public GBDeviceEvent[] decodeResponse(byte[] responseData) { + logger.debug("response: {}", StringUtils.bytesToHex(responseData)); + + ArrayList events = new ArrayList<>(); + + ByteBuffer buffer = ByteBuffer.wrap(responseData); + while(buffer.remaining() > 0){ + int first = buffer.get(); + int second = buffer.get(); + int third = buffer.get(); + int length = buffer.get(); + byte[] data = new byte[length]; + buffer.get(data); + if(first == 0x02){ + if(second == 0x02){ + if(third == 0x03){ + GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo(); + batteryInfo.level = data[0]; + events.add(batteryInfo); + } + } + } + } + + return events.toArray(new GBDeviceEvent[0]); + } + + @Override + public byte[] encodeTestNewFunction() { + return new byte[]{0x02, 0x02, 0x01, 0x00}; + } + + @Override + public byte[] encodeSendConfiguration(String config) { + SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); + + if(config.equals(DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL)){ + int level = prefs.getInt(config, 0); + if(level == 2){ + level = 1; + }else if(level == 1){ + level = 3; + } + return new byte[]{0x01, 0x06, 0x02, 0x01, (byte) level}; + } + + return null; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index abd6b8952..a9adb78b4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -103,6 +103,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.nothing.Ear1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.nut.NutCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.qc35.QC35Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator; @@ -317,6 +318,7 @@ public class DeviceHelper { result.add(new GalaxyBudsLiveDeviceCoordinator()); result.add(new SonyWH1000XM3Coordinator()); result.add(new SonyWFSP800NCoordinator()); + result.add(new QC35Coordinator()); return result; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25aae9a00..0f211a346 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1442,4 +1442,6 @@ Show circle on timeout: Enable on-device pairing confirmation On-device pairing confirmations can get annoying. Disabling them might lose you functionality. + Bose QC35 + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_qc35.xml b/app/src/main/res/xml/devicesettings_qc35.xml new file mode 100644 index 000000000..4d1899c10 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_qc35.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file