mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-25 18:15:49 +01:00
Sony LinkBuds S: Initial support
This commit is contained in:
parent
8f3b9cbabe
commit
ff15fdaa9f
@ -253,6 +253,7 @@ public class DeviceSettingsPreferenceConst {
|
||||
|
||||
public static final String PREF_SONY_AUDIO_CODEC = "pref_sony_audio_codec";
|
||||
public static final String PREF_SONY_AMBIENT_SOUND_CONTROL = "pref_sony_ambient_sound_control";
|
||||
public static final String PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE = "pref_sony_ambient_sound_control_button_mode";
|
||||
public static final String PREF_SONY_FOCUS_VOICE = "pref_sony_focus_voice";
|
||||
public static final String PREF_SONY_AMBIENT_SOUND_LEVEL = "pref_sony_ambient_sound_level";
|
||||
public static final String PREF_SONY_NOISE_OPTIMIZER_START = "pref_sony_noise_optimizer_start";
|
||||
@ -274,6 +275,8 @@ public class DeviceSettingsPreferenceConst {
|
||||
public static final String PREF_SONY_PAUSE_WHEN_TAKEN_OFF = "pref_sony_pause_when_taken_off";
|
||||
public static final String PREF_SONY_BUTTON_MODE_LEFT = "pref_sony_button_mode_left";
|
||||
public static final String PREF_SONY_BUTTON_MODE_RIGHT = "pref_sony_button_mode_right";
|
||||
public static final String PREF_SONY_QUICK_ACCESS_DOUBLE_TAP = "pref_sony_quick_access_double_tap";
|
||||
public static final String PREF_SONY_QUICK_ACCESS_TRIPLE_TAP = "pref_sony_quick_access_triple_tap";
|
||||
public static final String PREF_SONY_AUTOMATIC_POWER_OFF = "pref_sony_automatic_power_off";
|
||||
public static final String PREF_SONY_NOTIFICATION_VOICE_GUIDE = "pref_sony_notification_voice_guide";
|
||||
public static final String PREF_SONY_SPEAK_TO_CHAT = "pref_sony_speak_to_chat";
|
||||
|
@ -485,6 +485,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
|
||||
addPreferenceHandlerFor(PREF_GALAXY_BUDS_TOUCH_RIGHT_SWITCH);
|
||||
|
||||
addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_CONTROL);
|
||||
addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE);
|
||||
addPreferenceHandlerFor(PREF_SONY_FOCUS_VOICE);
|
||||
addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_LEVEL);
|
||||
addPreferenceHandlerFor(PREF_SONY_SOUND_POSITION);
|
||||
@ -501,6 +502,8 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
|
||||
addPreferenceHandlerFor(PREF_SONY_PAUSE_WHEN_TAKEN_OFF);
|
||||
addPreferenceHandlerFor(PREF_SONY_BUTTON_MODE_LEFT);
|
||||
addPreferenceHandlerFor(PREF_SONY_BUTTON_MODE_RIGHT);
|
||||
addPreferenceHandlerFor(PREF_SONY_QUICK_ACCESS_DOUBLE_TAP);
|
||||
addPreferenceHandlerFor(PREF_SONY_QUICK_ACCESS_TRIPLE_TAP);
|
||||
addPreferenceHandlerFor(PREF_SONY_AUTOMATIC_POWER_OFF);
|
||||
addPreferenceHandlerFor(PREF_SONY_NOTIFICATION_VOICE_GUIDE);
|
||||
addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT);
|
||||
|
@ -27,6 +27,7 @@ public enum SonyHeadphonesCapabilities {
|
||||
AudioSettingsOnlyOnSbcCodec,
|
||||
AudioUpsampling,
|
||||
ButtonModesLeftRight,
|
||||
AmbientSoundControlButtonMode,
|
||||
VoiceNotifications,
|
||||
AutomaticPowerOffWhenTakenOff,
|
||||
AutomaticPowerOffByTime,
|
||||
@ -35,5 +36,6 @@ public enum SonyHeadphonesCapabilities {
|
||||
EqualizerWithCustomBands,
|
||||
SoundPosition,
|
||||
SurroundMode,
|
||||
QuickAccess,
|
||||
PauseWhenTakenOff
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceC
|
||||
if (supports(SonyHeadphonesCapabilities.BatterySingle)) {
|
||||
if (supports(SonyHeadphonesCapabilities.BatteryDual)) {
|
||||
throw new IllegalStateException("A device can't have both single and dual battery");
|
||||
} else if(supports(SonyHeadphonesCapabilities.BatteryCase)) {
|
||||
} else if (supports(SonyHeadphonesCapabilities.BatteryCase)) {
|
||||
throw new IllegalStateException("Devices with single battery + case are not supported by the protocol");
|
||||
}
|
||||
}
|
||||
@ -194,6 +194,8 @@ public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceC
|
||||
|
||||
addSettingsUnderHeader(settings, R.xml.devicesettings_header_system, new LinkedHashMap<SonyHeadphonesCapabilities, Integer>() {{
|
||||
put(SonyHeadphonesCapabilities.ButtonModesLeftRight, R.xml.devicesettings_sony_headphones_button_modes_left_right);
|
||||
put(SonyHeadphonesCapabilities.AmbientSoundControlButtonMode, R.xml.devicesettings_sony_headphones_ambient_sound_control_button_modes);
|
||||
put(SonyHeadphonesCapabilities.QuickAccess, R.xml.devicesettings_sony_headphones_quick_access);
|
||||
put(SonyHeadphonesCapabilities.TouchSensorSingle, R.xml.devicesettings_sony_headphones_touch_sensor_single);
|
||||
put(SonyHeadphonesCapabilities.PauseWhenTakenOff, R.xml.devicesettings_sony_headphones_pause_when_taken_off);
|
||||
put(SonyHeadphonesCapabilities.AutomaticPowerOffWhenTakenOff, R.xml.devicesettings_automatic_power_off_when_taken_off);
|
||||
@ -217,8 +219,8 @@ public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceC
|
||||
/**
|
||||
* Add the preference screens for capabilities under a header. The header is also only added if at least one capability is supported by the device.
|
||||
*
|
||||
* @param settings the list of settings to update
|
||||
* @param header the header to add, if any capability supported
|
||||
* @param settings the list of settings to update
|
||||
* @param header the header to add, if any capability supported
|
||||
* @param capabilities the map of capability to preference screen
|
||||
*/
|
||||
private void addSettingsUnderHeader(final List<Integer> settings,
|
||||
@ -244,4 +246,8 @@ public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean preferServiceV2() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2022 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCapabilities;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
|
||||
public class SonyLinkBudsSCoordinator extends SonyHeadphonesCoordinator {
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(final GBDeviceCandidate candidate) {
|
||||
if (candidate.getName().contains("LinkBuds S")) {
|
||||
return DeviceType.SONY_LINKBUDS_S;
|
||||
}
|
||||
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.SONY_LINKBUDS_S;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SonyHeadphonesCapabilities> getCapabilities() {
|
||||
return Arrays.asList(
|
||||
SonyHeadphonesCapabilities.BatteryDual,
|
||||
SonyHeadphonesCapabilities.BatteryCase,
|
||||
SonyHeadphonesCapabilities.AmbientSoundControl,
|
||||
SonyHeadphonesCapabilities.EqualizerSimple,
|
||||
SonyHeadphonesCapabilities.AudioUpsampling,
|
||||
SonyHeadphonesCapabilities.ButtonModesLeftRight,
|
||||
SonyHeadphonesCapabilities.AmbientSoundControlButtonMode,
|
||||
SonyHeadphonesCapabilities.QuickAccess,
|
||||
SonyHeadphonesCapabilities.PauseWhenTakenOff,
|
||||
SonyHeadphonesCapabilities.AutomaticPowerOffWhenTakenOff
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preferServiceV2() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/* Copyright (C) 2022 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
|
||||
public enum AmbientSoundControlButtonMode {
|
||||
NC_AS_OFF((byte) 0x01),
|
||||
NC_AS((byte) 0x02),
|
||||
NC_OFF((byte) 0x03),
|
||||
AS_OFF((byte) 0x04),
|
||||
;
|
||||
|
||||
private final byte code;
|
||||
|
||||
AmbientSoundControlButtonMode(final byte code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public Map<String, Object> toPreferences() {
|
||||
return new HashMap<String, Object>() {{
|
||||
put(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE, name().toLowerCase(Locale.getDefault()));
|
||||
}};
|
||||
}
|
||||
|
||||
public static AmbientSoundControlButtonMode fromCode(final byte code) {
|
||||
for (AmbientSoundControlButtonMode value : AmbientSoundControlButtonMode.values()) {
|
||||
if (value.getCode() == code) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AmbientSoundControlButtonMode fromPreferences(final SharedPreferences prefs) {
|
||||
return AmbientSoundControlButtonMode.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE, "nc_as_off").toUpperCase());
|
||||
}
|
||||
}
|
@ -26,30 +26,11 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett
|
||||
|
||||
public class ButtonModes {
|
||||
public enum Mode {
|
||||
OFF((byte) 0xff),
|
||||
AMBIENT_SOUND_CONTROL((byte) 0x00),
|
||||
PLAYBACK_CONTROL((byte) 0x20),
|
||||
VOLUME_CONTROL((byte) 0x10);
|
||||
|
||||
private final byte code;
|
||||
|
||||
Mode(final byte code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static Mode fromCode(final byte code) {
|
||||
for (ButtonModes.Mode value : ButtonModes.Mode.values()) {
|
||||
if (value.getCode() == code) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
OFF,
|
||||
AMBIENT_SOUND_CONTROL,
|
||||
PLAYBACK_CONTROL,
|
||||
VOLUME_CONTROL,
|
||||
;
|
||||
}
|
||||
|
||||
final Mode left;
|
||||
|
@ -0,0 +1,83 @@
|
||||
/* Copyright (C) 2022 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
|
||||
public class QuickAccess {
|
||||
public enum Mode {
|
||||
OFF((byte) 0x00),
|
||||
SPOTIFY((byte) 0x01),
|
||||
;
|
||||
|
||||
private final byte code;
|
||||
|
||||
Mode(final byte code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static Mode fromCode(final byte code) {
|
||||
for (Mode value : Mode.values()) {
|
||||
if (value.getCode() == code) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final Mode doubleTap;
|
||||
final Mode tripleTap;
|
||||
|
||||
public QuickAccess(final Mode doubleTap, final Mode tripleTap) {
|
||||
this.doubleTap = doubleTap;
|
||||
this.tripleTap = tripleTap;
|
||||
}
|
||||
|
||||
public Mode getModeDoubleTap() {
|
||||
return doubleTap;
|
||||
}
|
||||
|
||||
public Mode getModeTripleTap() {
|
||||
return tripleTap;
|
||||
}
|
||||
|
||||
public Map<String, Object> toPreferences() {
|
||||
return new HashMap<String, Object>() {{
|
||||
put(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_DOUBLE_TAP, doubleTap.name().toLowerCase(Locale.getDefault()));
|
||||
put(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_TRIPLE_TAP, tripleTap.name().toLowerCase(Locale.getDefault()));
|
||||
}};
|
||||
}
|
||||
|
||||
public static QuickAccess fromPreferences(final SharedPreferences prefs) {
|
||||
return new QuickAccess(
|
||||
Mode.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_DOUBLE_TAP, "off").toUpperCase()),
|
||||
Mode.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_TRIPLE_TAP, "off").toUpperCase())
|
||||
);
|
||||
}
|
||||
}
|
@ -120,6 +120,7 @@ public enum DeviceType {
|
||||
SONY_WF_1000XM3(433, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_wf_1000xm3),
|
||||
SONY_WH_1000XM2(434, R.drawable.ic_device_sony_overhead, R.drawable.ic_device_sony_overhead_disabled, R.string.devicetype_sony_wh_1000xm2),
|
||||
SONY_WF_1000XM4(435, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_wf_1000xm4),
|
||||
SONY_LINKBUDS_S(436, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_linkbuds_s),
|
||||
BOSE_QC35(440, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_bose_qc35),
|
||||
VESC_NRF(500, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc),
|
||||
VESC_HM10(501, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc),
|
||||
|
@ -342,6 +342,8 @@ public class DeviceSupportFactory {
|
||||
return new ServiceDeviceSupport(new SonyHeadphonesSupport());
|
||||
case SONY_WF_1000XM4:
|
||||
return new ServiceDeviceSupport(new SonyHeadphonesSupport());
|
||||
case SONY_LINKBUDS_S:
|
||||
return new ServiceDeviceSupport(new SonyHeadphonesSupport());
|
||||
case VESC_NRF:
|
||||
case VESC_HM10:
|
||||
return new ServiceDeviceSupport(new VescDeviceSupport(device.getType()));
|
||||
|
@ -31,9 +31,11 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.Message;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class SonyHeadphonesIoThread extends BtClassicIoThread {
|
||||
@ -117,21 +119,30 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread {
|
||||
@NonNull
|
||||
@Override
|
||||
protected UUID getUuidToConnect(@NonNull final ParcelUuid[] uuids) {
|
||||
boolean hasV1 = false;
|
||||
boolean hasV2 = false;
|
||||
boolean preferV2 = getCoordinator().preferServiceV2();
|
||||
for (final ParcelUuid uuid : uuids) {
|
||||
if (uuid.getUuid().equals(btrfcommUuidV1)) {
|
||||
// Prioritize V1
|
||||
return btrfcommUuidV1;
|
||||
LOG.info("Found Sony UUID V1");
|
||||
hasV1 = true;
|
||||
} else if (uuid.getUuid().equals(btrfcommUuidV2)) {
|
||||
LOG.info("Found Sony UUID V2");
|
||||
hasV2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasV2) {
|
||||
LOG.info("Using Sony UUID V2");
|
||||
return btrfcommUuidV2;
|
||||
} else if (hasV1) {
|
||||
LOG.info("Using Sony UUID V1");
|
||||
return btrfcommUuidV1;
|
||||
}
|
||||
|
||||
return super.getUuidToConnect(uuids);
|
||||
LOG.warn("Failed to find a known Sony UUID, will fallback to {}", (preferV2 ? "V2" : "V1"));
|
||||
|
||||
return preferV2 ? btrfcommUuidV2 : btrfcommUuidV1;
|
||||
}
|
||||
|
||||
private void scheduleInitRetry() {
|
||||
@ -139,4 +150,8 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread {
|
||||
|
||||
handler.postDelayed(initSendRunnable, 1250);
|
||||
}
|
||||
|
||||
private SonyHeadphonesCoordinator getCoordinator() {
|
||||
return (SonyHeadphonesCoordinator) DeviceHelper.getInstance().getCoordinator(getDevice());
|
||||
}
|
||||
}
|
||||
|
@ -32,12 +32,14 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdateDeviceState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
|
||||
@ -49,6 +51,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.prot
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.AbstractSonyProtocolImpl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.SonyProtocolImplV1;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v2.SonyProtocolImplV2;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v3.SonyProtocolImplV3;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
|
||||
public class SonyHeadphonesProtocol extends GBDeviceProtocol {
|
||||
@ -101,9 +104,21 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
|
||||
if (MessageType.COMMAND_1.equals(messageType) && message.getPayload()[0] == 0x01) {
|
||||
// Init reply, set the protocol version
|
||||
if (message.getPayload().length == 4) {
|
||||
// WH-1000XM3: 01:00:40:10
|
||||
// WF-SP800N 1.0.1: 01:00:70:00
|
||||
protocolImpl = new SonyProtocolImplV1(getDevice());
|
||||
} else if (message.getPayload().length == 8) {
|
||||
protocolImpl = new SonyProtocolImplV2(getDevice());
|
||||
switch (message.getPayload()[2]) {
|
||||
case 0x01:
|
||||
// WF-1000XM4 1.1.5: 01:00:01:00:00:00:00:00
|
||||
protocolImpl = new SonyProtocolImplV2(getDevice());
|
||||
break;
|
||||
case 0x03:
|
||||
// LinkBuds S 2.0.2: 01:00:03:00:00:07:00:00
|
||||
default:
|
||||
protocolImpl = new SonyProtocolImplV3(getDevice());
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
LOG.error("Unexpected init response payload length: {}", message.getPayload().length);
|
||||
return null;
|
||||
@ -152,6 +167,9 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
|
||||
case DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL:
|
||||
configRequest = protocolImpl.setAmbientSoundControl(AmbientSoundControl.fromPreferences(prefs));
|
||||
break;
|
||||
case DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE:
|
||||
configRequest = protocolImpl.setAmbientSoundControlButtonMode(AmbientSoundControlButtonMode.fromPreferences(prefs));
|
||||
break;
|
||||
case DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_START:
|
||||
configRequest = protocolImpl.startNoiseCancellingOptimizer(true);
|
||||
break;
|
||||
@ -188,6 +206,10 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
|
||||
case DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_MODE_RIGHT:
|
||||
configRequest = protocolImpl.setButtonModes(ButtonModes.fromPreferences(prefs));
|
||||
break;
|
||||
case DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_DOUBLE_TAP:
|
||||
case DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_TRIPLE_TAP:
|
||||
configRequest = protocolImpl.setQuickAccess(QuickAccess.fromPreferences(prefs));
|
||||
break;
|
||||
case DeviceSettingsPreferenceConst.PREF_SONY_PAUSE_WHEN_TAKEN_OFF:
|
||||
configRequest = protocolImpl.setPauseWhenTakenOff(PauseWhenTakenOff.fromPreferences(prefs));
|
||||
break;
|
||||
|
@ -21,12 +21,14 @@ import java.util.List;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.TouchSensor;
|
||||
@ -76,6 +78,14 @@ public abstract class AbstractSonyProtocolImpl {
|
||||
|
||||
public abstract Request setButtonModes(final ButtonModes config);
|
||||
|
||||
public abstract Request getQuickAccess();
|
||||
|
||||
public abstract Request setQuickAccess(final QuickAccess quickAccess);
|
||||
|
||||
public abstract Request getAmbientSoundControlButtonMode();
|
||||
|
||||
public abstract Request setAmbientSoundControlButtonMode(final AmbientSoundControlButtonMode ambientSoundControlButtonMode);
|
||||
|
||||
public abstract Request getPauseWhenTakenOff();
|
||||
|
||||
public abstract Request setPauseWhenTakenOff(final PauseWhenTakenOff config);
|
||||
|
@ -41,12 +41,14 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInf
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCapabilities;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.TouchSensor;
|
||||
@ -247,12 +249,36 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
|
||||
PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(),
|
||||
(byte) 0x06,
|
||||
(byte) 0x02,
|
||||
config.getModeLeft().getCode(),
|
||||
config.getModeRight().getCode()
|
||||
encodeButtonMode(config.getModeLeft()),
|
||||
encodeButtonMode(config.getModeRight())
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getQuickAccess() {
|
||||
LOG.warn("Quick access not implemented for V1");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request setQuickAccess(final QuickAccess quickAccess) {
|
||||
LOG.warn("Quick access not implemented for V1");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getAmbientSoundControlButtonMode() {
|
||||
LOG.warn("Ambient sound control button modes not implemented for V1");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request setAmbientSoundControlButtonMode(final AmbientSoundControlButtonMode ambientSoundControlButtonMode) {
|
||||
LOG.warn("Ambient sound control button modes not implemented for V1");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getPauseWhenTakenOff() {
|
||||
return new Request(
|
||||
@ -517,6 +543,8 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
|
||||
put(SonyHeadphonesCapabilities.SoundPosition, getSoundPosition());
|
||||
put(SonyHeadphonesCapabilities.SurroundMode, getSurroundMode());
|
||||
put(SonyHeadphonesCapabilities.PauseWhenTakenOff, getPauseWhenTakenOff());
|
||||
put(SonyHeadphonesCapabilities.AmbientSoundControlButtonMode, getAmbientSoundControlButtonMode());
|
||||
put(SonyHeadphonesCapabilities.QuickAccess, getQuickAccess());
|
||||
}};
|
||||
|
||||
for (Map.Entry<SonyHeadphonesCapabilities, Request> capabilityEntry : capabilityRequestMap.entrySet()) {
|
||||
@ -685,8 +713,8 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final ButtonModes.Mode modeLeft = ButtonModes.Mode.fromCode(payload[3]);
|
||||
final ButtonModes.Mode modeRight = ButtonModes.Mode.fromCode(payload[4]);
|
||||
final ButtonModes.Mode modeLeft = decodeButtonMode(payload[3]);
|
||||
final ButtonModes.Mode modeRight = decodeButtonMode(payload[4]);
|
||||
if (modeLeft == null || modeRight == null) {
|
||||
LOG.warn("Unknown button mode codes {}", String.format("%02x %02x", payload[3], payload[4]));
|
||||
return Collections.emptyList();
|
||||
@ -1063,4 +1091,34 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
|
||||
|
||||
throw new IllegalArgumentException("Unknown battery type " + batteryType);
|
||||
}
|
||||
|
||||
protected ButtonModes.Mode decodeButtonMode(final byte b) {
|
||||
switch (b) {
|
||||
case (byte) 0xff:
|
||||
return ButtonModes.Mode.OFF;
|
||||
case (byte) 0x00:
|
||||
return ButtonModes.Mode.AMBIENT_SOUND_CONTROL;
|
||||
case (byte) 0x20:
|
||||
return ButtonModes.Mode.PLAYBACK_CONTROL;
|
||||
case (byte) 0x10:
|
||||
return ButtonModes.Mode.VOLUME_CONTROL;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected byte encodeButtonMode(final ButtonModes.Mode buttonMode) {
|
||||
switch (buttonMode) {
|
||||
case OFF:
|
||||
return (byte) 0xff;
|
||||
case AMBIENT_SOUND_CONTROL:
|
||||
return (byte) 0x00;
|
||||
case PLAYBACK_CONTROL:
|
||||
return (byte) 0x20;
|
||||
case VOLUME_CONTROL:
|
||||
return (byte) 0x10;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown button mode " + buttonMode);
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.pro
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType;
|
||||
|
||||
public enum PayloadTypeV2 {
|
||||
AUDIO_CODEC_REQUEST(MessageType.COMMAND_1, 0x13), // TODO confirm
|
||||
AUDIO_CODEC_REPLY(MessageType.COMMAND_1, 0x14), // TODO confirm
|
||||
AUDIO_CODEC_NOTIFY(MessageType.COMMAND_1, 0x15),
|
||||
|
||||
BATTERY_LEVEL_REQUEST(MessageType.COMMAND_1, 0x22),
|
||||
BATTERY_LEVEL_REPLY(MessageType.COMMAND_1, 0x23),
|
||||
BATTERY_LEVEL_NOTIFY(MessageType.COMMAND_1, 0x24), // TODO confirm
|
||||
|
@ -32,12 +32,14 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInf
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdateDeviceInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.TouchSensor;
|
||||
@ -110,13 +112,8 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
||||
|
||||
@Override
|
||||
public Request getAudioCodec() {
|
||||
return new Request(
|
||||
PayloadTypeV2.AUDIO_CODEC_REQUEST.getMessageType(),
|
||||
new byte[]{
|
||||
PayloadTypeV2.AUDIO_CODEC_REQUEST.getCode(),
|
||||
(byte) 0x00
|
||||
}
|
||||
);
|
||||
LOG.warn("Audio codec not implemented for V2");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -201,12 +198,36 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
||||
PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(),
|
||||
(byte) 0x03,
|
||||
(byte) 0x02,
|
||||
config.getModeLeft().getCode(),
|
||||
config.getModeRight().getCode()
|
||||
encodeButtonMode(config.getModeLeft()),
|
||||
encodeButtonMode(config.getModeRight())
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getQuickAccess() {
|
||||
LOG.warn("Quick access not implemented for V2");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request setQuickAccess(final QuickAccess quickAccess) {
|
||||
LOG.warn("Quick access not implemented for V2");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getAmbientSoundControlButtonMode() {
|
||||
LOG.warn("Ambient sound control button modes not implemented for V2");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request setAmbientSoundControlButtonMode(final AmbientSoundControlButtonMode ambientSoundControlButtonMode) {
|
||||
LOG.warn("Ambient sound control button modes not implemented for V2");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getPauseWhenTakenOff() {
|
||||
return new Request(
|
||||
@ -325,8 +346,6 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
||||
final PayloadTypeV2 payloadType = PayloadTypeV2.fromCode(messageType, payload[0]);
|
||||
|
||||
switch (payloadType) {
|
||||
case AUDIO_CODEC_NOTIFY:
|
||||
return handleAudioCodec(payload);
|
||||
case BATTERY_LEVEL_NOTIFY:
|
||||
case BATTERY_LEVEL_REPLY:
|
||||
return handleBattery(payload);
|
||||
@ -477,8 +496,8 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final ButtonModes.Mode modeLeft = ButtonModes.Mode.fromCode(payload[3]);
|
||||
final ButtonModes.Mode modeRight = ButtonModes.Mode.fromCode(payload[4]);
|
||||
final ButtonModes.Mode modeLeft = decodeButtonMode(payload[3]);
|
||||
final ButtonModes.Mode modeRight = decodeButtonMode(payload[4]);
|
||||
|
||||
if (modeLeft == null || modeRight == null) {
|
||||
LOG.warn("Unknown button mode codes {}", String.format("%02x %02x", payload[3], payload[4]));
|
||||
@ -526,28 +545,8 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
||||
|
||||
@Override
|
||||
public List<? extends GBDeviceEvent> handleAudioCodec(final byte[] payload) {
|
||||
if (payload.length != 3) {
|
||||
LOG.warn("Unexpected payload length {}", payload.length);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (payload[1] != 0x03) {
|
||||
LOG.warn("Not audio codec, ignoring");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final AudioCodec audioCodec = AudioCodec.fromCode(payload[2]);
|
||||
if (audioCodec == null) {
|
||||
LOG.warn("Unable to determine audio codec from {}", GB.hexdump(payload));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final GBDeviceEventUpdateDeviceInfo gbDeviceEventUpdateDeviceInfo = new GBDeviceEventUpdateDeviceInfo("AUDIO_CODEC: ", audioCodec.name());
|
||||
|
||||
final GBDeviceEventUpdatePreferences gbDeviceEventUpdatePreferences = new GBDeviceEventUpdatePreferences()
|
||||
.withPreference(DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_CODEC, audioCodec.name().toLowerCase(Locale.getDefault()));
|
||||
|
||||
return Arrays.asList(gbDeviceEventUpdateDeviceInfo, gbDeviceEventUpdatePreferences);
|
||||
LOG.warn("Audio codec not implemented for V2");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,59 @@
|
||||
/* Copyright (C) 2022 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v3;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType;
|
||||
|
||||
public enum PayloadTypeV3 {
|
||||
QUICK_ACCESS_GET(MessageType.COMMAND_1, 0xf6),
|
||||
QUICK_ACCESS_RET(MessageType.COMMAND_1, 0xf7),
|
||||
QUICK_ACCESS_SET(MessageType.COMMAND_1, 0xf8),
|
||||
QUICK_ACCESS_NOTIFY(MessageType.COMMAND_1, 0xf9),
|
||||
|
||||
AMBIENT_SOUND_CONTROL_BUTTON_MODE_GET(MessageType.COMMAND_1, 0xfa),
|
||||
AMBIENT_SOUND_CONTROL_BUTTON_MODE_RET(MessageType.COMMAND_1, 0xfb),
|
||||
AMBIENT_SOUND_CONTROL_BUTTON_MODE_SET(MessageType.COMMAND_1, 0xfc),
|
||||
AMBIENT_SOUND_CONTROL_BUTTON_MODE_NOTIFY(MessageType.COMMAND_1, 0xfd),
|
||||
|
||||
UNKNOWN(MessageType.UNKNOWN, 0xff);
|
||||
|
||||
private final MessageType messageType;
|
||||
private final byte code;
|
||||
|
||||
PayloadTypeV3(final MessageType messageType, final int code) {
|
||||
this.messageType = messageType;
|
||||
this.code = (byte) code;
|
||||
}
|
||||
|
||||
public MessageType getMessageType() {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static PayloadTypeV3 fromCode(final MessageType messageType, final byte code) {
|
||||
for (final PayloadTypeV3 payloadType : values()) {
|
||||
if (messageType.equals(payloadType.messageType) && payloadType.code == code) {
|
||||
return payloadType;
|
||||
}
|
||||
}
|
||||
|
||||
return PayloadTypeV3.UNKNOWN;
|
||||
}
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
/* Copyright (C) 2022 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v3;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCapabilities;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.Request;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.PayloadTypeV1;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v2.PayloadTypeV2;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v2.SonyProtocolImplV2;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class SonyProtocolImplV3 extends SonyProtocolImplV2 {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SonyProtocolImplV3.class);
|
||||
|
||||
public SonyProtocolImplV3(final GBDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getAmbientSoundControl() {
|
||||
return new Request(
|
||||
PayloadTypeV1.AMBIENT_SOUND_CONTROL_GET.getMessageType(),
|
||||
new byte[]{
|
||||
PayloadTypeV1.AMBIENT_SOUND_CONTROL_GET.getCode(),
|
||||
(byte) 0x17
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request setAmbientSoundControl(final AmbientSoundControl ambientSoundControl) {
|
||||
final ByteBuffer buf = ByteBuffer.allocate(8);
|
||||
|
||||
buf.put(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getCode());
|
||||
buf.put((byte) 0x17);
|
||||
buf.put((byte) 0x01); // 0x00 while dragging the slider?
|
||||
|
||||
if (AmbientSoundControl.Mode.OFF.equals(ambientSoundControl.getMode())) {
|
||||
buf.put((byte) 0x00);
|
||||
} else {
|
||||
buf.put((byte) 0x01);
|
||||
}
|
||||
|
||||
if (AmbientSoundControl.Mode.AMBIENT_SOUND.equals(ambientSoundControl.getMode())) {
|
||||
buf.put((byte) 0x01);
|
||||
} else {
|
||||
buf.put((byte) 0x00);
|
||||
}
|
||||
|
||||
buf.put((byte) (ambientSoundControl.isFocusOnVoice() ? 0x01 : 0x00));
|
||||
buf.put((byte) (ambientSoundControl.getAmbientSound()));
|
||||
|
||||
return new Request(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getQuickAccess() {
|
||||
return new Request(
|
||||
PayloadTypeV3.QUICK_ACCESS_GET.getMessageType(),
|
||||
new byte[]{
|
||||
PayloadTypeV3.QUICK_ACCESS_GET.getCode(),
|
||||
(byte) 0x0d
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request setQuickAccess(final QuickAccess quickAccess) {
|
||||
return new Request(
|
||||
PayloadTypeV3.QUICK_ACCESS_SET.getMessageType(),
|
||||
new byte[]{
|
||||
PayloadTypeV3.QUICK_ACCESS_SET.getCode(),
|
||||
(byte) 0x0d,
|
||||
(byte) 0x02,
|
||||
quickAccess.getModeDoubleTap().getCode(),
|
||||
quickAccess.getModeTripleTap().getCode()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getAmbientSoundControlButtonMode() {
|
||||
return new Request(
|
||||
PayloadTypeV3.AMBIENT_SOUND_CONTROL_BUTTON_MODE_GET.getMessageType(),
|
||||
new byte[]{
|
||||
PayloadTypeV3.AMBIENT_SOUND_CONTROL_BUTTON_MODE_GET.getCode(),
|
||||
(byte) 0x03
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request setAmbientSoundControlButtonMode(final AmbientSoundControlButtonMode ambientSoundControlButtonMode) {
|
||||
return new Request(
|
||||
PayloadTypeV3.AMBIENT_SOUND_CONTROL_BUTTON_MODE_SET.getMessageType(),
|
||||
new byte[]{
|
||||
PayloadTypeV3.AMBIENT_SOUND_CONTROL_BUTTON_MODE_SET.getCode(),
|
||||
(byte) 0x03,
|
||||
(byte) 0x01,
|
||||
(byte) 0x35,
|
||||
(byte) 0x01,
|
||||
(byte) 0x00,
|
||||
ambientSoundControlButtonMode.getCode()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends GBDeviceEvent> handlePayload(final MessageType messageType, final byte[] payload) {
|
||||
final PayloadTypeV3 payloadType = PayloadTypeV3.fromCode(messageType, payload[0]);
|
||||
|
||||
switch (payloadType) {
|
||||
case QUICK_ACCESS_RET:
|
||||
case QUICK_ACCESS_NOTIFY:
|
||||
return handleQuickAccess(payload);
|
||||
case AMBIENT_SOUND_CONTROL_BUTTON_MODE_RET:
|
||||
case AMBIENT_SOUND_CONTROL_BUTTON_MODE_NOTIFY:
|
||||
return handleAmbientSoundControlButtonMode(payload);
|
||||
}
|
||||
|
||||
return super.handlePayload(messageType, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends GBDeviceEvent> handleAmbientSoundControl(final byte[] payload) {
|
||||
if (payload.length != 7) {
|
||||
LOG.warn("Unexpected payload length {}", payload.length);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (payload[1] != 0x17) {
|
||||
LOG.warn("Not ambient sound control, ignoring {}", payload[1]);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
AmbientSoundControl.Mode mode = null;
|
||||
|
||||
if (payload[3] == (byte) 0x00) {
|
||||
mode = AmbientSoundControl.Mode.OFF;
|
||||
} else if (payload[3] == (byte) 0x01) {
|
||||
// Enabled, determine mode
|
||||
|
||||
if (payload[4] == (byte) 0x00) {
|
||||
mode = AmbientSoundControl.Mode.NOISE_CANCELLING;
|
||||
} else if (payload[4] == (byte) 0x01) {
|
||||
mode = AmbientSoundControl.Mode.AMBIENT_SOUND;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == null) {
|
||||
LOG.warn("Unable to determine ambient sound control mode from {}", GB.hexdump(payload));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Boolean focusOnVoice = booleanFromByte(payload[5]);
|
||||
if (focusOnVoice == null) {
|
||||
LOG.warn("Unknown focus on voice mode {}", String.format("%02x", payload[5]));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
int ambientSound = payload[6];
|
||||
if (ambientSound < 0 || ambientSound > 20) {
|
||||
LOG.warn("Ambient sound level {} is out of range", String.format("%02x", payload[6]));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final AmbientSoundControl ambientSoundControl = new AmbientSoundControl(mode, focusOnVoice, ambientSound);
|
||||
|
||||
LOG.warn("Ambient sound control: {}", ambientSoundControl);
|
||||
|
||||
final GBDeviceEventUpdatePreferences eventUpdatePreferences = new GBDeviceEventUpdatePreferences()
|
||||
.withPreferences(ambientSoundControl.toPreferences());
|
||||
|
||||
return Collections.singletonList(eventUpdatePreferences);
|
||||
}
|
||||
|
||||
public List<? extends GBDeviceEvent> handleQuickAccess(final byte[] payload) {
|
||||
if (payload.length != 5) {
|
||||
LOG.warn("Unexpected payload length {}", payload.length);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (payload[1] != 0x0d || payload[2] != 0x02) {
|
||||
LOG.warn("Unexpected quick access payload bytes {}", String.format("%02x %02x", payload[1], payload[2]));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final QuickAccess.Mode modeDouble = QuickAccess.Mode.fromCode(payload[3]);
|
||||
final QuickAccess.Mode modeTriple = QuickAccess.Mode.fromCode(payload[4]);
|
||||
if (modeDouble == null || modeTriple == null) {
|
||||
LOG.warn("Unknown quick access codes {}", String.format("%02x %02x", payload[3], payload[4]));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
LOG.debug("Quick Access: Double Tap: {}, Triple Tap: {}", modeDouble, modeTriple);
|
||||
|
||||
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
|
||||
.withPreferences(new QuickAccess(modeDouble, modeTriple).toPreferences());
|
||||
|
||||
return Collections.singletonList(event);
|
||||
}
|
||||
|
||||
public List<? extends GBDeviceEvent> handleAmbientSoundControlButtonMode(final byte[] payload) {
|
||||
if (payload.length != 7) {
|
||||
LOG.warn("Unexpected payload length {}", payload.length);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (payload[1] != 0x03 || payload[2] != 0x01 || payload[3] != 0x35 || payload[4] != 0x01 || payload[5] != 0x00) {
|
||||
LOG.warn(
|
||||
"Unexpected ambient sound control button mode payload bytes {}",
|
||||
String.format("%02x %02x %02x %02x %02x", payload[1], payload[2], payload[3], payload[4], payload[5])
|
||||
);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final AmbientSoundControlButtonMode mode = AmbientSoundControlButtonMode.fromCode(payload[6]);
|
||||
if (mode == null) {
|
||||
LOG.warn("Unknown ambient sound control button mode code {}", String.format("%02x", payload[6]));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
LOG.debug("Ambient Sound Control Buton Mode: {}", mode);
|
||||
|
||||
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
|
||||
.withPreferences(mode.toPreferences());
|
||||
|
||||
return Collections.singletonList(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ButtonModes.Mode decodeButtonMode(final byte b) {
|
||||
switch (b) {
|
||||
case (byte) 0xff:
|
||||
return ButtonModes.Mode.OFF;
|
||||
case (byte) 0x35: // Seems to be the only one that differs?
|
||||
return ButtonModes.Mode.AMBIENT_SOUND_CONTROL;
|
||||
case (byte) 0x20:
|
||||
return ButtonModes.Mode.PLAYBACK_CONTROL;
|
||||
case (byte) 0x10:
|
||||
return ButtonModes.Mode.VOLUME_CONTROL;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte encodeButtonMode(final ButtonModes.Mode buttonMode) {
|
||||
switch (buttonMode) {
|
||||
case OFF:
|
||||
return (byte) 0xff;
|
||||
case AMBIENT_SOUND_CONTROL:
|
||||
return (byte) 0x35; // Seems to be the only one that differs?
|
||||
case PLAYBACK_CONTROL:
|
||||
return (byte) 0x20;
|
||||
case VOLUME_CONTROL:
|
||||
return (byte) 0x10;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown button mode " + buttonMode);
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts4.AmazfitGTS4Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts4mini.AmazfitGTS4MiniCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyLinkBudsSCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.supercars.SuperCarsCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.fitpro.FitProDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator;
|
||||
@ -345,6 +346,7 @@ public class DeviceHelper {
|
||||
result.add(new GalaxyBudsProDeviceCoordinator());
|
||||
result.add(new GalaxyBuds2DeviceCoordinator());
|
||||
result.add(new VescCoordinator());
|
||||
result.add(new SonyLinkBudsSCoordinator());
|
||||
result.add(new SonyWH1000XM3Coordinator());
|
||||
result.add(new SonyWH1000XM4Coordinator());
|
||||
result.add(new SonyWFSP800NCoordinator());
|
||||
|
@ -2729,6 +2729,30 @@
|
||||
<item>volume_control</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="sony_quick_access_names">
|
||||
<item>@string/off</item>
|
||||
<item>@string/pref_title_touch_spotify</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="sony_quick_access_values">
|
||||
<item>off</item>
|
||||
<item>spotify</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="sony_ambient_sound_control_button_mode_names">
|
||||
<item>@string/sony_ambient_sound_control_button_mode_nc_as_off</item>
|
||||
<item>@string/sony_ambient_sound_control_button_mode_nc_as</item>
|
||||
<item>@string/sony_ambient_sound_control_button_mode_nc_off</item>
|
||||
<item>@string/sony_ambient_sound_control_button_mode_as_off</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="sony_ambient_sound_control_button_mode_values">
|
||||
<item>nc_as_off</item>
|
||||
<item>nc_as</item>
|
||||
<item>nc_off</item>
|
||||
<item>as_off</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="fossil_hr_activity_recognition_modes">
|
||||
<item>@string/pref_activity_recognition_mode_none</item>
|
||||
<item>@string/pref_activity_recognition_mode_ask</item>
|
||||
|
@ -1230,6 +1230,7 @@
|
||||
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
|
||||
<string name="devicetype_sony_wf_1000xm3">Sony WF-1000XM3</string>
|
||||
<string name="devicetype_sony_wf_1000xm4">Sony WF-1000XM4</string>
|
||||
<string name="devicetype_sony_linkbuds_s">Sony LinkBuds S</string>
|
||||
<string name="devicetype_binary_sensor">Binary sensor</string>
|
||||
<string name="choose_auto_export_location">Choose export location</string>
|
||||
<string name="notification_channel_name">General</string>
|
||||
@ -1877,6 +1878,13 @@
|
||||
<string name="sony_button_mode_ambient_sound_control">Ambient Sound Control</string>
|
||||
<string name="sony_button_mode_playback_control">Playback Control</string>
|
||||
<string name="sony_button_mode_volume_control">Volume Control</string>
|
||||
<string name="sony_ambient_sound_control_button_modes">Ambient Sound Control Button Modes</string>
|
||||
<string name="sony_ambient_sound_control_button_mode_nc_as_off">Noise Cancelling, Ambient Sound, Off</string>
|
||||
<string name="sony_ambient_sound_control_button_mode_nc_as">Noise Cancelling, Ambient Sound</string>
|
||||
<string name="sony_ambient_sound_control_button_mode_nc_off">Noise Cancelling, Off</string>
|
||||
<string name="sony_ambient_sound_control_button_mode_as_off">Ambient Sound, Off</string>
|
||||
<string name="sony_quick_access_double_tap">Quick Access (Double Tap)</string>
|
||||
<string name="sony_quick_access_triple_tap">Quick Access (Triple Tap)</string>
|
||||
<string name="pref_screen_auto_brightness_title">Auto Brightness</string>
|
||||
<string name="pref_screen_auto_brightness_summary">Adjust screen brightness according to ambient light</string>
|
||||
<string name="pref_screen_brightness">Screen Brightness</string>
|
||||
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<ListPreference
|
||||
android:defaultValue="nc_as_off"
|
||||
android:entries="@array/sony_ambient_sound_control_button_mode_names"
|
||||
android:entryValues="@array/sony_ambient_sound_control_button_mode_values"
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:key="pref_sony_ambient_sound_control_button_mode"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_ambient_sound_control_button_modes" />
|
||||
</androidx.preference.PreferenceScreen>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<ListPreference
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/sony_quick_access_names"
|
||||
android:entryValues="@array/sony_quick_access_values"
|
||||
android:icon="@drawable/ic_filter_2"
|
||||
android:key="pref_sony_quick_access_double_tap"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_quick_access_double_tap" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/sony_quick_access_names"
|
||||
android:entryValues="@array/sony_quick_access_values"
|
||||
android:icon="@drawable/ic_filter_3"
|
||||
android:key="pref_sony_quick_access_triple_tap"
|
||||
android:summary="%s"
|
||||
android:title="@string/sony_quick_access_triple_tap" />
|
||||
</androidx.preference.PreferenceScreen>
|
@ -17,6 +17,7 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v2;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.assertRequest;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.handleMessage;
|
||||
@ -325,6 +326,7 @@ public class SonyProtocolImplV2Test {
|
||||
final Object modePrefValue = entry.getKey()
|
||||
.toPreferences()
|
||||
.get(DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE);
|
||||
assertNotNull(modePrefValue);
|
||||
assertEquals(modePrefValue, event.preferences.get(DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,134 @@
|
||||
/* Copyright (C) 2022 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v3;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.assertRequest;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.handleMessage;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyLinkBudsSCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.Request;
|
||||
|
||||
public class SonyProtocolImplV3Test {
|
||||
private final SonyProtocolImplV3 protocol = new SonyProtocolImplV3(null) {
|
||||
@Override
|
||||
protected SonyHeadphonesCoordinator getCoordinator() {
|
||||
return new SonyLinkBudsSCoordinator();
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void getQuickAccess() {
|
||||
final Request request = protocol.getQuickAccess();
|
||||
assertRequest(request, "3e:0c:00:00:00:00:02:f6:0d:11:3c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setQuickAccess() {
|
||||
final Map<QuickAccess, String> commands = new LinkedHashMap<QuickAccess, String>() {{
|
||||
put(new QuickAccess(QuickAccess.Mode.OFF, QuickAccess.Mode.OFF), "3e:0c:01:00:00:00:05:f8:0d:02:00:00:19:3c");
|
||||
put(new QuickAccess(QuickAccess.Mode.OFF, QuickAccess.Mode.SPOTIFY), "3e:0c:00:00:00:00:05:f8:0d:02:00:01:19:3c");
|
||||
put(new QuickAccess(QuickAccess.Mode.SPOTIFY, QuickAccess.Mode.OFF), "3e:0c:00:00:00:00:05:f8:0d:02:01:00:19:3c");
|
||||
}};
|
||||
|
||||
for (Map.Entry<QuickAccess, String> entry : commands.entrySet()) {
|
||||
final Request request = protocol.setQuickAccess(entry.getKey());
|
||||
assertRequest(request, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAmbientSoundControlButtonMode() {
|
||||
final Request request = protocol.getAmbientSoundControlButtonMode();
|
||||
assertRequest(request, "3e:0c:00:00:00:00:02:fa:03:0b:3c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAmbientSoundControlButtonMode() {
|
||||
final Map<AmbientSoundControlButtonMode, String> commands = new LinkedHashMap<AmbientSoundControlButtonMode, String>() {{
|
||||
put(AmbientSoundControlButtonMode.NC_AS_OFF, "3e:0c:00:00:00:00:07:fc:03:01:35:01:00:01:4a:3c");
|
||||
put(AmbientSoundControlButtonMode.NC_AS, "3e:0c:01:00:00:00:07:fc:03:01:35:01:00:02:4c:3c");
|
||||
put(AmbientSoundControlButtonMode.NC_OFF, "3e:0c:01:00:00:00:07:fc:03:01:35:01:00:03:4d:3c");
|
||||
put(AmbientSoundControlButtonMode.AS_OFF, "3e:0c:01:00:00:00:07:fc:03:01:35:01:00:04:4e:3c");
|
||||
}};
|
||||
|
||||
for (Map.Entry<AmbientSoundControlButtonMode, String> entry : commands.entrySet()) {
|
||||
final Request request = protocol.setAmbientSoundControlButtonMode(entry.getKey());
|
||||
assertRequest(request, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleQuickAccess() {
|
||||
final Map<QuickAccess, String> commands = new LinkedHashMap<QuickAccess, String>() {{
|
||||
// Notify
|
||||
put(new QuickAccess(QuickAccess.Mode.OFF, QuickAccess.Mode.OFF), "3e:0c:00:00:00:00:05:f9:0d:02:00:00:19:3c");
|
||||
put(new QuickAccess(QuickAccess.Mode.OFF, QuickAccess.Mode.SPOTIFY), "3e:0c:01:00:00:00:05:f9:0d:02:00:01:1b:3c");
|
||||
put(new QuickAccess(QuickAccess.Mode.SPOTIFY, QuickAccess.Mode.OFF), "3e:0c:01:00:00:00:05:f9:0d:02:01:00:1b:3c");
|
||||
}};
|
||||
|
||||
for (Map.Entry<QuickAccess, String> entry : commands.entrySet()) {
|
||||
final List<? extends GBDeviceEvent> events = handleMessage(protocol, entry.getValue());
|
||||
assertEquals("Expect 1 events", 1, events.size());
|
||||
final GBDeviceEventUpdatePreferences event = (GBDeviceEventUpdatePreferences) events.get(0);
|
||||
final Map<String, Object> expectedPrefs = entry.getKey().toPreferences();
|
||||
assertEquals("Expect 2 prefs", 2, expectedPrefs.size());
|
||||
final Object prefDoubleTap = expectedPrefs.get(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_DOUBLE_TAP);
|
||||
assertNotNull(prefDoubleTap);
|
||||
assertEquals(prefDoubleTap, event.preferences.get(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_DOUBLE_TAP));
|
||||
final Object prefTripleTap = expectedPrefs.get(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_TRIPLE_TAP);
|
||||
assertNotNull(prefTripleTap);
|
||||
assertEquals(prefTripleTap, event.preferences.get(DeviceSettingsPreferenceConst.PREF_SONY_QUICK_ACCESS_TRIPLE_TAP));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleAmbientSoundControlButtonMode() {
|
||||
final Map<AmbientSoundControlButtonMode, String> commands = new LinkedHashMap<AmbientSoundControlButtonMode, String>() {{
|
||||
// Notify
|
||||
put(AmbientSoundControlButtonMode.NC_AS_OFF, "3e:0c:01:00:00:00:07:fd:03:01:35:01:00:01:4c:3c");
|
||||
put(AmbientSoundControlButtonMode.NC_AS, "3e:0c:00:00:00:00:07:fd:03:01:35:01:00:02:4c:3c");
|
||||
put(AmbientSoundControlButtonMode.NC_OFF, "3e:0c:00:00:00:00:07:fd:03:01:35:01:00:03:4d:3c");
|
||||
put(AmbientSoundControlButtonMode.AS_OFF, "3e:0c:01:00:00:00:07:fd:03:01:35:01:00:04:4f:3c");
|
||||
}};
|
||||
|
||||
for (Map.Entry<AmbientSoundControlButtonMode, String> entry : commands.entrySet()) {
|
||||
final List<? extends GBDeviceEvent> events = handleMessage(protocol, entry.getValue());
|
||||
assertEquals("Expect 1 events", 1, events.size());
|
||||
final GBDeviceEventUpdatePreferences event = (GBDeviceEventUpdatePreferences) events.get(0);
|
||||
final Map<String, Object> expectedPrefs = entry.getKey().toPreferences();
|
||||
assertEquals("Expect 1 prefs", 1, expectedPrefs.size());
|
||||
final Object modePrefValue = expectedPrefs
|
||||
.get(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE);
|
||||
assertNotNull(modePrefValue);
|
||||
assertEquals(modePrefValue, event.preferences.get(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL_BUTTON_MODE));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user