mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-23 09:07:44 +01:00
Realme Buds T110: Initial support
This commit is contained in:
parent
1618fda418
commit
5f91715c89
@ -124,6 +124,7 @@
|
||||
<w>protomors</w>
|
||||
<w>qhybrid</w>
|
||||
<w>quallenauge</w>
|
||||
<w>realme</w>
|
||||
<w>rebelo</w>
|
||||
<w>roidmi</w>
|
||||
<w>romanization</w>
|
||||
|
@ -16,93 +16,60 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.oppo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLClassicDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.OppoHeadphonesSupport;
|
||||
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;
|
||||
|
||||
public class OppoEncoAirCoordinator extends AbstractBLClassicDeviceCoordinator {
|
||||
public class OppoEncoAirCoordinator extends OppoHeadphonesCoordinator {
|
||||
@Override
|
||||
protected Pattern getSupportedDeviceName() {
|
||||
return Pattern.compile("OPPO Enco Air", Pattern.LITERAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull final GBDevice gbDevice, @NonNull final Device device, @NonNull final DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Oppo";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return OppoHeadphonesSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeviceNameResource() {
|
||||
return R.string.devicetype_oppo_enco_air;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultIconResource() {
|
||||
return R.drawable.ic_device_nothingear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisabledIconResource() {
|
||||
return R.drawable.ic_device_nothingear_disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatteryCount() {
|
||||
return 3;
|
||||
}
|
||||
protected Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> getTouchOptions() {
|
||||
return new LinkedHashMap<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>>() {{
|
||||
put(Pair.create(TouchConfigSide.LEFT, TouchConfigType.TAP_2), Arrays.asList(
|
||||
TouchConfigValue.OFF,
|
||||
TouchConfigValue.PLAY_PAUSE,
|
||||
TouchConfigValue.PREVIOUS,
|
||||
TouchConfigValue.NEXT,
|
||||
TouchConfigValue.VOICE_ASSISTANT
|
||||
));
|
||||
put(Pair.create(TouchConfigSide.LEFT, TouchConfigType.TAP_3), Arrays.asList(
|
||||
TouchConfigValue.OFF,
|
||||
TouchConfigValue.VOICE_ASSISTANT,
|
||||
TouchConfigValue.GAME_MODE
|
||||
));
|
||||
put(Pair.create(TouchConfigSide.LEFT, TouchConfigType.HOLD), Arrays.asList(
|
||||
TouchConfigValue.OFF,
|
||||
TouchConfigValue.VOLUME_UP,
|
||||
TouchConfigValue.VOLUME_DOWN
|
||||
));
|
||||
|
||||
@Override
|
||||
public BatteryConfig[] getBatteryConfig(final GBDevice device) {
|
||||
final BatteryConfig battery1 = new BatteryConfig(0, R.drawable.ic_nothing_ear_l, R.string.left_earbud);
|
||||
final BatteryConfig battery2 = new BatteryConfig(1, R.drawable.ic_nothing_ear_r, R.string.right_earbud);
|
||||
final BatteryConfig battery3 = new BatteryConfig(2, R.drawable.ic_tws_case, R.string.battery_case);
|
||||
return new BatteryConfig[]{battery1, battery2, battery3};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||
final DeviceSpecificSettings settings = new DeviceSpecificSettings();
|
||||
|
||||
settings.addRootScreen(DeviceSpecificSettingsScreen.TOUCH_OPTIONS);
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.TOUCH_OPTIONS, R.xml.devicesettings_oppo_headphones_touch_options);
|
||||
|
||||
settings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS);
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS, R.xml.devicesettings_headphones);
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new OppoHeadphonesSettingsCustomizer();
|
||||
// Right side is the same
|
||||
put(Pair.create(TouchConfigSide.RIGHT, TouchConfigType.TAP_2), get(Pair.create(TouchConfigSide.LEFT, TouchConfigType.TAP_2)));
|
||||
put(Pair.create(TouchConfigSide.RIGHT, TouchConfigType.TAP_3), get(Pair.create(TouchConfigSide.LEFT, TouchConfigType.TAP_3)));
|
||||
put(Pair.create(TouchConfigSide.RIGHT, TouchConfigType.HOLD), get(Pair.create(TouchConfigSide.LEFT, TouchConfigType.HOLD)));
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
/* 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.devices.oppo;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLClassicDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.oppo.OppoHeadphonesSupport;
|
||||
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;
|
||||
|
||||
public abstract class OppoHeadphonesCoordinator extends AbstractBLClassicDeviceCoordinator {
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull final GBDevice gbDevice, @NonNull final Device device, @NonNull final DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Oppo";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return OppoHeadphonesSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultIconResource() {
|
||||
return R.drawable.ic_device_nothingear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisabledIconResource() {
|
||||
return R.drawable.ic_device_nothingear_disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatteryCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryConfig[] getBatteryConfig(final GBDevice device) {
|
||||
final BatteryConfig battery1 = new BatteryConfig(0, R.drawable.ic_nothing_ear_l, R.string.left_earbud);
|
||||
final BatteryConfig battery2 = new BatteryConfig(1, R.drawable.ic_nothing_ear_r, R.string.right_earbud);
|
||||
final BatteryConfig battery3 = new BatteryConfig(2, R.drawable.ic_tws_case, R.string.battery_case);
|
||||
return new BatteryConfig[]{battery1, battery2, battery3};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||
final DeviceSpecificSettings settings = new DeviceSpecificSettings();
|
||||
|
||||
settings.addRootScreen(DeviceSpecificSettingsScreen.TOUCH_OPTIONS);
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.TOUCH_OPTIONS, R.xml.devicesettings_oppo_headphones_touch_options);
|
||||
|
||||
settings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS);
|
||||
settings.addSubScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS, R.xml.devicesettings_headphones);
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new OppoHeadphonesSettingsCustomizer(getTouchOptions());
|
||||
}
|
||||
|
||||
protected abstract Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> getTouchOptions();
|
||||
}
|
@ -17,23 +17,43 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.oppo;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
|
||||
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.util.Prefs;
|
||||
|
||||
public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||
private final Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> touchOptions;
|
||||
|
||||
public static final Creator<OppoHeadphonesSettingsCustomizer> CREATOR = new Creator<OppoHeadphonesSettingsCustomizer>() {
|
||||
@Override
|
||||
public OppoHeadphonesSettingsCustomizer createFromParcel(final Parcel in) {
|
||||
return new OppoHeadphonesSettingsCustomizer();
|
||||
final Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> touchOptions = new LinkedHashMap<>();
|
||||
final int numOptions = in.readInt();
|
||||
for (int i = 0; i < numOptions; i++) {
|
||||
final TouchConfigSide touchConfigSide = TouchConfigSide.valueOf(in.readString());
|
||||
final TouchConfigType touchConfigType = TouchConfigType.valueOf(in.readString());
|
||||
final List<TouchConfigValue> values = new ArrayList<>();
|
||||
in.readList(values, TouchConfigValue.class.getClassLoader());
|
||||
touchOptions.put(Pair.create(touchConfigSide, touchConfigType), values);
|
||||
}
|
||||
return new OppoHeadphonesSettingsCustomizer(touchOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -42,15 +62,70 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
}
|
||||
};
|
||||
|
||||
public OppoHeadphonesSettingsCustomizer(final Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> touchOptions) {
|
||||
this.touchOptions = touchOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs, final String rootKey) {
|
||||
final Set<TouchConfigSide> knownSides = new HashSet<>();
|
||||
final Set<TouchConfigType> knownTypes = new HashSet<>();
|
||||
|
||||
for (final Map.Entry<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> e : touchOptions.entrySet()) {
|
||||
final TouchConfigSide side = e.getKey().first;
|
||||
final TouchConfigType type = e.getKey().second;
|
||||
final Set<TouchConfigValue> possibleValues = new HashSet<>(e.getValue());
|
||||
|
||||
knownSides.add(side);
|
||||
knownTypes.add(type);
|
||||
|
||||
final String key = OppoHeadphonesPreferences.getKey(side, type);
|
||||
final ListPreference pref = handler.findPreference(key);
|
||||
if (pref == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final CharSequence[] originalEntries = pref.getEntries();
|
||||
final CharSequence[] originalValues = pref.getEntryValues();
|
||||
final CharSequence[] entries = new CharSequence[possibleValues.size()];
|
||||
final CharSequence[] values = new CharSequence[possibleValues.size()];
|
||||
int j = 0;
|
||||
for (int i = 0; i < originalValues.length; i++) {
|
||||
if (possibleValues.contains(TouchConfigValue.valueOf(originalValues[i].toString().toUpperCase(Locale.ROOT)))) {
|
||||
entries[j] = originalEntries[i];
|
||||
values[j] = originalValues[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
pref.setEntries(entries);
|
||||
pref.setEntryValues(values);
|
||||
|
||||
handler.addPreferenceHandlerFor(key);
|
||||
}
|
||||
|
||||
for (final TouchConfigSide side : TouchConfigSide.values()) {
|
||||
if (!knownSides.contains(side)) {
|
||||
// Side not configurable, hide it completely
|
||||
final Preference header = handler.findPreference("oppo_touch_header_" + side.name().toLowerCase(Locale.ROOT));
|
||||
if (header != null) {
|
||||
header.setVisible(false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (final TouchConfigType type : TouchConfigType.values()) {
|
||||
handler.addPreferenceHandlerFor(OppoHeadphonesPreferences.getKey(side, type));
|
||||
if (!knownTypes.contains(type)) {
|
||||
final String key = OppoHeadphonesPreferences.getKey(side, type);
|
||||
final Preference pref = handler.findPreference(key);
|
||||
if (pref != null) {
|
||||
pref.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,5 +142,11 @@ public class OppoHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
dest.writeInt(touchOptions.size());
|
||||
for (final Map.Entry<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> e : touchOptions.entrySet()) {
|
||||
dest.writeString(e.getKey().first.name());
|
||||
dest.writeString(e.getKey().second.name());
|
||||
dest.writeList(e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
/* 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.devices.realme;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.oppo.OppoHeadphonesCoordinator;
|
||||
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;
|
||||
|
||||
public class RealmeBudsT110Coordinator extends OppoHeadphonesCoordinator {
|
||||
@Override
|
||||
protected Pattern getSupportedDeviceName() {
|
||||
return Pattern.compile("realme Buds T110", Pattern.LITERAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Realme";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeviceNameResource() {
|
||||
return R.string.devicetype_realme_buds_t110;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>> getTouchOptions() {
|
||||
return new LinkedHashMap<Pair<TouchConfigSide, TouchConfigType>, List<TouchConfigValue>>() {{
|
||||
final List<TouchConfigValue> options = Arrays.asList(
|
||||
TouchConfigValue.OFF,
|
||||
TouchConfigValue.PLAY_PAUSE,
|
||||
TouchConfigValue.PREVIOUS,
|
||||
TouchConfigValue.NEXT,
|
||||
TouchConfigValue.VOLUME_UP,
|
||||
TouchConfigValue.VOLUME_DOWN,
|
||||
TouchConfigValue.VOICE_ASSISTANT_REALME
|
||||
);
|
||||
put(Pair.create(TouchConfigSide.LEFT, TouchConfigType.TAP_2), options);
|
||||
put(Pair.create(TouchConfigSide.LEFT, TouchConfigType.TAP_3), options);
|
||||
put(Pair.create(TouchConfigSide.LEFT, TouchConfigType.HOLD), options);
|
||||
put(Pair.create(TouchConfigSide.RIGHT, TouchConfigType.TAP_2), options);
|
||||
put(Pair.create(TouchConfigSide.RIGHT, TouchConfigType.TAP_3), options);
|
||||
put(Pair.create(TouchConfigSide.RIGHT, TouchConfigType.HOLD), options);
|
||||
put(Pair.create(TouchConfigSide.BOTH, TouchConfigType.HOLD), Arrays.asList(
|
||||
TouchConfigValue.OFF,
|
||||
TouchConfigValue.GAME_MODE
|
||||
));
|
||||
}};
|
||||
}
|
||||
}
|
@ -231,6 +231,7 @@ 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.realme.RealmeBudsT110Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.scannable.ScannableDeviceCoordinator;
|
||||
@ -544,6 +545,7 @@ public enum DeviceType {
|
||||
SUPER_CARS(SuperCarsCoordinator.class),
|
||||
ASTEROIDOS(AsteroidOSDeviceCoordinator.class),
|
||||
OPPO_ENCO_AIR(OppoEncoAirCoordinator.class),
|
||||
REALME_BUDS_T110(RealmeBudsT110Coordinator.class),
|
||||
SOFLOW_SO6(SoFlowCoordinator.class),
|
||||
WITHINGS_STEEL_HR(WithingsSteelHRDeviceCoordinator.class),
|
||||
SONY_WENA_3(SonyWena3Coordinator.class),
|
||||
|
@ -66,7 +66,7 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
final int totalLength = BLETypeConversions.toUint16(responseData, i + 1);
|
||||
final int totalLength = responseData[i + 1] & 0xff;
|
||||
if (responseData.length - i < totalLength + 2) {
|
||||
LOG.error("Got partial response with {} bytes, expected {}", responseData.length - i, totalLength + 2);
|
||||
break;
|
||||
@ -99,9 +99,9 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
}
|
||||
|
||||
final short zero = responseBuf.getShort();
|
||||
if (zero != 0) {
|
||||
LOG.error("Unexpected bytes: {}, expected 0", zero);
|
||||
return Collections.emptyList();
|
||||
if (zero != 0 && zero != 4) {
|
||||
// 0 on oppo, 4 on realme?
|
||||
LOG.warn("Unexpected bytes: {}, expected 0 or 4", zero);
|
||||
}
|
||||
|
||||
final short code = responseBuf.getShort();
|
||||
@ -146,10 +146,11 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
break;
|
||||
}
|
||||
|
||||
final String fwString = StringUtils.untilNullTerminator(payload, 1);
|
||||
if (fwString == null) {
|
||||
LOG.warn("Failed to get firmware string");
|
||||
break;
|
||||
final String fwString;
|
||||
if (payload[payload.length - 1] == 0) {
|
||||
fwString = new String(ArrayUtils.subarray(payload, 2, payload.length - 1)).strip();
|
||||
} else {
|
||||
fwString = new String(ArrayUtils.subarray(payload, 2, payload.length - 2)).strip();
|
||||
}
|
||||
final String[] parts = fwString.split(",");
|
||||
if (parts.length % 3 != 0) {
|
||||
@ -180,7 +181,18 @@ public class OppoHeadphonesProtocol extends GBDeviceProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
final String fwVersion = String.join(".", fwVersionParts);
|
||||
final List<String> nonNullParts = new ArrayList<>(fwVersionParts.length);
|
||||
for (int i = 0; i < fwVersionParts.length; i++) {
|
||||
if (fwVersionParts[i] == null) {
|
||||
continue;
|
||||
}
|
||||
nonNullParts.add(fwVersionParts[i]);
|
||||
if (fwVersionParts[i].contains(".")) {
|
||||
// Realme devices have the version already with the dots, repeated multiple times
|
||||
break;
|
||||
}
|
||||
}
|
||||
final String fwVersion = String.join(".", nonNullParts);
|
||||
|
||||
final GBDeviceEventVersionInfo eventVersionInfo = new GBDeviceEventVersionInfo();
|
||||
eventVersionInfo.fwVersion = fwVersion;
|
||||
|
@ -21,6 +21,7 @@ import androidx.annotation.Nullable;
|
||||
public enum TouchConfigSide {
|
||||
LEFT(0x01),
|
||||
RIGHT(0x02),
|
||||
BOTH(0x04),
|
||||
;
|
||||
|
||||
private final int code;
|
||||
|
@ -21,7 +21,8 @@ import androidx.annotation.Nullable;
|
||||
public enum TouchConfigValue {
|
||||
OFF(0x00),
|
||||
PLAY_PAUSE(0x01),
|
||||
VOICE_ASSISTANT(0x03),
|
||||
VOICE_ASSISTANT(0x03), // oppo
|
||||
VOICE_ASSISTANT_REALME(0x04),
|
||||
PREVIOUS(0x05),
|
||||
NEXT(0x06),
|
||||
GAME_MODE(0x11),
|
||||
|
@ -3566,44 +3566,30 @@
|
||||
<item>as_off</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="oppo_touch_tap_2_names">
|
||||
<string-array name="oppo_touch_tap_names">
|
||||
<!-- superset of values, unsupported are hidden by the coordinator -->
|
||||
<item>@string/sony_button_mode_off</item>
|
||||
<item>@string/moondrop_touch_action_play_pause</item>
|
||||
<item>@string/pref_media_previous</item>
|
||||
<item>@string/pref_media_next</item>
|
||||
<item>@string/pref_media_volumeup</item>
|
||||
<item>@string/pref_media_volumedown</item>
|
||||
<item>@string/pref_title_touch_voice_assistant</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="oppo_touch_tap_2_values">
|
||||
<item>off</item>
|
||||
<item>play_pause</item>
|
||||
<item>previous</item>
|
||||
<item>next</item>
|
||||
<item>voice_assistant</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="oppo_touch_tap_3_names">
|
||||
<item>@string/sony_button_mode_off</item>
|
||||
<item>@string/pref_title_touch_voice_assistant</item>
|
||||
<item>@string/prefs_game_mode</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="oppo_touch_tap_3_values">
|
||||
<item>off</item>
|
||||
<item>voice_assistant</item>
|
||||
<item>game_mode</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="oppo_touch_hold_names">
|
||||
<item>@string/sony_button_mode_off</item>
|
||||
<item>@string/pref_media_volumeup</item>
|
||||
<item>@string/pref_media_volumedown</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="oppo_touch_hold_values">
|
||||
<string-array name="oppo_touch_tap_values">
|
||||
<!-- superset of values, unsupported are hidden by the coordinator -->
|
||||
<item>off</item>
|
||||
<item>play_pause</item>
|
||||
<item>previous</item>
|
||||
<item>next</item>
|
||||
<item>volume_up</item>
|
||||
<item>volume_down</item>
|
||||
<item>voice_assistant</item>
|
||||
<item>voice_assistant_realme</item>
|
||||
<item>game_mode</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="soundcore_button_function_names">
|
||||
|
@ -2489,6 +2489,7 @@
|
||||
<string name="devicetype_nothing_cmf_watch_pro">CMF Watch Pro</string>
|
||||
<string name="devicetype_nothing_cmf_watch_pro_2">CMF Watch Pro 2</string>
|
||||
<string name="devicetype_oppo_enco_air">Oppo Enco Air</string>
|
||||
<string name="devicetype_realme_buds_t110">Realme Buds T110</string>
|
||||
<string name="devicetype_galaxybuds">Galaxy Buds</string>
|
||||
<string name="devicetype_galaxybuds_live">Galaxy Buds Live</string>
|
||||
<string name="devicetype_galaxybuds_pro">Galaxy Buds Pro</string>
|
||||
|
@ -3,28 +3,29 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="oppo_touch_header_left"
|
||||
android:title="@string/left_earbud"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
android:defaultValue="none"
|
||||
android:entries="@array/oppo_touch_tap_2_names"
|
||||
android:entryValues="@array/oppo_touch_tap_2_values"
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/oppo_touch_tap_names"
|
||||
android:entryValues="@array/oppo_touch_tap_values"
|
||||
android:icon="@drawable/ic_filter_2"
|
||||
android:key="oppo_touch__left__tap_2"
|
||||
android:summary="%s"
|
||||
android:title="@string/double_tap" />
|
||||
<ListPreference
|
||||
android:defaultValue="none"
|
||||
android:entries="@array/oppo_touch_tap_3_names"
|
||||
android:entryValues="@array/oppo_touch_tap_3_values"
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/oppo_touch_tap_names"
|
||||
android:entryValues="@array/oppo_touch_tap_values"
|
||||
android:icon="@drawable/ic_filter_3"
|
||||
android:key="oppo_touch__left__tap_3"
|
||||
android:summary="%s"
|
||||
android:title="@string/triple_tap" />
|
||||
<ListPreference
|
||||
android:defaultValue="none"
|
||||
android:entries="@array/oppo_touch_hold_names"
|
||||
android:entryValues="@array/oppo_touch_hold_values"
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/oppo_touch_tap_names"
|
||||
android:entryValues="@array/oppo_touch_tap_values"
|
||||
android:icon="@drawable/ic_horizontal_rule"
|
||||
android:key="oppo_touch__left__hold"
|
||||
android:summary="%s"
|
||||
@ -32,31 +33,46 @@
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="oppo_touch_header_right"
|
||||
android:title="@string/right_earbud"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
android:defaultValue="none"
|
||||
android:entries="@array/oppo_touch_tap_2_names"
|
||||
android:entryValues="@array/oppo_touch_tap_2_values"
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/oppo_touch_tap_names"
|
||||
android:entryValues="@array/oppo_touch_tap_values"
|
||||
android:icon="@drawable/ic_filter_2"
|
||||
android:key="oppo_touch__right__tap_2"
|
||||
android:summary="%s"
|
||||
android:title="@string/double_tap" />
|
||||
<ListPreference
|
||||
android:defaultValue="none"
|
||||
android:entries="@array/oppo_touch_tap_3_names"
|
||||
android:entryValues="@array/oppo_touch_tap_3_values"
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/oppo_touch_tap_names"
|
||||
android:entryValues="@array/oppo_touch_tap_values"
|
||||
android:icon="@drawable/ic_filter_3"
|
||||
android:key="oppo_touch__right__tap_3"
|
||||
android:summary="%s"
|
||||
android:title="@string/triple_tap" />
|
||||
<ListPreference
|
||||
android:defaultValue="none"
|
||||
android:entries="@array/oppo_touch_hold_names"
|
||||
android:entryValues="@array/oppo_touch_hold_values"
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/oppo_touch_tap_names"
|
||||
android:entryValues="@array/oppo_touch_tap_values"
|
||||
android:icon="@drawable/ic_horizontal_rule"
|
||||
android:key="oppo_touch__right__hold"
|
||||
android:summary="%s"
|
||||
android:title="@string/long_press" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="oppo_touch_header_both"
|
||||
android:title="@string/moondrop_touch_earbud_both"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/oppo_touch_tap_names"
|
||||
android:entryValues="@array/oppo_touch_tap_values"
|
||||
android:icon="@drawable/ic_horizontal_rule"
|
||||
android:key="oppo_touch__both__hold"
|
||||
android:summary="%s"
|
||||
android:title="@string/long_press" />
|
||||
</PreferenceCategory>
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
@ -0,0 +1,27 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.oppo;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.test.TestBase;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class OppoHeadphonesProtocolTest extends TestBase {
|
||||
@Test
|
||||
public void testHandleFirmware() {
|
||||
final OppoHeadphonesProtocol protocol = new OppoHeadphonesProtocol(null);
|
||||
GBDeviceEvent[] oppoEvents = protocol.decodeResponse(GB.hexStringToByteArray("aa4f00000581f34800000a312c312c312c312c322c3136302c312c332c3838382c312c342c302c322c312c312c322c322c3136302c322c332c3838382c322c342c302c332c312c312c332c322c38323700"));
|
||||
Assert.assertEquals(1, oppoEvents.length);
|
||||
GBDeviceEventVersionInfo oppoEvent = (GBDeviceEventVersionInfo) oppoEvents[0];
|
||||
Assert.assertEquals("160.160.827", oppoEvent.fwVersion);
|
||||
|
||||
GBDeviceEvent[] realme = protocol.decodeResponse(GB.hexStringToByteArray("aa2a040005810e23000003312c322c312e312e302e37352c322c322c312e312e302e37352c332c322c303031"));
|
||||
Assert.assertEquals(1, realme.length);
|
||||
GBDeviceEventVersionInfo realmeEvent = (GBDeviceEventVersionInfo) realme[0];
|
||||
Assert.assertEquals("1.1.0.75", realmeEvent.fwVersion);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user