mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-24 09:35:50 +01:00
Add support for Redmi Buds 5 Pro (#4343)
This PR introduces support for the Redmi Buds 5 Pro earbuds. Working: - Connection and authentication; - Firmware version; - Battery percentage; - Switching between ANC/Transparency/OFF, their relative strength settings and Adaptive ANC toggle; - Configuring all touch options; - Ear detection play/pause, Auto call answer, Double connection, Adaptive sound settings toggles; - Equalizer presets and custom curve; Not working: - Personalized ANC (code for toggle is present but commented-out, as the logic for ear calibration is missing); - Spatial Audio - Find my earbuds - Firmware Update Closes #3566 Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/4343 Co-authored-by: Jonathan Gobbo <jonathangobbo@outlook.it> Co-committed-by: Jonathan Gobbo <jonathangobbo@outlook.it>
This commit is contained in:
parent
9270bd9003
commit
287a759e28
@ -365,6 +365,39 @@ public class DeviceSettingsPreferenceConst {
|
||||
public static final String PREF_GALAXY_BUDS_PRO_ANC_LEVEL="pref_galaxy_buds_pro_anc_level";
|
||||
public static final String PREFS_GALAXY_BUDS_SEAMLESS_CONNECTION="prefs_galaxy_buds_seamless_connection";
|
||||
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL="pref_redmi_buds_5_pro_ambient_sound_control";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_NOISE_CANCELLING_STRENGTH="pref_redmi_buds_5_pro_noise_cancelling_strength";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_TRANSPARENCY_STRENGTH="pref_redmi_buds_5_pro_transparency_strength";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_ADAPTIVE_NOISE_CANCELLING="pref_redmi_buds_5_pro_adaptive_noise_cancelling";
|
||||
// public static final String PREF_REDMI_BUDS_5_PRO_PERSONALIZED_NOISE_CANCELLING="pref_redmi_buds_5_pro_personalized_noise_cancelling";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_LEFT="pref_redmi_buds_5_pro_control_single_tap_left";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_RIGHT="pref_redmi_buds_5_pro_control_single_tap_right";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_LEFT="pref_redmi_buds_5_pro_control_double_tap_left";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_RIGHT="pref_redmi_buds_5_pro_control_double_tap_right";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_LEFT="pref_redmi_buds_5_pro_control_triple_tap_left";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_RIGHT="pref_redmi_buds_5_pro_control_triple_tap_right";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_LEFT ="pref_redmi_buds_5_pro_control_long_tap_mode_left";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_RIGHT ="pref_redmi_buds_5_pro_control_long_tap_mode_right";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_LEFT ="pref_redmi_buds_5_pro_control_long_tap_settings_left";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_RIGHT ="pref_redmi_buds_5_pro_control_long_tap_settings_right";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_WEARING_DETECTION="pref_redmi_buds_5_pro_wearing_detection";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_AUTO_REPLY_PHONECALL="pref_redmi_buds_5_pro_auto_reply_phonecall";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_DOUBLE_CONNECTION="pref_redmi_buds_5_pro_double_connection";
|
||||
// public static final String PREF_REDMI_BUDS_5_PRO_SURROUND_SOUND="pref_redmi_buds_5_pro_surround_sound";
|
||||
// public static final String PREF_REDMI_BUDS_5_PRO_SURROUND_SOUND_MODE="pref_redmi_buds_5_pro_surround_sound_mode";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_PRESET="pref_redmi_buds_5_pro_equalizer_preset";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_62="pref_redmi_buds_5_pro_equalizer_band_62";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_125="pref_redmi_buds_5_pro_equalizer_band_125";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_250="pref_redmi_buds_5_pro_equalizer_band_250";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_500="pref_redmi_buds_5_pro_equalizer_band_500";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_1k="pref_redmi_buds_5_pro_equalizer_band_1k";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_2k="pref_redmi_buds_5_pro_equalizer_band_2k";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_4k="pref_redmi_buds_5_pro_equalizer_band_4k";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_8k="pref_redmi_buds_5_pro_equalizer_band_8k";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_12k="pref_redmi_buds_5_pro_equalizer_band_12k";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_16k="pref_redmi_buds_5_pro_equalizer_band_16k";
|
||||
public static final String PREF_REDMI_BUDS_5_PRO_ADAPTIVE_SOUND="pref_redmi_buds_5_pro_adaptive_sound";
|
||||
|
||||
public static final String PREF_SONY_AUDIO_CODEC = "pref_sony_audio_codec";
|
||||
public static final String PREF_SONY_PROTOCOL_VERSION = "pref_protocol_version";
|
||||
public static final String PREF_SONY_ACTUAL_PROTOCOL_VERSION = "pref_actual_protocol_version";
|
||||
|
@ -676,6 +676,39 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
|
||||
|
||||
addPreferenceHandlerFor(PREF_WATCHFACE);
|
||||
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_NOISE_CANCELLING_STRENGTH);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_TRANSPARENCY_STRENGTH);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_ADAPTIVE_NOISE_CANCELLING);
|
||||
// addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_PERSONALIZED_NOISE_CANCELLING);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_LEFT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_RIGHT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_LEFT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_RIGHT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_LEFT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_RIGHT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_LEFT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_RIGHT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_LEFT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_RIGHT);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_WEARING_DETECTION);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_AUTO_REPLY_PHONECALL);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_DOUBLE_CONNECTION);
|
||||
// addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_SURROUND_SOUND);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_ADAPTIVE_SOUND);
|
||||
// addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_SURROUND_SOUND_MODE);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_PRESET);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_62);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_125);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_250);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_500);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_1k);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_2k);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_4k);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_8k);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_12k);
|
||||
addPreferenceHandlerFor(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_16k);
|
||||
|
||||
addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_CONTROL);
|
||||
addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE);
|
||||
addPreferenceHandlerFor(PREF_SONY_FOCUS_VOICE);
|
||||
|
@ -0,0 +1,103 @@
|
||||
/* Copyright (C) 2024 Jonathan Gobbo
|
||||
|
||||
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.xiaomi.redmibuds5pro;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
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.devices.AbstractDeviceCoordinator;
|
||||
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.redmibuds5pro.RedmiBuds5ProDeviceSupport;
|
||||
|
||||
public class RedmiBuds5ProCoordinator extends AbstractDeviceCoordinator {
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Xiaomi";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return RedmiBuds5ProDeviceSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeviceNameResource() {
|
||||
return R.string.devicetype_redmi_buds_5_pro;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pattern getSupportedDeviceName() {
|
||||
return Pattern.compile("Redmi Buds 5 Pro");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle() {
|
||||
return BONDING_STYLE_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatteryCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryConfig[] getBatteryConfig(GBDevice device) {
|
||||
BatteryConfig battery1 = new BatteryConfig(0, R.drawable.ic_tws_case, R.string.battery_case);
|
||||
BatteryConfig battery2 = new BatteryConfig(1, R.drawable.ic_nothing_ear_l, R.string.left_earbud);
|
||||
BatteryConfig battery3 = new BatteryConfig(2, R.drawable.ic_nothing_ear_r, R.string.right_earbud);
|
||||
return new BatteryConfig[]{battery1, battery2, battery3};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_redmibuds5pro_headphones);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_redmibuds5pro_gestures);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_redmibuds5pro_sound);
|
||||
return deviceSpecificSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new RedmiBuds5ProSettingsCustomizer(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultIconResource() {
|
||||
return R.drawable.ic_device_nothingear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisabledIconResource() {
|
||||
return R.drawable.ic_device_nothingear_disabled;
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/* Copyright (C) 2024 Jonathan Gobbo
|
||||
|
||||
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.xiaomi.redmibuds5pro;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class RedmiBuds5ProSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||
|
||||
final GBDevice device;
|
||||
|
||||
public RedmiBuds5ProSettingsCustomizer(final GBDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onPreferenceChange(Preference preference, DeviceSpecificSettingsHandler handler) {
|
||||
|
||||
// Hide or Show ANC/Transparency settings according to the current ambient sound control mode
|
||||
if (preference.getKey().equals(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL)) {
|
||||
String mode = ((ListPreference) preference).getValue();
|
||||
final Preference ancLevel = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_NOISE_CANCELLING_STRENGTH);
|
||||
final Preference transparencyLevel = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_TRANSPARENCY_STRENGTH);
|
||||
final Preference adaptiveAnc = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_ADAPTIVE_NOISE_CANCELLING);
|
||||
// final Preference customizedAnc = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_PERSONALIZED_NOISE_CANCELLING);
|
||||
if (ancLevel != null) {
|
||||
ancLevel.setVisible(mode.equals("1"));
|
||||
}
|
||||
if (transparencyLevel != null) {
|
||||
transparencyLevel.setVisible(mode.equals("2"));
|
||||
}
|
||||
if (adaptiveAnc != null) {
|
||||
adaptiveAnc.setVisible(mode.equals("1"));
|
||||
}
|
||||
// if (customizedAnc != null) {
|
||||
// customizedAnc.setVisible(mode.equals("1"));
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeSettings(DeviceSpecificSettingsHandler handler, Prefs prefs, String rootKey) {
|
||||
|
||||
final ListPreference longPressLeft = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_LEFT);
|
||||
final ListPreference longPressRight = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_RIGHT);
|
||||
|
||||
final Preference longPressLeftSettings = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_LEFT);
|
||||
final Preference longPressRightSettings = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_RIGHT);
|
||||
|
||||
final ListPreference equalizerPreset = handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_PRESET);
|
||||
|
||||
if (longPressLeft != null) {
|
||||
final Preference.OnPreferenceChangeListener longPressLeftButtonListener = (preference, newVal) -> {
|
||||
String mode = newVal.toString();
|
||||
if (longPressLeftSettings != null) {
|
||||
longPressLeftSettings.setVisible(mode.equals("6"));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
longPressLeftButtonListener.onPreferenceChange(longPressLeft, prefs.getString(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_LEFT, "6"));
|
||||
handler.addPreferenceHandlerFor(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_LEFT, longPressLeftButtonListener);
|
||||
}
|
||||
if (longPressRight != null) {
|
||||
final Preference.OnPreferenceChangeListener longPressRightButtonListener = (preference, newVal) -> {
|
||||
String mode = newVal.toString();
|
||||
if (longPressRightSettings != null) {
|
||||
longPressRightSettings.setVisible(mode.equals("6"));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
longPressRightButtonListener.onPreferenceChange(longPressRight, prefs.getString(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_RIGHT, "6"));
|
||||
handler.addPreferenceHandlerFor(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_RIGHT, longPressRightButtonListener);
|
||||
}
|
||||
|
||||
if (equalizerPreset != null) {
|
||||
|
||||
final Preference.OnPreferenceChangeListener equalizerPresetListener = (preference, newVal) -> {
|
||||
|
||||
final List<Preference> prefsToDisable = Arrays.asList(
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_62),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_125),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_250),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_500),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_1k),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_2k),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_4k),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_8k),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_12k),
|
||||
handler.findPreference(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_16k)
|
||||
);
|
||||
|
||||
String mode = newVal.toString();
|
||||
for (Preference pref : prefsToDisable) {
|
||||
if (pref != null) {
|
||||
pref.setEnabled(mode.equals("10"));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
equalizerPresetListener.onPreferenceChange(equalizerPreset, prefs.getString(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_PRESET, "0"));
|
||||
handler.addPreferenceHandlerFor(DeviceSettingsPreferenceConst.PREF_REDMI_BUDS_5_PRO_EQUALIZER_PRESET, equalizerPresetListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPreferenceKeysWithSummary() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel parcel, int i) {
|
||||
parcel.writeParcelable(device, 0);
|
||||
}
|
||||
|
||||
public static final Creator<RedmiBuds5ProSettingsCustomizer> CREATOR = new Creator<RedmiBuds5ProSettingsCustomizer>() {
|
||||
@Override
|
||||
public RedmiBuds5ProSettingsCustomizer createFromParcel(final Parcel in) {
|
||||
final GBDevice device = in.readParcelable(RedmiBuds5ProSettingsCustomizer.class.getClassLoader());
|
||||
return new RedmiBuds5ProSettingsCustomizer(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedmiBuds5ProSettingsCustomizer[] newArray(final int size) {
|
||||
return new RedmiBuds5ProSettingsCustomizer[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmibuds5pro.prefs;
|
||||
|
||||
public class Configuration {
|
||||
|
||||
public enum Config {
|
||||
GESTURES((byte) 0x02),
|
||||
AUTO_ANSWER((byte) 0x03),
|
||||
DOUBLE_CONNECTION((byte) 0x04),
|
||||
EAR_DETECTION((byte) 0x06),
|
||||
EQ_PRESET((byte) 0x07),
|
||||
LONG_GESTURES((byte) 0x0a),
|
||||
EFFECT_STRENGTH((byte) 0x0b),
|
||||
ADAPTIVE_ANC((byte) 0x25),
|
||||
ADAPTIVE_SOUND((byte) 0x29),
|
||||
EQ_CURVE((byte) 0x37),
|
||||
CUSTOMIZED_ANC((byte) 0x3b),
|
||||
UNKNOWN((byte) 0xff);
|
||||
|
||||
public final byte value;
|
||||
|
||||
Config(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Config fromCode(final byte code) {
|
||||
for (final Config config : values()) {
|
||||
if (config.value == code) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return Config.UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum StrengthTarget {
|
||||
ANC((byte) 0x01),
|
||||
TRANSPARENCY((byte) 0x02);
|
||||
|
||||
public final byte value;
|
||||
|
||||
StrengthTarget(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmibuds5pro.prefs;
|
||||
|
||||
public class Gestures {
|
||||
|
||||
public enum InteractionType {
|
||||
SINGLE((byte) 0x04),
|
||||
DOUBLE((byte) 0x01),
|
||||
TRIPLE((byte) 0x02),
|
||||
LONG((byte) 0x03);
|
||||
|
||||
public final byte value;
|
||||
|
||||
InteractionType(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Position {
|
||||
LEFT,
|
||||
RIGHT
|
||||
}
|
||||
}
|
@ -274,6 +274,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.miband8pro.MiBand8Pro
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.miband9.MiBand9Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.miwatch.MiWatchLiteCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.miwatchcolorsport.MiWatchColorSportCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmibuds5pro.RedmiBuds5ProCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmismartband2.RedmiSmartBand2Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmismartbandpro.RedmiSmartBandProCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmiwatch2.RedmiWatch2Coordinator;
|
||||
@ -346,6 +347,7 @@ public enum DeviceType {
|
||||
MIBAND9(MiBand9Coordinator.class),
|
||||
MIWATCHLITE(MiWatchLiteCoordinator.class),
|
||||
MIWATCHCOLORSPORT(MiWatchColorSportCoordinator.class),
|
||||
REDMIBUDS5PRO(RedmiBuds5ProCoordinator.class),
|
||||
REDMIWATCH3ACTIVE(RedmiWatch3ActiveCoordinator.class),
|
||||
REDMIWATCH3(RedmiWatch3Coordinator.class),
|
||||
REDMISMARTBAND2(RedmiSmartBand2Coordinator.class),
|
||||
|
@ -0,0 +1,46 @@
|
||||
/* Copyright (C) 2024 Jonathan Gobbo
|
||||
|
||||
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.redmibuds5pro;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
|
||||
public class RedmiBuds5ProDeviceSupport extends AbstractSerialDeviceSupport {
|
||||
@Override
|
||||
protected GBDeviceProtocol createDeviceProtocol() {
|
||||
return new RedmiBuds5ProProtocol(getDevice());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GBDeviceIoThread createDeviceIOThread() {
|
||||
return new RedmiBuds5ProIOThread(getDevice(), getContext(),
|
||||
(RedmiBuds5ProProtocol) getDeviceProtocol(),
|
||||
RedmiBuds5ProDeviceSupport.this, getBluetoothAdapter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
getDeviceIOThread().start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/* Copyright (C) 2024 Jonathan Gobbo
|
||||
|
||||
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.redmibuds5pro;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
|
||||
|
||||
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.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread;
|
||||
|
||||
public class RedmiBuds5ProIOThread extends BtClassicIoThread {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RedmiBuds5ProIOThread.class);
|
||||
private final RedmiBuds5ProProtocol redmiProtocol;
|
||||
|
||||
public RedmiBuds5ProIOThread(GBDevice gbDevice, Context context, RedmiBuds5ProProtocol redmiProtocol, RedmiBuds5ProDeviceSupport deviceSupport, BluetoothAdapter btAdapter) {
|
||||
super(gbDevice, context, redmiProtocol, deviceSupport, btAdapter);
|
||||
this.redmiProtocol = redmiProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] parseIncoming(InputStream stream) throws IOException {
|
||||
byte[] buffer = new byte[1048576];
|
||||
int bytes = stream.read(buffer);
|
||||
LOG.debug("read {} bytes. {}", bytes, hexdump(buffer, 0, bytes));
|
||||
return Arrays.copyOf(buffer, bytes);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected UUID getUuidToConnect(@NonNull ParcelUuid[] uuids) {
|
||||
return this.redmiProtocol.UUID_DEVICE_CTRL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
write(redmiProtocol.encodeStartAuthentication());
|
||||
setUpdateState(GBDevice.State.INITIALIZING);
|
||||
}
|
||||
}
|
@ -0,0 +1,548 @@
|
||||
/* Copyright (C) 2024 Jonathan Gobbo
|
||||
|
||||
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.redmibuds5pro;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdateDeviceState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmibuds5pro.prefs.Configuration.Config;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmibuds5pro.prefs.Configuration.StrengthTarget;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmibuds5pro.prefs.Gestures.InteractionType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.redmibuds5pro.prefs.Gestures.Position;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.redmibuds5pro.protocol.Authentication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.redmibuds5pro.protocol.Message;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.redmibuds5pro.protocol.MessageType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.redmibuds5pro.protocol.Opcode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class RedmiBuds5ProProtocol extends GBDeviceProtocol {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RedmiBuds5ProProtocol.class);
|
||||
final UUID UUID_DEVICE_CTRL = UUID.fromString("0000fd2d-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
private byte sequenceNumber = 0;
|
||||
|
||||
protected RedmiBuds5ProProtocol(GBDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
public byte[] encodeStartAuthentication() {
|
||||
byte[] authRnd = Authentication.getRandomChallenge();
|
||||
LOG.debug("[AUTH] Sending challenge: {}", hexdump(authRnd));
|
||||
|
||||
byte[] payload = new byte[17];
|
||||
payload[0] = 0x01;
|
||||
System.arraycopy(authRnd, 0, payload, 1, 16);
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.AUTH_CHALLENGE, sequenceNumber++, payload).encode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeSendConfiguration(String config) {
|
||||
switch (config) {
|
||||
case PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL:
|
||||
return encodeSetAmbientSoundControl();
|
||||
case PREF_REDMI_BUDS_5_PRO_NOISE_CANCELLING_STRENGTH:
|
||||
return encodeSetEffectStrength(config, StrengthTarget.ANC);
|
||||
case PREF_REDMI_BUDS_5_PRO_TRANSPARENCY_STRENGTH:
|
||||
return encodeSetEffectStrength(config, StrengthTarget.TRANSPARENCY);
|
||||
case PREF_REDMI_BUDS_5_PRO_ADAPTIVE_NOISE_CANCELLING:
|
||||
return encodeSetBooleanConfig(config, Config.ADAPTIVE_ANC);
|
||||
// case PREF_REDMI_BUDS_5_PRO_PERSONALIZED_NOISE_CANCELLING:
|
||||
// return encodeSetBooleanConfig(config, Config.CUSTOMIZED_ANC);
|
||||
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_LEFT:
|
||||
return encodeSetGesture(config, InteractionType.SINGLE, Position.LEFT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_RIGHT:
|
||||
return encodeSetGesture(config, InteractionType.SINGLE, Position.RIGHT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_LEFT:
|
||||
return encodeSetGesture(config, InteractionType.DOUBLE, Position.LEFT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_RIGHT:
|
||||
return encodeSetGesture(config, InteractionType.DOUBLE, Position.RIGHT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_LEFT:
|
||||
return encodeSetGesture(config, InteractionType.TRIPLE, Position.LEFT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_RIGHT:
|
||||
return encodeSetGesture(config, InteractionType.TRIPLE, Position.RIGHT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_LEFT:
|
||||
return encodeSetGesture(config, InteractionType.LONG, Position.LEFT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_RIGHT:
|
||||
return encodeSetGesture(config, InteractionType.LONG, Position.RIGHT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_LEFT:
|
||||
return encodeSetLongGestureMode(config, Position.LEFT);
|
||||
case PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_RIGHT:
|
||||
return encodeSetLongGestureMode(config, Position.RIGHT);
|
||||
|
||||
case PREF_REDMI_BUDS_5_PRO_WEARING_DETECTION:
|
||||
return encodeSetEarDetection();
|
||||
case PREF_REDMI_BUDS_5_PRO_AUTO_REPLY_PHONECALL:
|
||||
return encodeSetBooleanConfig(config, Config.AUTO_ANSWER);
|
||||
case PREF_REDMI_BUDS_5_PRO_DOUBLE_CONNECTION:
|
||||
return encodeSetBooleanConfig(config, Config.DOUBLE_CONNECTION);
|
||||
case PREF_REDMI_BUDS_5_PRO_ADAPTIVE_SOUND:
|
||||
return encodeSetBooleanConfig(config, Config.ADAPTIVE_SOUND);
|
||||
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_PRESET:
|
||||
return encodeSetIntegerConfig(config, Config.EQ_PRESET);
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_62:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_125:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_250:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_500:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_1k:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_2k:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_4k:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_8k:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_12k:
|
||||
case PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_16k:
|
||||
return encodeSetCustomEqualizer();
|
||||
|
||||
default:
|
||||
LOG.debug("Unsupported config: {}", config);
|
||||
}
|
||||
|
||||
return super.encodeSendConfiguration(config);
|
||||
}
|
||||
|
||||
public byte[] encodeSetCustomEqualizer() {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
|
||||
List<String> bands = List.of(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_62, PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_125,
|
||||
PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_250, PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_500, PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_1k,
|
||||
PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_2k, PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_4k, PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_8k,
|
||||
PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_12k, PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_16k);
|
||||
|
||||
byte[] eqCurve = new byte[10];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
eqCurve[i] = (byte) Integer.parseInt(prefs.getString(bands.get(i), "0"));
|
||||
}
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.SET_CONFIG, sequenceNumber++, new byte[]{
|
||||
0x24, 0x00, 0x37, 0x05, 0x01, 0x01, 0x0A,
|
||||
0x00, 0x3E, eqCurve[0], 0x00, 0x7D, eqCurve[1],
|
||||
0x00, (byte) 0xFA, eqCurve[2], 0x01, (byte) 0xF4, eqCurve[3],
|
||||
0x03, (byte) 0xE8, eqCurve[4], 0x07, (byte) 0xE0, eqCurve[5],
|
||||
0x0F, (byte) 0xA0, eqCurve[6], 0x1F, 0x40, eqCurve[7],
|
||||
0x2E, (byte) 0xE0, eqCurve[8], 0x3E, (byte) 0x80, eqCurve[9]
|
||||
}).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeSetEarDetection() {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte value = (byte) (prefs.getBoolean(PREF_REDMI_BUDS_5_PRO_WEARING_DETECTION, false) ? 0x00 : 0x01);
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.ANC, sequenceNumber++, new byte[]{0x02, 0x06, value}).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeSetLongGestureMode(String config, Position position) {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte value = (byte) Integer.parseInt(prefs.getString(config, "7"));
|
||||
byte[] payload = new byte[]{0x04, 0x00, 0x0a, (byte) 0xFF, (byte) 0xFF};
|
||||
if (position == Position.LEFT) {
|
||||
payload[3] = value;
|
||||
} else {
|
||||
payload[4] = value;
|
||||
}
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.SET_CONFIG, sequenceNumber++, payload).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeSetGesture(String config, InteractionType interactionType, Position position) {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte value = (byte) Integer.parseInt(prefs.getString(config, "1"));
|
||||
byte[] payload = new byte[]{0x05, 0x00, 0x02, interactionType.value, (byte) 0xFF, (byte) 0xFF};
|
||||
if (position == Position.LEFT) {
|
||||
payload[4] = value;
|
||||
} else {
|
||||
payload[5] = value;
|
||||
}
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.SET_CONFIG, sequenceNumber++, payload).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeSetEffectStrength(String pref, StrengthTarget effect) {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte mode = (byte) Integer.parseInt(prefs.getString(pref, "0"));
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.SET_CONFIG, sequenceNumber++, new byte[]{0x04, 0x00, 0x0b, effect.value, mode}).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeSetIntegerConfig(String pref, Config config) {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte value = (byte) Integer.parseInt(prefs.getString(pref, "0"));
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.SET_CONFIG, sequenceNumber++, new byte[]{0x03, 0x00, config.value, value}).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeSetBooleanConfig(String pref, Config config) {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte value = (byte) (prefs.getBoolean(pref, false) ? 0x01 : 0x00);
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.SET_CONFIG, sequenceNumber++, new byte[]{0x03, 0x00, config.value, value}).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeGetConfig() {
|
||||
List<Config> configs = List.of(Config.EFFECT_STRENGTH, Config.ADAPTIVE_ANC, // Config.CUSTOMIZED_ANC,
|
||||
Config.GESTURES, Config.LONG_GESTURES, Config.EAR_DETECTION, Config.DOUBLE_CONNECTION,
|
||||
Config.AUTO_ANSWER, Config.ADAPTIVE_SOUND, Config.EQ_PRESET, Config.EQ_CURVE);
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
for (Config config : configs) {
|
||||
Message message = new Message(MessageType.PHONE_REQUEST, Opcode.GET_CONFIG, sequenceNumber++, new byte[]{0x00, config.value});
|
||||
outputStream.write(message.encode());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] encodeSetAmbientSoundControl() {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte mode = (byte) Integer.parseInt(prefs.getString(PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL, "0"));
|
||||
return new Message(MessageType.PHONE_REQUEST, Opcode.ANC, sequenceNumber++, new byte[]{0x02, 0x04, mode}).encode();
|
||||
}
|
||||
|
||||
public void decodeGetConfig(byte[] configPayload) {
|
||||
|
||||
SharedPreferences preferences = getDevicePrefs().getPreferences();
|
||||
Editor editor = preferences.edit();
|
||||
Config config = Config.fromCode(configPayload[2]);
|
||||
switch (config) {
|
||||
case GESTURES:
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_LEFT, Integer.toString(configPayload[4]));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_SINGLE_TAP_RIGHT, Integer.toString(configPayload[5]));
|
||||
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_LEFT, Integer.toString(configPayload[7]));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_DOUBLE_TAP_RIGHT, Integer.toString(configPayload[8]));
|
||||
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_LEFT, Integer.toString(configPayload[10]));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_TRIPLE_TAP_RIGHT, Integer.toString(configPayload[11]));
|
||||
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_LEFT, Integer.toString(configPayload[13]));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_MODE_RIGHT, Integer.toString(configPayload[14]));
|
||||
break;
|
||||
case AUTO_ANSWER:
|
||||
editor.putBoolean(PREF_REDMI_BUDS_5_PRO_AUTO_REPLY_PHONECALL, configPayload[3] == 0x01);
|
||||
break;
|
||||
case DOUBLE_CONNECTION:
|
||||
editor.putBoolean(PREF_REDMI_BUDS_5_PRO_DOUBLE_CONNECTION, configPayload[3] == 0x01);
|
||||
break;
|
||||
case EQ_PRESET:
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_PRESET, Integer.toString(configPayload[3]));
|
||||
break;
|
||||
case LONG_GESTURES:
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_LEFT, Integer.toString(configPayload[3]));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_CONTROL_LONG_TAP_SETTINGS_RIGHT, Integer.toString(configPayload[4]));
|
||||
break;
|
||||
case EFFECT_STRENGTH:
|
||||
byte mode = configPayload[4];
|
||||
if (configPayload[3] == StrengthTarget.ANC.value) {
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_NOISE_CANCELLING_STRENGTH, Integer.toString(mode));
|
||||
} else if (configPayload[3] == StrengthTarget.TRANSPARENCY.value) {
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_TRANSPARENCY_STRENGTH, Integer.toString(mode));
|
||||
}
|
||||
break;
|
||||
case ADAPTIVE_ANC:
|
||||
editor.putBoolean(PREF_REDMI_BUDS_5_PRO_ADAPTIVE_NOISE_CANCELLING, configPayload[3] == 0x01);
|
||||
break;
|
||||
case ADAPTIVE_SOUND:
|
||||
editor.putBoolean(PREF_REDMI_BUDS_5_PRO_ADAPTIVE_SOUND, configPayload[3] == 0x01);
|
||||
break;
|
||||
case EQ_CURVE:
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_62, Integer.toString(configPayload[12] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_125, Integer.toString(configPayload[15] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_250, Integer.toString(configPayload[18] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_500, Integer.toString(configPayload[21] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_1k, Integer.toString(configPayload[24] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_2k, Integer.toString(configPayload[27] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_4k, Integer.toString(configPayload[30] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_8k, Integer.toString(configPayload[33] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_12k, Integer.toString(configPayload[36] & 0xFF));
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_EQUALIZER_BAND_16k, Integer.toString(configPayload[39] & 0xFF));
|
||||
break;
|
||||
// case CUSTOMIZED_ANC:
|
||||
// editor.putBoolean(PREF_REDMI_BUDS_5_PRO_PERSONALIZED_NOISE_CANCELLING, configPayload[3] == 0x01);
|
||||
// break;
|
||||
default:
|
||||
LOG.debug("Unhandled device update: {}", hexdump(configPayload));
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private GBDeviceEventBatteryInfo parseBatteryInfo(byte batteryInfo, int index) {
|
||||
|
||||
if (batteryInfo == (byte) 0xff) {
|
||||
return null;
|
||||
}
|
||||
GBDeviceEventBatteryInfo batteryEvent = new GBDeviceEventBatteryInfo();
|
||||
batteryEvent.state = (batteryInfo & 128) != 0 ? BatteryState.BATTERY_CHARGING : BatteryState.BATTERY_NORMAL;
|
||||
batteryEvent.batteryIndex = index;
|
||||
batteryEvent.level = (batteryInfo & 127);
|
||||
LOG.debug("Battery {}: {}", index, batteryEvent.level);
|
||||
return batteryEvent;
|
||||
}
|
||||
|
||||
private GBDeviceEvent[] decodeDeviceInfo(byte[] deviceInfoPayload) {
|
||||
|
||||
List<GBDeviceEvent> events = new ArrayList<>();
|
||||
|
||||
GBDeviceEventVersionInfo info = new GBDeviceEventVersionInfo();
|
||||
byte[] fw = new byte[4];
|
||||
byte[] vidPid = new byte[4];
|
||||
byte[] batteryData = new byte[3];
|
||||
int i = 0;
|
||||
while (i < deviceInfoPayload.length) {
|
||||
byte len = deviceInfoPayload[i];
|
||||
byte index = deviceInfoPayload[i + 1];
|
||||
switch (index) {
|
||||
case 0x01:
|
||||
System.arraycopy(deviceInfoPayload, i + 2, fw, 0, 4);
|
||||
break;
|
||||
case 0x03:
|
||||
System.arraycopy(deviceInfoPayload, i + 2, vidPid, 0, 4);
|
||||
break;
|
||||
case 0x07:
|
||||
System.arraycopy(deviceInfoPayload, i + 2, batteryData, 0, 3);
|
||||
break;
|
||||
}
|
||||
i += len + 1;
|
||||
}
|
||||
|
||||
String fwVersion1 = ((fw[0] >> 4) & 0xF) + "." + (fw[0] & 0xF) + "." + ((fw[1] >> 4) & 0xF) + "." + (fw[1] & 0xF);
|
||||
String fwVersion2 = ((fw[2] >> 4) & 0xF) + "." + (fw[2] & 0xF) + "." + ((fw[3] >> 4) & 0xF) + "." + (fw[3] & 0xF);
|
||||
String hwVersion = String.format("VID: 0x%02X%02X, PID: 0x%02X%02X", vidPid[0], vidPid[1], vidPid[2], vidPid[3]);
|
||||
|
||||
info.fwVersion = fwVersion1;
|
||||
info.fwVersion2 = fwVersion2;
|
||||
info.hwVersion = hwVersion;
|
||||
|
||||
events.add(parseBatteryInfo(batteryData[0], 1));
|
||||
events.add(parseBatteryInfo(batteryData[1], 2));
|
||||
events.add(parseBatteryInfo(batteryData[2], 0));
|
||||
events.add(info);
|
||||
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
}
|
||||
|
||||
private void decodeDeviceRunInfo(byte[] deviceRunInfoPayload) {
|
||||
int i = 0;
|
||||
while (i < deviceRunInfoPayload.length) {
|
||||
byte len = deviceRunInfoPayload[i];
|
||||
byte index = deviceRunInfoPayload[i + 1];
|
||||
SharedPreferences preferences = getDevicePrefs().getPreferences();
|
||||
Editor editor = preferences.edit();
|
||||
switch (index) {
|
||||
case 0x09:
|
||||
byte mode = deviceRunInfoPayload[i + 2];
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL, Integer.toString(mode));
|
||||
break;
|
||||
case 0x0A:
|
||||
editor.putBoolean(PREF_REDMI_BUDS_5_PRO_WEARING_DETECTION, deviceRunInfoPayload[i + 2] == 0x00);
|
||||
}
|
||||
editor.apply();
|
||||
i += len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private GBDeviceEvent[] decodeDeviceUpdate(Message updateMessage) {
|
||||
byte[] updatePayload = updateMessage.getPayload();
|
||||
List<GBDeviceEvent> events = new ArrayList<>();
|
||||
|
||||
int i = 0;
|
||||
while (i < updatePayload.length) {
|
||||
byte len = updatePayload[i];
|
||||
byte index = updatePayload[i + 1];
|
||||
switch (index) {
|
||||
case 0x00:
|
||||
events.add(parseBatteryInfo(updatePayload[i + 2], 1));
|
||||
events.add(parseBatteryInfo(updatePayload[i + 3], 2));
|
||||
events.add(parseBatteryInfo(updatePayload[i + 4], 0));
|
||||
break;
|
||||
case 0x04:
|
||||
SharedPreferences preferences = getDevicePrefs().getPreferences();
|
||||
Editor editor = preferences.edit();
|
||||
|
||||
byte mode = updatePayload[i + 2];
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL, Integer.toString(mode));
|
||||
editor.apply();
|
||||
break;
|
||||
default:
|
||||
LOG.debug("Unimplemented device update: {}", hexdump(updatePayload));
|
||||
}
|
||||
i += len + 1;
|
||||
}
|
||||
events.add(new GBDeviceEventSendBytes(new Message(MessageType.RESPONSE, Opcode.REPORT_STATUS, updateMessage.getSequenceNumber(), new byte[]{}).encode()));
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
}
|
||||
|
||||
private GBDeviceEvent[] decodeNotifyConfig(Message notifyMessage) {
|
||||
|
||||
byte[] notifyPayload = notifyMessage.getPayload();
|
||||
List<GBDeviceEvent> events = new ArrayList<>();
|
||||
|
||||
int i = 0;
|
||||
while (i < notifyPayload.length) {
|
||||
byte len = notifyPayload[i];
|
||||
byte index = notifyPayload[i + 2];
|
||||
switch (index) {
|
||||
case 0x0C:
|
||||
LOG.debug("Received earbuds position info");
|
||||
/*
|
||||
e.g. 0C 03
|
||||
0011
|
||||
wearing left, wearing right, left in case, right in case
|
||||
*/
|
||||
break;
|
||||
case 0x0B:
|
||||
SharedPreferences preferences = getDevicePrefs().getPreferences();
|
||||
Editor editor = preferences.edit();
|
||||
|
||||
byte soundCtrlMode = notifyPayload[i + 3];
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_AMBIENT_SOUND_CONTROL, Integer.toString(soundCtrlMode));
|
||||
|
||||
byte mode = notifyPayload[i + 4];
|
||||
if (notifyPayload[i + 3] == 0x01) {
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_NOISE_CANCELLING_STRENGTH, Integer.toString(mode));
|
||||
} else {
|
||||
editor.putString(PREF_REDMI_BUDS_5_PRO_TRANSPARENCY_STRENGTH, Integer.toString(mode));
|
||||
}
|
||||
|
||||
editor.apply();
|
||||
break;
|
||||
}
|
||||
|
||||
i += len + 1;
|
||||
}
|
||||
events.add(new GBDeviceEventSendBytes(new Message(MessageType.RESPONSE, Opcode.NOTIFY_CONFIG, notifyMessage.getSequenceNumber(), new byte[]{}).encode()));
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
}
|
||||
|
||||
private GBDeviceEvent[] handleAuthentication(Message authMessage) {
|
||||
List<GBDeviceEvent> events = new ArrayList<>();
|
||||
switch (authMessage.getOpcode()) {
|
||||
case AUTH_CHALLENGE:
|
||||
if (authMessage.getType() == MessageType.RESPONSE) {
|
||||
LOG.debug("[AUTH] Received Challenge Response");
|
||||
/*
|
||||
Should check if equal, but does not really matter
|
||||
*/
|
||||
LOG.debug("[AUTH] Sending authentication confirmation");
|
||||
events.add(new GBDeviceEventSendBytes(new Message(MessageType.PHONE_REQUEST, Opcode.AUTH_CONFIRM, sequenceNumber++, new byte[]{0x01, 0x00}).encode()));
|
||||
} else {
|
||||
byte[] responsePayload = authMessage.getPayload();
|
||||
byte[] challenge = new byte[16];
|
||||
System.arraycopy(responsePayload, 1, challenge, 0, 16);
|
||||
|
||||
LOG.info("[AUTH] Received Challenge: {}", hexdump(challenge));
|
||||
Authentication auth = new Authentication();
|
||||
byte[] challengeResponse = auth.computeChallengeResponse(challenge);
|
||||
LOG.info("[AUTH] Sending Challenge Response: {}", hexdump(challengeResponse));
|
||||
|
||||
byte[] payload = new byte[17];
|
||||
payload[0] = 0x01;
|
||||
System.arraycopy(challengeResponse, 0, payload, 1, 16);
|
||||
Message res = new Message(MessageType.RESPONSE, Opcode.AUTH_CHALLENGE, authMessage.getSequenceNumber(), payload);
|
||||
events.add(new GBDeviceEventSendBytes(res.encode()));
|
||||
}
|
||||
break;
|
||||
case AUTH_CONFIRM:
|
||||
if (authMessage.getType() == MessageType.RESPONSE) {
|
||||
LOG.debug("[AUTH] Confirmed first authentication step");
|
||||
} else {
|
||||
LOG.debug("[AUTH] Received authentication confirmation");
|
||||
Message res = new Message(MessageType.RESPONSE, Opcode.AUTH_CONFIRM, authMessage.getSequenceNumber(), new byte[]{0x01});
|
||||
LOG.debug("[AUTH] Sending final authentication confirmation");
|
||||
events.add(new GBDeviceEventSendBytes(res.encode()));
|
||||
|
||||
LOG.debug("[INIT] Sending device info request");
|
||||
Message info = new Message(MessageType.PHONE_REQUEST, Opcode.GET_DEVICE_INFO, sequenceNumber++, new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff});
|
||||
events.add(new GBDeviceEventSendBytes(info.encode()));
|
||||
|
||||
LOG.debug("[INIT] Sending device run info request");
|
||||
Message runInfo = new Message(MessageType.PHONE_REQUEST, Opcode.GET_DEVICE_RUN_INFO, sequenceNumber++, new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff});
|
||||
events.add(new GBDeviceEventSendBytes(runInfo.encode()));
|
||||
|
||||
LOG.debug("[INIT] Sending configuration request");
|
||||
events.add(new GBDeviceEventSendBytes(encodeGetConfig()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
|
||||
|
||||
LOG.debug("Incoming message: {}", hexdump(responseData));
|
||||
|
||||
List<GBDeviceEvent> events = new ArrayList<>();
|
||||
|
||||
List<Message> incomingMessages = Message.splitPiggybackedMessages(responseData);
|
||||
|
||||
for (Message message : incomingMessages) {
|
||||
|
||||
LOG.debug("Parsed message: {}", message);
|
||||
|
||||
switch (message.getOpcode()) {
|
||||
case AUTH_CHALLENGE:
|
||||
case AUTH_CONFIRM:
|
||||
events.addAll(Arrays.asList(handleAuthentication(message)));
|
||||
break;
|
||||
case GET_DEVICE_INFO:
|
||||
LOG.debug("[INIT] Received device info");
|
||||
if (getDevice().getState() != State.INITIALIZED) {
|
||||
events.addAll(Arrays.asList(decodeDeviceInfo(message.getPayload())));
|
||||
LOG.debug("[INIT] Device Initialized");
|
||||
events.add(new GBDeviceEventUpdateDeviceState(State.INITIALIZED));
|
||||
}
|
||||
break;
|
||||
case GET_DEVICE_RUN_INFO:
|
||||
LOG.debug("[INIT] Received device run info");
|
||||
decodeDeviceRunInfo(message.getPayload());
|
||||
break;
|
||||
case REPORT_STATUS:
|
||||
events.addAll(Arrays.asList(decodeDeviceUpdate(message)));
|
||||
break;
|
||||
case GET_CONFIG:
|
||||
decodeGetConfig(message.getPayload());
|
||||
break;
|
||||
case NOTIFY_CONFIG:
|
||||
events.addAll(Arrays.asList(decodeNotifyConfig(message)));
|
||||
break;
|
||||
default:
|
||||
LOG.debug("Unhandled message: {}", message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.redmibuds5pro.protocol;
|
||||
|
||||
class AuthData {
|
||||
|
||||
final static byte[] SEQ = { 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x11, 0x22, 0x33, 0x33 };
|
||||
|
||||
final static int[][] COEFFICIENTS = {
|
||||
{2,1,1,1,4,2,1,1,2,2,4,2,4,4,16,8},
|
||||
{2,1,1,1,4,2,1,1,1,1,2,1,2,2,8,4},
|
||||
{1,1,4,2,2,2,4,2,16,8,4,4,2,1,1,1},
|
||||
{1,1,4,2,1,1,2,1,8,4,2,2,2,1,1,1},
|
||||
{16,8,2,2,4,2,4,4,1,1,4,2,1,1,2,1},
|
||||
{8,4,1,1,2,1,2,2,1,1,4,2,1,1,2,1},
|
||||
{2,2,4,2,4,4,16,8,2,1,1,1,4,2,1,1},
|
||||
{1,1,2,1,2,2,8,4,2,1,1,1,4,2,1,1},
|
||||
{4,2,4,4,16,8,2,2,1,1,2,1,1,1,4,2},
|
||||
{2,1,2,2,8,4,1,1,1,1,2,1,1,1,4,2},
|
||||
{4,4,16,8,1,1,2,1,4,2,1,1,4,2,2,2},
|
||||
{2,2,8,4,1,1,2,1,4,2,1,1,2,1,1,1},
|
||||
{1,1,2,1,1,1,4,2,4,4,16,8,2,2,4,2},
|
||||
{1,1,2,1,1,1,4,2,2,2,8,4,1,1,2,1},
|
||||
{4,2,1,1,2,1,1,1,4,2,2,2,16,8,4,4},
|
||||
{4,2,1,1,2,1,1,1,2,1,1,1,8,4,2,2}
|
||||
};
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/* Copyright (C) 2024 Jonathan Gobbo
|
||||
|
||||
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.redmibuds5pro.protocol;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
Authentication based on the custom Bluetooth version of the SAFER+ encryption algorithm with:
|
||||
- 128 bit key size
|
||||
- 8 rounds
|
||||
*/
|
||||
public class Authentication {
|
||||
|
||||
private final static int PATTERN = 0x9999;
|
||||
private final static int BLOCK_SIZE = 16;
|
||||
|
||||
byte[][] biasMatrix;
|
||||
byte[] expTab;
|
||||
byte[] logTab;
|
||||
|
||||
public Authentication() {
|
||||
generateBiasMatrix();
|
||||
generateExpTab();
|
||||
generateLogTab();
|
||||
}
|
||||
|
||||
private void generateBiasMatrix() {
|
||||
biasMatrix = new byte[16][];
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
byte[] biasVec = new byte[16];
|
||||
for (int j = 0; j < 16; j++) {
|
||||
int exponent = (17 * (i + 2) + (j + 1));
|
||||
BigInteger base = BigInteger.valueOf(45);
|
||||
BigInteger modExp = base.pow(base.pow(exponent).mod(BigInteger.valueOf(257)).intValue())
|
||||
.mod(BigInteger.valueOf(257));
|
||||
byte val = (byte) (modExp.intValue() == 256 ? 0 : modExp.intValue());
|
||||
biasVec[j] = val;
|
||||
}
|
||||
biasMatrix[i] = biasVec;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateExpTab() {
|
||||
expTab = new byte[256];
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
BigInteger base = BigInteger.valueOf(45);
|
||||
BigInteger exp = base.pow(i).mod(BigInteger.valueOf(257));
|
||||
expTab[i] = (byte) (i == 128 ? 0 : exp.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void generateLogTab() {
|
||||
logTab = new byte[256];
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (i == 0) {
|
||||
logTab[i] = (byte) 128;
|
||||
} else {
|
||||
BigInteger base = BigInteger.valueOf(45);
|
||||
BigInteger modExp = base.pow(i).mod(BigInteger.valueOf(257));
|
||||
if (modExp.intValue() != 256) {
|
||||
logTab[modExp.intValue()] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[][] keySchedule(byte[] keyInit) {
|
||||
|
||||
keyInit[15] ^= 6;
|
||||
|
||||
byte[][] keys = new byte[17][];
|
||||
keys[0] = keyInit;
|
||||
|
||||
List<Byte> register = new ArrayList<>();
|
||||
for (byte b : keyInit) {
|
||||
register.add(b);
|
||||
}
|
||||
byte xor = register.stream().reduce((byte) 0x0, (cSum, e) -> (byte) (cSum ^ e));
|
||||
register.add(xor);
|
||||
|
||||
for (int keyIdx = 1; keyIdx < keys.length; keyIdx++) {
|
||||
byte[] keyI = new byte[16];
|
||||
for (int i = 0; i < 17; i++) {
|
||||
byte rot = (byte) (((register.get(i) & 0xff) >>> 5) | ((register.get(i) & 0xff) << (8 - 5)));
|
||||
register.set(i, rot);
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
keyI[i] = (byte) (register.get((keyIdx + i) % 17) + biasMatrix[keyIdx - 1][i]);
|
||||
}
|
||||
keys[keyIdx] = keyI;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private byte[] encrypt(byte[] plaintext, byte[][] keys) {
|
||||
|
||||
byte[] ciphertext = plaintext.clone();
|
||||
for (int round = 0; round < 8; round++) {
|
||||
if (round == 2) {
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
if ((1 << i & PATTERN) != 0) {
|
||||
ciphertext[i] ^= plaintext[i];
|
||||
} else {
|
||||
ciphertext[i] += plaintext[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
if ((1 << i & PATTERN) != 0) {
|
||||
ciphertext[i] ^= keys[round * 2][i];
|
||||
} else {
|
||||
ciphertext[i] += keys[round * 2][i];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
if ((1 << i & PATTERN) != 0) {
|
||||
ciphertext[i] = expTab[ciphertext[i] & 0xff];
|
||||
} else {
|
||||
ciphertext[i] = logTab[ciphertext[i] & 0xff];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
if ((1 << i & PATTERN) != 0) {
|
||||
ciphertext[i] = (byte) (keys[round * 2 + 1][i] + ciphertext[i]);
|
||||
} else {
|
||||
ciphertext[i] = (byte) (keys[round * 2 + 1][i] ^ ciphertext[i]);
|
||||
}
|
||||
}
|
||||
byte[] ciphertextCopy = ciphertext.clone();
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
byte cSum = 0;
|
||||
for (int j = 0; j < BLOCK_SIZE; j++) {
|
||||
cSum += (byte) (AuthData.COEFFICIENTS[i][j] * ciphertextCopy[j]);
|
||||
}
|
||||
ciphertext[i] = cSum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
if ((1 << i & PATTERN) != 0) {
|
||||
ciphertext[i] = (byte) (keys[16][i] ^ ciphertext[i]);
|
||||
} else {
|
||||
ciphertext[i] = (byte) (keys[16][i] + ciphertext[i]);
|
||||
}
|
||||
}
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
public static byte[] getRandomChallenge() {
|
||||
byte[] res = new byte[BLOCK_SIZE];
|
||||
SecureRandom rnd = new SecureRandom();
|
||||
rnd.nextBytes(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
public byte[] computeChallengeResponse(byte[] challenge) {
|
||||
byte[][] keys = keySchedule(challenge);
|
||||
return encrypt(AuthData.SEQ, keys);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/* Copyright (C) 2024 Jonathan Gobbo
|
||||
|
||||
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.redmibuds5pro.protocol;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Message {
|
||||
|
||||
private static final byte[] MESSAGE_HEADER = {(byte) 0xfe, (byte) 0xdc, (byte) 0xba};
|
||||
private static final byte MESSAGE_TRAILER = (byte) 0xef;
|
||||
|
||||
private static final int MESSAGE_OFFSET = MESSAGE_HEADER.length;
|
||||
|
||||
private final MessageType type;
|
||||
private final Opcode opcode;
|
||||
private final byte sequenceNumber;
|
||||
private final byte[] payload;
|
||||
|
||||
public Message(final MessageType type, final Opcode opcode, final byte sequenceNumber, final byte[] payload) {
|
||||
this.type = type;
|
||||
this.opcode = opcode;
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public static Message fromBytes(byte[] message) {
|
||||
MessageType type = MessageType.fromCode(message[MESSAGE_OFFSET]);
|
||||
Opcode opcode = Opcode.fromCode(message[MESSAGE_OFFSET + 1]);
|
||||
|
||||
int payloadOffset = MESSAGE_OFFSET + ((!type.isRequest()) ? 6 : 5);
|
||||
byte sequenceNumber = message[payloadOffset - 1];
|
||||
|
||||
int actualPayloadLength = message.length - payloadOffset - 1;
|
||||
byte[] payload = new byte[actualPayloadLength];
|
||||
System.arraycopy(message, payloadOffset, payload, 0, actualPayloadLength);
|
||||
return new Message(type, opcode, sequenceNumber, payload);
|
||||
}
|
||||
|
||||
public static List<Message> splitPiggybackedMessages(byte[] input) {
|
||||
List<Message> messages = new ArrayList<>();
|
||||
|
||||
List<Integer> startHeader = new ArrayList<>();
|
||||
for (int i = 0; i < input.length - 3; i++) {
|
||||
if (input[i] == MESSAGE_HEADER[0] && input[i + 1] == MESSAGE_HEADER[1] && input[i + 2] == MESSAGE_HEADER[2]) {
|
||||
startHeader.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < startHeader.size(); i++) {
|
||||
if (i == startHeader.size() - 1) {
|
||||
messages.add(fromBytes(Arrays.copyOfRange(input, startHeader.get(i), input.length)));
|
||||
} else {
|
||||
messages.add(fromBytes(Arrays.copyOfRange(input, startHeader.get(i), startHeader.get(i + 1))));
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
public byte[] encode() {
|
||||
|
||||
int size = (!type.isRequest()) ? 2 : 1;
|
||||
final ByteBuffer buf = ByteBuffer.allocate(payload.length + 8 + size);
|
||||
int payloadLength = payload.length + size;
|
||||
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
buf.put(MESSAGE_HEADER);
|
||||
buf.put(type.getCode());
|
||||
buf.put(opcode.getOpcode());
|
||||
buf.putShort((short) payloadLength);
|
||||
if (!type.isRequest()) {
|
||||
buf.put((byte) 0x00);
|
||||
}
|
||||
buf.put(sequenceNumber);
|
||||
buf.put(payload);
|
||||
buf.put(MESSAGE_TRAILER);
|
||||
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Message{" + "type=" + type + ", opcode=" + opcode + ", sequenceNumber=" + sequenceNumber + ", payload=" + hexdump(payload) + '}';
|
||||
}
|
||||
|
||||
public MessageType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Opcode getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
public byte getSequenceNumber() {
|
||||
return sequenceNumber;
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
return payload;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.redmibuds5pro.protocol;
|
||||
|
||||
public enum MessageType {
|
||||
PHONE_REQUEST(0xC4),
|
||||
RESPONSE(0x04),
|
||||
EARBUDS_REQUEST(0xC0),
|
||||
EARBUDS_NOTIFY(0xC7),
|
||||
UNKNOWN(0xFF);
|
||||
|
||||
private final byte code;
|
||||
private final boolean isRequest;
|
||||
|
||||
MessageType(final int code) {
|
||||
this.code = (byte) code;
|
||||
this.isRequest = (this.code & 0x40) != 0;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public boolean isRequest() {
|
||||
return isRequest;
|
||||
}
|
||||
|
||||
public static MessageType fromCode(final byte code) {
|
||||
for (final MessageType messageType : values()) {
|
||||
if (messageType.code == code) {
|
||||
return messageType;
|
||||
}
|
||||
}
|
||||
|
||||
return MessageType.UNKNOWN;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.redmibuds5pro.protocol;
|
||||
|
||||
public enum Opcode {
|
||||
GET_DEVICE_INFO(0x02),
|
||||
ANC(0x08),
|
||||
GET_DEVICE_RUN_INFO(0x09),
|
||||
REPORT_STATUS(0x0E),
|
||||
AUTH_CHALLENGE(0x50),
|
||||
AUTH_CONFIRM(0x51),
|
||||
SET_CONFIG(0xF2),
|
||||
GET_CONFIG(0xF3),
|
||||
NOTIFY_CONFIG(0xF4),
|
||||
UNKNOWN(0xFF);
|
||||
|
||||
private final byte opcode;
|
||||
|
||||
Opcode(final int opcode) {
|
||||
this.opcode = (byte) opcode;
|
||||
}
|
||||
|
||||
public byte getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
public static Opcode fromCode(final byte code) {
|
||||
for (final Opcode opcode : values()) {
|
||||
if (opcode.opcode == code) {
|
||||
return opcode;
|
||||
}
|
||||
}
|
||||
return Opcode.UNKNOWN;
|
||||
}
|
||||
}
|
@ -3377,6 +3377,160 @@
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_ambient_sound_control_names">
|
||||
<item>@string/sony_ambient_sound_off</item>
|
||||
<item>@string/sony_ambient_sound_noise_cancelling</item>
|
||||
<item>@string/prefs_active_noise_cancelling_transparency</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_ambient_sound_control_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_noise_cancelling_strength_names">
|
||||
<item>@string/redmi_buds_5_pro_anc_balanced</item>
|
||||
<item>@string/redmi_buds_5_pro_anc_light</item>
|
||||
<item>@string/redmi_buds_5_pro_anc_deep</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_noise_cancelling_strength_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_transparency_strength_names">
|
||||
<item>@string/redmi_buds_5_pro_transparency_regular</item>
|
||||
<item>@string/redmi_buds_5_pro_transparency_voice</item>
|
||||
<item>@string/redmi_buds_5_pro_transparency_ambient</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_transparency_strength_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_single_button_function_names">
|
||||
<item>@string/none</item>
|
||||
<item>@string/pref_media_playpause</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>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_single_button_function_values">
|
||||
<item>8</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_button_function_names">
|
||||
<item>@string/pref_media_playpause</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>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_button_function_values">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_long_button_mode_names">
|
||||
<item>@string/pref_title_touch_voice_assistant</item>
|
||||
<item>@string/pref_header_sony_ambient_sound_control</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_long_button_mode_values">
|
||||
<item>0</item>
|
||||
<item>6</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_long_button_settings_names">
|
||||
<item>@string/redmi_buds_5_pro_combo_anc_off</item>
|
||||
<item>@string/redmi_buds_5_pro_combo_transparency_off</item>
|
||||
<item>@string/redmi_buds_5_pro_combo_anc_transparency</item>
|
||||
<item>@string/redmi_buds_5_pro_combo_all</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_long_button_settings_values">
|
||||
<item>3</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
</string-array>
|
||||
|
||||
<!-- <string-array name="redmi_buds_5_pro_surround_sound_mode_names">-->
|
||||
<!-- <item>Standard</item>-->
|
||||
<!-- <item>Music</item>-->
|
||||
<!-- <item>Video</item>-->
|
||||
<!-- </string-array>-->
|
||||
|
||||
<!-- <string-array name="redmi_buds_5_pro_surround_sound_mode_values">-->
|
||||
<!-- <item>1</item>-->
|
||||
<!-- <item>2</item>-->
|
||||
<!-- <item>3</item>-->
|
||||
<!-- </string-array>-->
|
||||
|
||||
<string-array name="redmi_buds_5_pro_equalizer_presets_names">
|
||||
<item>@string/redmi_buds_5_pro_equalizer_preset_standard</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_preset_treble</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_preset_bass</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_preset_voice</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_preset_custom</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_equalizer_presets_values">
|
||||
<item>0</item>
|
||||
<item>6</item>
|
||||
<item>5</item>
|
||||
<item>1</item>
|
||||
<item>10</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_equalizer_offsets_names">
|
||||
<item>@string/redmi_buds_5_pro_equalizer_neg6</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_neg5</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_neg4</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_neg3</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_neg2</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_neg1</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_zero</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_1</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_2</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_3</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_4</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_5</item>
|
||||
<item>@string/redmi_buds_5_pro_equalizer_6</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="redmi_buds_5_pro_equalizer_offsets_values">
|
||||
<item>134</item>
|
||||
<item>133</item>
|
||||
<item>132</item>
|
||||
<item>131</item>
|
||||
<item>130</item>
|
||||
<item>129</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="sony_ambient_sound_control_wind_noise_reduction_names">
|
||||
<item>@string/sony_ambient_sound_off</item>
|
||||
<item>@string/sony_ambient_sound_noise_cancelling</item>
|
||||
|
@ -1877,6 +1877,7 @@
|
||||
<string name="devicetype_xiaomi_watch_lite">Xiaomi Watch Lite</string>
|
||||
<string name="devicetype_redmiwatch3active">Redmi Watch 3 Active</string>
|
||||
<string name="devicetype_redmiwatch3">Redmi Watch 3</string>
|
||||
<string name="devicetype_redmi_buds_5_pro">Redmi Buds 5 Pro</string>
|
||||
<string name="devicetype_redmi_smart_band_2">Redmi Smart Band 2</string>
|
||||
<string name="devicetype_redmi_watch_2">Redmi Watch 2</string>
|
||||
<string name="devicetype_redmi_watch_2_lite">Redmi Watch 2 Lite</string>
|
||||
@ -2611,6 +2612,51 @@
|
||||
<string name="weight_kg">%1$.2f kg</string>
|
||||
<string name="weight_lbs">%1$.2f lbs</string>
|
||||
<string name="target">Target</string>
|
||||
<string name="redmi_buds_5_pro_anc_balanced">Balanced</string>
|
||||
<string name="redmi_buds_5_pro_anc_light">Light</string>
|
||||
<string name="redmi_buds_5_pro_anc_deep">Deep</string>
|
||||
<string name="redmi_buds_5_pro_transparency_strength">Transparency Strength</string>
|
||||
<string name="redmi_buds_5_pro_transparency_regular">Regular</string>
|
||||
<string name="redmi_buds_5_pro_transparency_voice">Enhance Voices</string>
|
||||
<string name="redmi_buds_5_pro_transparency_ambient">Enhance Ambient Sounds</string>
|
||||
<string name="redmi_buds_5_pro_combo_anc_off">ANC / Off</string>
|
||||
<string name="redmi_buds_5_pro_combo_transparency_off">Transparency / Off</string>
|
||||
<string name="redmi_buds_5_pro_combo_anc_transparency">ANC / Transparency</string>
|
||||
<string name="redmi_buds_5_pro_combo_all">ANC / Transparency / Off</string>
|
||||
<string name="redmi_buds_5_pro_double_connection">Double Connection</string>
|
||||
<string name="redmi_buds_5_pro_double_connection_description">Allow the earbuds to connect to two devices at the same time</string>
|
||||
<!-- <string name="redmi_buds_5_pro_spatial_audio">Spatial Audio</string>-->
|
||||
<!-- <string name="redmi_buds_5_pro_spatial_audio_mode">Spatial Audio Mode</string>-->
|
||||
<string name="redmi_buds_5_pro_adaptive_sound">Adaptive Sound</string>
|
||||
<string name="redmi_buds_5_pro_adaptive_sound_description">Adjusts the sound according to the ear shape and the environment</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_preset_standard">Standard</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_preset_treble">Enhance Treble</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_preset_bass">Enhance Bass</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_preset_voice">Enhance Voice</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_preset_custom">Custom</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_62">62 Hz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_125">125 Hz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_250">250 Hz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_500">500 Hz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_1k">1 kHz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_2k">2 kHz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_4k">4 kHz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_8k">8 kHz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_12k">12 kHz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_band_16k">16 kHz</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_neg6">-6 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_neg5">-5 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_neg4">-4 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_neg3">-3 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_neg2">-2 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_neg1">-1 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_zero">0 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_1">1 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_2">2 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_3">3 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_4">4 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_5">5 dB</string>
|
||||
<string name="redmi_buds_5_pro_equalizer_6">6 dB</string>
|
||||
<string name="sony_ambient_sound">Mode</string>
|
||||
<string name="sony_ambient_sound_off">Off</string>
|
||||
<string name="sony_ambient_sound_noise_cancelling">Noise Cancelling</string>
|
||||
@ -2674,6 +2720,8 @@
|
||||
<string name="pref_adaptive_volume_control_title">Adaptive volume control</string>
|
||||
<string name="pref_adaptive_noise_cancelling_title">Adaptive ANC</string>
|
||||
<string name="pref_adaptive_noise_cancelling_summary">Set the strength of the ANC automatically depending on the ambient sound level</string>
|
||||
<!-- <string name="pref_personalized_noise_cancelling_title">Personalized ANC</string>-->
|
||||
<!-- <string name="pref_personalized_noise_cancelling_summary">Set whether to use personalized active noise cancelling</string>-->
|
||||
<string name="sony_speak_to_chat">Speak-to-chat</string>
|
||||
<string name="sony_speak_to_chat_summary">Turn off noise cancelling automatically when you start talking.</string>
|
||||
<string name="sony_speak_to_chat_sensitivity">Voice Detection Sensitivity</string>
|
||||
|
123
app/src/main/res/xml/devicesettings_redmibuds5pro_gestures.xml
Normal file
123
app/src/main/res/xml/devicesettings_redmibuds5pro_gestures.xml
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_header_redmibuds5pro_system"
|
||||
android:title="@string/pref_header_system">
|
||||
|
||||
<PreferenceScreen
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:key="pref_screen_redmibuds5pro_touch_options"
|
||||
android:persistent="false"
|
||||
android:title="@string/prefs_galaxy_touch_options">
|
||||
|
||||
<PreferenceCategory android:title="@string/single_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_single_button_function_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_single_button_function_values"
|
||||
android:icon="@drawable/ic_filter_1"
|
||||
android:defaultValue="8"
|
||||
android:key="pref_redmi_buds_5_pro_control_single_tap_left"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_single_button_function_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_single_button_function_values"
|
||||
android:defaultValue="8"
|
||||
android:key="pref_redmi_buds_5_pro_control_single_tap_right"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/double_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_button_function_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_button_function_values"
|
||||
android:icon="@drawable/ic_filter_2"
|
||||
android:defaultValue="1"
|
||||
android:key="pref_redmi_buds_5_pro_control_double_tap_left"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_button_function_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_button_function_values"
|
||||
android:defaultValue="1"
|
||||
android:key="pref_redmi_buds_5_pro_control_double_tap_right"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/triple_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_button_function_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_button_function_values"
|
||||
android:icon="@drawable/ic_filter_3"
|
||||
android:defaultValue="2"
|
||||
android:key="pref_redmi_buds_5_pro_control_triple_tap_left"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_button_function_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_button_function_values"
|
||||
android:defaultValue="3"
|
||||
android:key="pref_redmi_buds_5_pro_control_triple_tap_right"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/long_press">
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_long_button_mode_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_long_button_mode_values"
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:defaultValue="6"
|
||||
android:key="pref_redmi_buds_5_pro_control_long_tap_mode_left"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_button_mode_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_long_button_settings_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_long_button_settings_values"
|
||||
android:defaultValue="7"
|
||||
android:key="pref_redmi_buds_5_pro_control_long_tap_settings_left"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_ambient_sound_control_button_modes" />
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_long_button_mode_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_long_button_mode_values"
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:defaultValue="6"
|
||||
android:key="pref_redmi_buds_5_pro_control_long_tap_mode_right"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_button_mode_right" />
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_long_button_settings_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_long_button_settings_values"
|
||||
android:defaultValue="7"
|
||||
android:key="pref_redmi_buds_5_pro_control_long_tap_settings_right"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_ambient_sound_control_button_modes" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_redmi_buds_5_pro_wearing_detection"
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:icon="@drawable/ic_pause"
|
||||
android:summary="@string/nothing_prefs_inear_summary"
|
||||
android:title="@string/nothing_prefs_inear_title"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:icon="@drawable/ic_phone"
|
||||
android:key="pref_redmi_buds_5_pro_auto_reply_phonecall"
|
||||
android:summary="@string/pref_auto_reply_calls_summary"
|
||||
android:title="@string/pref_auto_reply_calls_title"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_redmi_buds_5_pro_double_connection"
|
||||
android:summary="@string/redmi_buds_5_pro_double_connection_description"
|
||||
android:title="@string/redmi_buds_5_pro_double_connection"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
</androidx.preference.PreferenceScreen>
|
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_header_redmibuds5pro_ambient_sound_control"
|
||||
android:title="@string/pref_header_sony_ambient_sound_control">
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/redmi_buds_5_pro_ambient_sound_control_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_ambient_sound_control_values"
|
||||
android:icon="@drawable/ic_hearing"
|
||||
android:key="pref_redmi_buds_5_pro_ambient_sound_control"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_ambient_sound" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:dependency="pref_redmi_buds_5_pro_adaptive_noise_cancelling"
|
||||
android:entries="@array/redmi_buds_5_pro_noise_cancelling_strength_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_noise_cancelling_strength_values"
|
||||
android:key="pref_redmi_buds_5_pro_noise_cancelling_strength"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_active_noise_cancelling_level" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/redmi_buds_5_pro_transparency_strength_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_transparency_strength_values"
|
||||
android:key="pref_redmi_buds_5_pro_transparency_strength"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_transparency_strength" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:disableDependentsState="true"
|
||||
android:key="pref_redmi_buds_5_pro_adaptive_noise_cancelling"
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:summary="@string/pref_adaptive_noise_cancelling_summary"
|
||||
android:title="@string/pref_adaptive_noise_cancelling_title" />
|
||||
|
||||
<!-- <SwitchPreferenceCompat-->
|
||||
<!-- android:defaultValue="true"-->
|
||||
<!-- android:key="pref_redmi_buds_5_pro_personalized_noise_cancelling"-->
|
||||
<!-- android:layout="@layout/preference_checkbox"-->
|
||||
<!-- android:summary="@string/pref_personalized_noise_cancelling_summary"-->
|
||||
<!-- android:title="@string/pref_personalized_noise_cancelling_title" />-->
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
144
app/src/main/res/xml/devicesettings_redmibuds5pro_sound.xml
Normal file
144
app/src/main/res/xml/devicesettings_redmibuds5pro_sound.xml
Normal file
@ -0,0 +1,144 @@
|
||||
<?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="pref_key_header_redmibuds5pro_sound"
|
||||
android:title="@string/pref_header_sound">
|
||||
|
||||
<PreferenceScreen
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_sony_equalizer"
|
||||
android:persistent="false"
|
||||
android:title="@string/pref_header_equalizer">
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_presets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_presets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_preset"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_equalizer_preset" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_header_redmibuds5pro_custom_equalizer"
|
||||
android:title="@string/soundcore_equalizer_custom_title">
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_62"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_62" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_125"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_125" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_250"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_250" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_500"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_500" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_1k"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_1k" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_2k"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_2k" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_4k"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_4k" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_8k"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_8k" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_12k"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_12k" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/redmi_buds_5_pro_equalizer_offsets_names"
|
||||
android:entryValues="@array/redmi_buds_5_pro_equalizer_offsets_values"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:defaultValue="0"
|
||||
android:key="pref_redmi_buds_5_pro_equalizer_band_16k"
|
||||
android:summary="%s"
|
||||
android:title="@string/redmi_buds_5_pro_equalizer_band_16k" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
<!-- <SwitchPreferenceCompat-->
|
||||
<!-- android:defaultValue="false"-->
|
||||
<!-- android:key="pref_redmi_buds_5_pro_surround_sound"-->
|
||||
<!-- android:icon="@drawable/ic_surround"-->
|
||||
<!-- android:layout="@layout/preference_checkbox"-->
|
||||
<!-- android:title="@string/redmi_buds_5_pro_spatial_audio" />-->
|
||||
|
||||
<!-- <ListPreference-->
|
||||
<!-- android:entries="@array/redmi_buds_5_pro_surround_sound_mode_names"-->
|
||||
<!-- android:entryValues="@array/redmi_buds_5_pro_surround_sound_mode_values"-->
|
||||
<!-- android:key="pref_redmi_buds_5_pro_surround_sound_mode"-->
|
||||
<!-- android:summary="%s"-->
|
||||
<!-- android:title="@string/redmi_buds_5_pro_spatial_audio_mode" />-->
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_redmi_buds_5_pro_adaptive_sound"
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:summary="@string/redmi_buds_5_pro_adaptive_sound_description"
|
||||
android:title="@string/redmi_buds_5_pro_adaptive_sound" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user