oppo: add AncConfigType.TOUCH_CYCLE_MODES

This commit is contained in:
NTeditor
2026-06-20 03:21:55 +03:00
parent c8fd2f0c15
commit 6881bab143
9 changed files with 177 additions and 28 deletions
@@ -83,6 +83,7 @@ public abstract class OppoHeadphonesCoordinator extends AbstractBLClassicDeviceC
}
if (this.supportsAnc(device)) {
settings.addSubScreen(DeviceSpecificSettingsScreen.AUDIO, R.xml.devicesettings_onemore_noise_control_selector);
settings.addSubScreen(DeviceSpecificSettingsScreen.TOUCH_OPTIONS, R.xml.devicesettings_oppo_headphones_touch_options_anc);
}
}
@@ -22,12 +22,13 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchC
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchConfigType;
public class OppoHeadphonesPreferences {
public static final String TOUCH_PREFIX= "oppo_touch__";
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 final String ANC_SELECTOR = "noise_control_selector";
public static final String ANC_TOUCH_CYCLE_MODES = "oppo_touch_anc_cycle_modes";
public static String getTouchKey(final TouchConfigSide side, final TouchConfigType type) {
return String.format(
@@ -93,6 +93,7 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
this.addPreferenceHandler(handler, OppoHeadphonesPreferences.MULTIPOINT, supportsMultipoint);
this.addPreferenceHandler(handler, OppoHeadphonesPreferences.GAME_MODE, supportsGameMode);
this.addPreferenceHandler(handler, OppoHeadphonesPreferences.ANC_SELECTOR, supportsAnc);
this.addPreferenceHandler(handler, OppoHeadphonesPreferences.ANC_TOUCH_CYCLE_MODES, supportsAnc);
for (final Map.Entry<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> e : touchOptions.entrySet()) {
final TouchConfigSide side = e.getKey().first;
@@ -26,6 +26,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.EnumSet;
import java.util.Set;
import java.util.HashSet;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
@@ -42,6 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.TouchC
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.MiscConfigType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.AncConfigType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.AncConfigValue;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.preferences.DevicePrefs;
@@ -344,16 +348,16 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
private static GBDeviceEvent parseMiscConfig(final byte[] payload) {
final GBDeviceEventUpdatePreferences eventUpdatePreferences = new GBDeviceEventUpdatePreferences();
for (int i = 2; i + 1 < payload.length; i += 2) {
final int code = payload[i] & 0xff;
final int value = payload[i + 1] & 0xff;
final int typeCode = payload[i] & 0xff;
final int valueCode = payload[i + 1] & 0xff;
final MiscConfigType type = MiscConfigType.fromCode(code);
final MiscConfigType type = MiscConfigType.fromCode(typeCode);
if (type == null) {
LOG.warn("Unknown misc config code {}", code);
LOG.warn("Unknown misc config type code {}", typeCode);
continue;
}
final boolean isEnabled = (value == 1);
final boolean isEnabled = (valueCode == 1);
switch (type) {
case LDAC:
LOG.debug("Got misc config for LDAC = {}", isEnabled);
@@ -377,7 +381,7 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
);
break;
default:
LOG.warn("Unknown misc config code {}", code);
LOG.warn("Unknown misc config type code {}", typeCode);
break;
}
}
@@ -421,22 +425,39 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
private static GBDeviceEvent parseAncConfig(final byte[] payload) {
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences();
for (int i = 2; i + 1 < payload.length; i += 2) {
final int code = payload[i] & 0xff;
final int value = payload[i + 1] & 0xff;
final int typeCode = payload[1] & 0xff;
final int valueCode = payload[3] & 0xff;
switch (code) {
case 0x01:
final AncConfigValue mode = AncConfigValue.fromCode(value);
if (mode != null) {
event.withPreference(OppoHeadphonesPreferences.ANC_SELECTOR, mode.getPrefId());
} else {
LOG.warn("Unknown ANC mode {}", value);
}
break;
default:
LOG.debug("Unknown code {}", code);
break;
final AncConfigType type = AncConfigType.fromCode(typeCode);
if (type == null) {
LOG.warn("Unknown anc type code {}", typeCode);
return event;
}
switch (type) {
case MODE: {
final AncConfigValue mode = AncConfigValue.fromCode(valueCode);
if (mode != null) {
LOG.debug("Got anc config for {} = {}", type, mode);
event.withPreference(OppoHeadphonesPreferences.ANC_SELECTOR, mode.getPrefId());
} else {
LOG.warn("Unknown anc mode code {}", valueCode);
}
break;
}
case TOUCH_CYCLE_MODES: {
final EnumSet<AncConfigValue> modes = AncConfigValue.fromMask(valueCode);
if (!modes.isEmpty()) {
final Set<String> prefIds = AncConfigValue.toPrefIds(modes);
LOG.debug("Got anc config for {} = {}", type, prefIds);
event.withPreference(OppoHeadphonesPreferences.ANC_TOUCH_CYCLE_MODES, prefIds);
}
break;
}
default: {
LOG.debug("Unknown anc type code {}", typeCode);
break;
}
}
return event;
@@ -498,7 +519,19 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
mode = AncConfigValue.OFF;
}
LOG.debug("Sending ANC mode = {}", mode);
return encodeAncConfigSet(mode);
return encodeAncModeSet(mode);
}
if (config.equals(OppoHeadphonesPreferences.ANC_TOUCH_CYCLE_MODES)) {
final Set<String> prefIds = prefs.getStringSet(OppoHeadphonesPreferences.ANC_TOUCH_CYCLE_MODES, Collections.emptySet());
final EnumSet<AncConfigValue> modes = AncConfigValue.fromPrefIds(prefIds);
if (modes.size() < 2) {
LOG.warn("ANC cycle must contain at least 2 modes. Current selection: {}", modes);
return super.encodeSendConfiguration(config);
}
LOG.debug("Sending ANC touch cycle modes = {}", modes);
return encodeAncTouchCycleModesSet(modes);
}
return super.encodeSendConfiguration(config);
@@ -563,18 +596,31 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
return encodeMessage(OppoCommand.MISC_CONFIG_REQ, payload);
}
public byte[] encodeAncConfigSet(final AncConfigValue mode) {
public byte[] encodeAncModeSet(final AncConfigValue mode) {
final byte[] payload = new byte[] {
(byte) 0x01,
(byte) AncConfigType.MODE.getCode(),
(byte) 0x01,
(byte) mode.getCode()
};
return encodeMessage(OppoCommand.ANC_CONFIG_SET, payload);
}
public byte[] encodeAncConfigReq() {
public byte[] encodeAncTouchCycleModesSet(final EnumSet<AncConfigValue> modes) {
int mask = 0;
for (AncConfigValue mode : modes) {
mask |= mode.getCode();
}
final byte[] payload = new byte[] {
(byte) AncConfigType.TOUCH_CYCLE_MODES.getCode(),
(byte) 0x01,
(byte) mask
};
return encodeMessage(OppoCommand.ANC_CONFIG_SET, payload);
}
public byte[] encodeAncConfigReq(final AncConfigType type) {
final byte[] payload = new byte[] {
(byte) type.getCode(),
(byte) 0x01,
};
return encodeMessage(OppoCommand.ANC_CONFIG_REQ, payload);
@@ -30,6 +30,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
import nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractHeadphoneSerialDeviceSupportV2;
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.MiscConfigType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands.AncConfigType;
import nodomain.freeyourgadget.gadgetbridge.devices.oppo.OppoHeadphonesCoordinator;
public class OppoHeadphonesSupport extends AbstractHeadphoneSerialDeviceSupportV2<OppoHeadphonesProtocol> {
@@ -91,7 +92,8 @@ public class OppoHeadphonesSupport extends AbstractHeadphoneSerialDeviceSupportV
}
builder.write(mDeviceProtocol.encodeTouchConfigReq());
if (coordinator.supportsAnc(getDevice())) {
builder.write(mDeviceProtocol.encodeAncConfigReq());
builder.write(mDeviceProtocol.encodeAncConfigReq(AncConfigType.MODE));
builder.write(mDeviceProtocol.encodeAncConfigReq(AncConfigType.TOUCH_CYCLE_MODES));
}
builder.write(mDeviceProtocol.encodeBatteryReq());
@@ -0,0 +1,46 @@
/* 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 AncConfigType {
MODE(0x01),
TOUCH_CYCLE_MODES(0x02),
;
private final int code;
AncConfigType(final int code) {
this.code = code;
}
public int getCode() {
return code;
}
@Nullable
public static AncConfigType fromCode(final int code) {
for (final AncConfigType param : AncConfigType.values()) {
if (param.code == code) {
return param;
}
}
return null;
}
}
@@ -16,6 +16,11 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.commands;
import java.lang.Iterable;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import androidx.annotation.Nullable;
public enum AncConfigValue {
@@ -51,6 +56,17 @@ public enum AncConfigValue {
return prefId;
}
public static EnumSet<AncConfigValue> fromMask(int mask) {
EnumSet<AncConfigValue> modes = EnumSet.noneOf(AncConfigValue.class);
for (AncConfigValue mode : AncConfigValue.values()) {
if ((mask & mode.getCode()) == mode.getCode()) {
modes.add(mode);
}
}
return modes;
}
@Nullable
public static AncConfigValue fromPrefId(final String prefId) {
for (final AncConfigValue param : AncConfigValue.values()) {
@@ -61,4 +77,23 @@ public enum AncConfigValue {
return null;
}
public static Set<String> toPrefIds(Iterable<AncConfigValue> modes) {
Set<String> prefIds = new HashSet<>();
for (AncConfigValue mode : modes) {
prefIds.add(mode.getPrefId());
}
return prefIds;
}
public static EnumSet<AncConfigValue> fromPrefIds(Iterable<String> prefIds) {
EnumSet<AncConfigValue> modes = EnumSet.noneOf(AncConfigValue.class);
for (String prefId : prefIds) {
AncConfigValue mode = fromPrefId(prefId);
if (mode != null) {
modes.add(mode);
}
}
return modes;
}
}
+1 -1
View File
@@ -4520,7 +4520,7 @@
<item>@string/pref_title_touch_voice_assistant</item>
<item>@string/pref_title_touch_voice_assistant</item>
<item>@string/prefs_game_mode</item>
<item>@string/prefs_noise_control</item>
<item>@string/moondrop_touch_action_anc_mode</item>
</string-array>
<string-array name="oppo_touch_tap_values">
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:key="oppo_touch_header_anc"
android:title="@string/prefs_active_noise_cancelling"
app:iconSpaceReserved="false">
<MultiSelectListPreference
android:key="oppo_touch_anc_cycle_modes"
android:title="@string/prefs_noise_modes_to_cycle"
android:icon="@drawable/ic_sync"
android:entries="@array/onemore_noise_control_modes"
android:entryValues="@array/onemore_noise_control_values"
android:defaultValue="@array/onemore_noise_control_values" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>