mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
oppo: add minimal support ldac, multipoint and gamemode
This commit is contained in:
+26
-1
@@ -76,12 +76,25 @@ public abstract class OppoHeadphonesCoordinator extends AbstractBLClassicDeviceC
|
||||
settings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS);
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS, R.xml.devicesettings_headphones);
|
||||
|
||||
settings.addRootScreen(DeviceSpecificSettingsScreen.DEVELOPER);
|
||||
if (this.supportsLdac(device)) {
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.DEVELOPER, R.xml.devicesettings_ldac_toggle);
|
||||
}
|
||||
|
||||
if (this.supportsMultipoint(device)) {
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.DEVELOPER, R.xml.devicesettings_oppo_headphones_multipoint);
|
||||
}
|
||||
|
||||
if (this.supportsGameMode(device)) {
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.DEVELOPER, R.xml.devicesettings_oppo_headphones_game_mode);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new OppoHeadphonesSettingsCustomizer(getTouchOptions());
|
||||
return new OppoHeadphonesSettingsCustomizer(getTouchOptions(), supportsLdac(device), supportsMultipoint(device), supportsGameMode(device));
|
||||
}
|
||||
|
||||
protected abstract Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> getTouchOptions();
|
||||
@@ -90,4 +103,16 @@ public abstract class OppoHeadphonesCoordinator extends AbstractBLClassicDeviceC
|
||||
public final DeviceKind getDeviceKind(@NonNull GBDevice device) {
|
||||
return DeviceKind.EARBUDS;
|
||||
}
|
||||
|
||||
public boolean supportsLdac(@NonNull GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsMultipoint(@NonNull GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsGameMode(@NonNull GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-2
@@ -22,10 +22,17 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchC
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchConfigType;
|
||||
|
||||
public class OppoHeadphonesPreferences {
|
||||
public static String getKey(final TouchConfigSide side, final TouchConfigType type) {
|
||||
public static final String TOUCH_PREFIX= "oppo_touch__";
|
||||
|
||||
public static final String LDAC = "pref_soundcore_ldac_mode";
|
||||
public static final String MULTIPOINT = "oppo_multipoint";
|
||||
public static final String GAME_MODE= "oppo_game_mode";
|
||||
|
||||
public static String getTouchKey(final TouchConfigSide side, final TouchConfigType type) {
|
||||
return String.format(
|
||||
Locale.ROOT,
|
||||
"oppo_touch__%s__%s",
|
||||
"%s%s__%s",
|
||||
TOUCH_PREFIX,
|
||||
side.name().toLowerCase(Locale.ROOT),
|
||||
type.name().toLowerCase(Locale.ROOT)
|
||||
);
|
||||
|
||||
+33
-4
@@ -40,10 +40,17 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||
private final Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> touchOptions;
|
||||
private final boolean supportsLdac;
|
||||
private final boolean supportsMultipoint;
|
||||
private final boolean supportsGameMode;
|
||||
|
||||
public static final Creator<OppoHeadphonesSettingsCustomizer> CREATOR = new Creator<OppoHeadphonesSettingsCustomizer>() {
|
||||
@Override
|
||||
public OppoHeadphonesSettingsCustomizer createFromParcel(final Parcel in) {
|
||||
final boolean supportsLdac = in.readByte() != 0;
|
||||
final boolean supportsMultipoint = in.readByte() != 0;
|
||||
final boolean supportsGameMode = in.readByte() != 0;
|
||||
|
||||
final Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> touchOptions = new LinkedHashMap<>();
|
||||
final int numOptions = in.readInt();
|
||||
for (int i = 0; i < numOptions; i++) {
|
||||
@@ -53,7 +60,8 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
in.readList(values, TouchConfigValue.class.getClassLoader());
|
||||
touchOptions.put(Pair.create(touchConfigSide, touchConfigType), values);
|
||||
}
|
||||
return new OppoHeadphonesSettingsCustomizer(touchOptions);
|
||||
|
||||
return new OppoHeadphonesSettingsCustomizer(touchOptions, supportsLdac, supportsMultipoint, supportsGameMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,8 +70,11 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
}
|
||||
};
|
||||
|
||||
public OppoHeadphonesSettingsCustomizer(final Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> touchOptions) {
|
||||
public OppoHeadphonesSettingsCustomizer(final Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> touchOptions, final boolean supportsLdac, final boolean supportsMultipoint, final boolean supportsGameMode) {
|
||||
this.touchOptions = touchOptions;
|
||||
this.supportsLdac = supportsLdac;
|
||||
this.supportsMultipoint = supportsMultipoint;
|
||||
this.supportsGameMode = supportsGameMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,6 +86,10 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
final Set<TouchConfigSide> knownSides = new HashSet<>();
|
||||
final Set<TouchConfigType> knownTypes = new HashSet<>();
|
||||
|
||||
this.addPreferenceHandler(handler, OppoHeadphonesPreferences.LDAC, supportsLdac);
|
||||
this.addPreferenceHandler(handler, OppoHeadphonesPreferences.MULTIPOINT, supportsMultipoint);
|
||||
this.addPreferenceHandler(handler, OppoHeadphonesPreferences.GAME_MODE, supportsGameMode);
|
||||
|
||||
for (final Map.Entry<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> e : touchOptions.entrySet()) {
|
||||
final TouchConfigSide side = e.getKey().first;
|
||||
final TouchConfigType type = e.getKey().second;
|
||||
@@ -83,7 +98,7 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
knownSides.add(side);
|
||||
knownTypes.add(type);
|
||||
|
||||
final String key = OppoHeadphonesPreferences.getKey(side, type);
|
||||
final String key = OppoHeadphonesPreferences.getTouchKey(side, type);
|
||||
final ListPreference pref = handler.findPreference(key);
|
||||
if (pref == null) {
|
||||
continue;
|
||||
@@ -120,7 +135,7 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
|
||||
for (final TouchConfigType type : TouchConfigType.values()) {
|
||||
if (!knownTypes.contains(type)) {
|
||||
final String key = OppoHeadphonesPreferences.getKey(side, type);
|
||||
final String key = OppoHeadphonesPreferences.getTouchKey(side, type);
|
||||
final Preference pref = handler.findPreference(key);
|
||||
if (pref != null) {
|
||||
pref.setVisible(false);
|
||||
@@ -130,6 +145,16 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
}
|
||||
}
|
||||
|
||||
private void addPreferenceHandler(DeviceSpecificSettingsHandler handler, String key, boolean isSupported) {
|
||||
Preference pref = handler.findPreference(key);
|
||||
if (pref != null) {
|
||||
pref.setVisible(isSupported);
|
||||
if (isSupported) {
|
||||
handler.addPreferenceHandlerFor(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPreferenceKeysWithSummary() {
|
||||
return Collections.emptySet();
|
||||
@@ -142,6 +167,10 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
dest.writeByte((byte) (supportsLdac ? 1 : 0));
|
||||
dest.writeByte((byte) (supportsMultipoint ? 1 : 0));
|
||||
dest.writeByte((byte) (supportsGameMode ? 1 : 0));
|
||||
|
||||
dest.writeInt(touchOptions.size());
|
||||
for (final Map.Entry<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> e : touchOptions.entrySet()) {
|
||||
dest.writeString(e.getKey().first.name());
|
||||
|
||||
+50
-3
@@ -41,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.OppoCo
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchConfigSide;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchConfigType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchConfigValue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.PrefType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.preferences.DevicePrefs;
|
||||
|
||||
@@ -262,7 +263,7 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
LOG.debug("Got touch config for {} {} = {}", side, type, value);
|
||||
|
||||
eventUpdatePreferences.withPreference(
|
||||
OppoHeadphonesPreferences.getKey(side, type),
|
||||
OppoHeadphonesPreferences.getTouchKey(side, type),
|
||||
value.name().toLowerCase(Locale.ROOT)
|
||||
);
|
||||
}
|
||||
@@ -325,11 +326,11 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
public byte[] encodeSendConfiguration(final String config) {
|
||||
final DevicePrefs prefs = getDevicePrefs();
|
||||
|
||||
if (config.startsWith("oppo_touch__")) {
|
||||
if (config.startsWith(OppoHeadphonesPreferences.TOUCH_PREFIX)) {
|
||||
final String[] parts = config.split("__");
|
||||
final TouchConfigSide side = TouchConfigSide.valueOf(parts[1].toUpperCase(Locale.ROOT));
|
||||
final TouchConfigType type = TouchConfigType.valueOf(parts[2].toUpperCase(Locale.ROOT));
|
||||
final String valueCode = prefs.getString(OppoHeadphonesPreferences.getKey(side, type), null);
|
||||
final String valueCode = prefs.getString(OppoHeadphonesPreferences.getTouchKey(side, type), null);
|
||||
if (valueCode == null) {
|
||||
LOG.warn("Failed to get touch option value for {}/{}", side, type);
|
||||
return super.encodeSendConfiguration(config);
|
||||
@@ -348,6 +349,24 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
return encodeMessage(OppoCommand.TOUCH_CONFIG_SET, buf.array());
|
||||
}
|
||||
|
||||
if (config.equals(OppoHeadphonesPreferences.LDAC)) {
|
||||
final boolean value = prefs.getBoolean(OppoHeadphonesPreferences.LDAC, false);
|
||||
LOG.debug("Sending ldac = {}", value);
|
||||
return encodeLdacSet(value);
|
||||
}
|
||||
|
||||
if (config.equals(OppoHeadphonesPreferences.MULTIPOINT)) {
|
||||
final boolean value = prefs.getBoolean(OppoHeadphonesPreferences.MULTIPOINT, false);
|
||||
LOG.debug("Sending multipoint = {}", value);
|
||||
return encodeMultipointSet(value);
|
||||
}
|
||||
|
||||
if (config.equals(OppoHeadphonesPreferences.GAME_MODE)) {
|
||||
final boolean value = prefs.getBoolean(OppoHeadphonesPreferences.GAME_MODE, false);
|
||||
LOG.debug("Sending game mode = {}", value);
|
||||
return encodeGameModeSet(value);
|
||||
}
|
||||
|
||||
return super.encodeSendConfiguration(config);
|
||||
}
|
||||
|
||||
@@ -359,6 +378,34 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
return encodeMessage(OppoCommand.TOUCH_CONFIG_REQ, new byte[]{0x02, 0x03, 0x01});
|
||||
}
|
||||
|
||||
public byte[] encodeLdacSet(final boolean enable) {
|
||||
final byte[] payload = new byte[] {
|
||||
(byte) (enable ? 0x01 : 0x00)
|
||||
};
|
||||
return encodePrefSet(PrefType.LDAC, payload);
|
||||
}
|
||||
|
||||
public byte[] encodeGameModeSet(final boolean enable) {
|
||||
final byte[] payload = new byte[] {
|
||||
(byte) (enable ? 0x01 : 0x00)
|
||||
};
|
||||
return encodePrefSet(PrefType.GAME_MODE, payload);
|
||||
}
|
||||
|
||||
public byte[] encodeMultipointSet(final boolean enable) {
|
||||
final byte[] payload = new byte[] {
|
||||
(byte) (enable ? 0x01 : 0x00)
|
||||
};
|
||||
return encodePrefSet(PrefType.MULTIPOINT, payload);
|
||||
}
|
||||
|
||||
public byte[] encodePrefSet(final PrefType type, final byte[] value) {
|
||||
final byte[] payload = new byte[1 + value.length];
|
||||
payload[0] = (byte) type.getCode();
|
||||
System.arraycopy(value, 0, payload, 1, value.length);
|
||||
return encodeMessage(OppoCommand.PREF_SET, payload);
|
||||
}
|
||||
|
||||
private byte[] encodeMessage(final OppoCommand command, final byte[] payload) {
|
||||
final ByteBuffer buf = ByteBuffer.allocate(9 + payload.length).order(ByteOrder.LITTLE_ENDIAN);
|
||||
buf.put(CMD_PREAMBLE);
|
||||
|
||||
+2
@@ -30,6 +30,8 @@ public enum OppoCommand {
|
||||
TOUCH_CONFIG_ACK(0x8401),
|
||||
FIND_DEVICE_REQ(0x0400),
|
||||
FIND_DEVICE_ACK(0x8400),
|
||||
PREF_SET(0x0403),
|
||||
PREF_ACK(0x8403),
|
||||
;
|
||||
|
||||
private final short code;
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/* Copyright (C) 2024 José Rebelo
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public enum PrefType {
|
||||
LDAC(0x18),
|
||||
GAME_MODE(0x06),
|
||||
MULTIPOINT(0x11),
|
||||
FIND_PHONE(0x26),
|
||||
;
|
||||
|
||||
private final int code;
|
||||
|
||||
PrefType(final int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PrefType fromCode(final int code) {
|
||||
for (final PrefType param : PrefType.values()) {
|
||||
if (param.code == code) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<SwitchPreferenceCompat
|
||||
android:key="oppo_game_mode"
|
||||
android:title="@string/prefs_game_mode"
|
||||
android:icon="@drawable/ic_videogame"
|
||||
android:defaultValue="false"
|
||||
android:layout="@layout/preference_checkbox" />
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<SwitchPreferenceCompat
|
||||
android:key="oppo_multipoint"
|
||||
android:title="@string/bluetooth_multipoint_pairing"
|
||||
android:icon="@drawable/ic_bluetooth_searching"
|
||||
android:defaultValue="false"
|
||||
android:layout="@layout/preference_checkbox" />
|
||||
</androidx.preference.PreferenceScreen>
|
||||
Reference in New Issue
Block a user