Adds advanced settings for EarFun Air Pro 4

This commit is contained in:
Lars Berning
2025-03-30 19:30:47 +00:00
committed by José Rebelo
parent fbff769333
commit cab12149d6
30 changed files with 719 additions and 310 deletions
@@ -81,9 +81,20 @@ public class EarFunAirPro4Coordinator extends AbstractEarFunCoordinator {
@Override
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_headphones);
// Category Audio Experience
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_header_audio_experience);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_10_band_equalizer);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_sound_control);
// Category Audio Quality & Connectivity
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_header_connectivity);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_audio_quality);
// Category System Settings
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_header_system_settings);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_gestures);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_in_ear_detection);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_find_device);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_device_name);
final List<Integer> callsAndNotif = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS);
callsAndNotif.add(R.xml.devicesettings_headphones);
@@ -79,8 +79,17 @@ public class EarFunAirSCoordinator extends AbstractEarFunCoordinator {
@Override
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_headphones);
// Category Audio Experience
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_header_audio_experience);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_6_band_equalizer);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_sound_control);
// Category Audio Quality & Connectivity
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_header_connectivity);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_audio_quality);
// Category System Settings
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_header_system_settings);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_gestures);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_device_name);
final List<Integer> callsAndNotif = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS);
@@ -40,9 +40,7 @@ public class EarFunIOThread extends BtClassicIoThread {
protected void initialize() {
super.initialize();
write(earFunProtocol.encodeFirmwareVersionReq());
write(earFunProtocol.encodeBatteryReq());
write(earFunProtocol.encodeSoundReq());
write(earFunProtocol.encodeTouchActionReq());
write(earFunProtocol.encodeSettingsReq());
}
@Override
@@ -69,6 +69,7 @@ public class EarFunPacket {
REQUEST_RESPONSE_0300((short) 0x0300), // 00030301
REQUEST_RESPONSE_BATTERY_STATE_LEFT((short) 0x0306),
REQUEST_RESPONSE_BATTERY_STATE_RIGHT((short) 0x0307),
COMMAND_REBOOT((short) 0x0308),
REQUEST_RESPONSE_FIRMWARE_VERSION((short) 0x0309),
SET_GAME_MODE((short) 0x0312),
REQUEST_RESPONSE_GAME_MODE((short) 0x0313),
@@ -76,26 +77,37 @@ public class EarFunPacket {
REQUEST_RESPONSE_AMBIENT_SOUND((short) 0x0315),
SET_DEVICENAME((short) 0x0316),
REQUEST_RESPONSE_BATTERY_STATE_CASE((short) 0x0317),
REQUEST_RESPONSE_0318((short) 0x0318), // 0000 or 0001
REQUEST_RESPONSE_0318((short) 0x0318), // 0000 or 0001 query constantly send
UNIDENTIFIED_0321((short) 0x0321), // Pro 4
UNIDENTIFIED_0326((short) 0x0326), // 0001 Pro 4
REQUEST_RESPONSE_CONNECT_TWO_DEVICES((short) 0x0326), // 0001
SET_CONNECT_TWO_DEVICES((short) 0x0327), // 00, 01
SET_TOUCH_ACTION((short) 0x030A),
REQUEST_RESPONSE_TOUCH_ACTION((short) 0x030B),
UNIDENTIFIED_032C((short) 0x032C), // 00004C47205137000000000000000000000000000000006DC3F201 Pro 4
UNIDENTIFIED_032F((short) 0x032F), // 0013 Pro 4 // check
UNIDENTIFIED_0331((short) 0x0331), // 0000 Pro 4 // check
UNIDENTIFIED_0333((short) 0x0333), // 0000 Pro 4 // check
UNIDENTIFIED_0335((short) 0x0335), // 0000 Pro 4 // check
UNIDENTIFIED_0339((short) 0x0339), // 0001 Pro 4 // check
UNIDENTIFIED_0329((short) 0x0329), // 00 = enable first device?, 01 = enable second device? SET
UNIDENTIFIED_032A((short) 0x032A), // 00 = disable first device, 01 = disable second device SET
UNIDENTIFIED_032B((short) 0x032B), // trigger pairing?
REQUEST_RESPONSE_CONNECTED_DEVICES((short) 0x032C), // names of paired devices + something else
SET_AUDIO_CODEC((short) 0x032E), // 00 = Stable Connection, 01 = aptX, 03 = aptX Adaptive, 13 = aptX Lossless, 08 = LDAC
REQUEST_RESPONSE_AUDIO_CODEC((short) 0x032F), // 0013
SET_MICROPHONE_MODE((short) 0x0330), // 00 = auto, 01 = left, 02 =right
REQUEST_RESPONSE_MICROPHONE_MODE((short) 0x0331), // 0000
SET_FIND_DEVICE((short) 0x0332), // 00 = off, 01 = left, 02 = right, 03 = both
REQUEST_RESPONSE_FIND_DEVICE((short) 0x0333), // 0000
SET_TOUCH_MODE((short) 0x0334), // 00 = both, 01 = none, 02 = right, 03 = left
REQUEST_RESPONSE_TOUCH_MODE((short) 0x0335), // 0000
SET_VOICE_PROMPT_VOLUME((short) 0x0338), // 00 (max) - 04 (min)
REQUEST_RESPONSE_VOICE_PROMPT_VOLUME((short) 0x0339), // 0000 (max) - 0004 (min)
SET_ANC_MODE((short) 0x033A),
REQUEST_RESPONSE_ANC_MODE((short) 0x033B),
SET_TRANSPARENCY_MODE((short) 0x033C),
REQUEST_RESPONSE_TRANSPARENCY_MODE((short) 0x033D),
UNIDENTIFIED_034A((short) 0x034A), // 0000 Pro 4
UNIDENTIFIED_034C((short) 0x034C), // 0000 Pro 4
UNIDENTIFIED_034D((short) 0x034D), // 0003 Pro 4
UNIDENTIFIED_0348((short) 0x0348), // no REQUEST
UNIDENTIFIED_0350((short) 0x0350), // 0001 Pro 4
UNIDENTIFIED_0348((short) 0x0348), // from phone to device
SET_DISABLE_IN_EAR_DETECTION((short) 0x349), // 00, 01
REQUEST_RESPONSE_DISABLE_IN_EAR_DETECTION((short) 0x034A), // 0000
SET_ADVANCED_AUDIO_MODE((short) 0x034B), // 00 = Google Fast Pair, 01 = LE Audio
REQUEST_RESPONSE_ADVANCED_AUDIO_MODE((short) 0x034C), // 0000
REQUEST_RESPONSE_034D((short) 0x034D), // 0003 with "AptX" (any mode) or 0002 with "Stable Connection" and "LDAC" can also be 0000, send if second device connects
REQUEST_RESPONSE_0350((short) 0x0350), // 0001 Pro 4 not sure what this is, send, if second device connects
SET_EQUALIZER_BAND((short) 0x0E01, OTHER_VENDOR_ID), // answers with UNIDENTIFIED_0F81
UNIDENTIFIED_0E80((short) 0x0E80, OTHER_VENDOR_ID), // 01
RESPONSE_EQUALIZER_BAND((short) 0x0F81, OTHER_VENDOR_ID),
@@ -21,25 +21,38 @@ public class EarFunPacketEncoder {
// Gaia uses a factor of 60 to convert to dB and EarFun projects 6 dBs on a slider scale of 10
private static final double EQUALIZER_GAIN_FACTOR = 60 * 0.6;
public static byte[] encodeBatteryReq() {
public static byte[] encodeCommonSettingsReq() {
return joinPackets(
// battery levels
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_BATTERY_STATE_LEFT).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_BATTERY_STATE_RIGHT).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_BATTERY_STATE_CASE).encode()
);
}
public static byte[] encodeSoundReq() {
return joinPackets(
// sound settings
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_GAME_MODE).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_AMBIENT_SOUND).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_ANC_MODE).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_TRANSPARENCY_MODE).encode()
// touch settings
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_TOUCH_ACTION).encode()
);
}
public static byte[] encodeTouchActionReq() {
return joinPackets(new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_TOUCH_ACTION).encode());
public static byte[] encodeAirPro4SettingsReq() {
return joinPackets(
encodeCommonSettingsReq(),
// battery levels
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_BATTERY_STATE_CASE).encode(),
// sound settings
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_ANC_MODE).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_TRANSPARENCY_MODE).encode(),
// touch settings
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_TOUCH_MODE).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_DISABLE_IN_EAR_DETECTION).encode(),
// system settings
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_CONNECT_TWO_DEVICES).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_ADVANCED_AUDIO_MODE).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_MICROPHONE_MODE).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_FIND_DEVICE).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_VOICE_PROMPT_VOLUME).encode(),
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_AUDIO_CODEC).encode()
);
}
public static byte[] encodeFirmwareVersionReq() {
@@ -66,6 +66,30 @@ public class EarFunProtocol extends GBDeviceProtocol {
// do nothing with these, they are returned after each EQ set operation and always return 01
case RESPONSE_EQUALIZER_BAND:
break;
case REQUEST_RESPONSE_DISABLE_IN_EAR_DETECTION:
events.add(handleInEarDetectionModeInfo(payload));
break;
case REQUEST_RESPONSE_TOUCH_MODE:
events.add(handleTouchModeInfo(payload));
break;
case REQUEST_RESPONSE_CONNECT_TWO_DEVICES:
events.add(handleConnectTwoDevicesModeInfo(payload));
break;
case REQUEST_RESPONSE_ADVANCED_AUDIO_MODE:
events.add(handleAdvancedAudioModeInfo(payload));
break;
case REQUEST_RESPONSE_MICROPHONE_MODE:
events.add(handleMicrophoneModeInfo(payload));
break;
case REQUEST_RESPONSE_FIND_DEVICE:
events.add(handleFindDeviceInfo(payload));
break;
case REQUEST_RESPONSE_VOICE_PROMPT_VOLUME:
events.add(handleVoicePromptVolumeInfo(payload));
break;
case REQUEST_RESPONSE_AUDIO_CODEC:
events.add(handleAudioCodecInfo(payload));
break;
default:
LOG.error("no handler for packet type {}", packet.getCommand().name());
}
@@ -73,26 +97,6 @@ public class EarFunProtocol extends GBDeviceProtocol {
return events.toArray(new GBDeviceEvent[0]);
}
@Override
public byte[] encodeTestNewFunction() {
return joinPackets(
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_0318).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0321).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0326).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_032C).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_032F).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0331).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0333).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0335).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0339).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_034A).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_034C).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_034D).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0348).encode(),
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0350).encode()
);
}
@Override
public byte[] encodeSendConfiguration(String config) {
Prefs prefs = getDevicePrefs();
@@ -129,22 +133,45 @@ public class EarFunProtocol extends GBDeviceProtocol {
case PREF_EARFUN_TRANSPARENCY_MODE:
byte transparencyMode = (byte) (Integer.parseInt(prefs.getString(PREF_EARFUN_TRANSPARENCY_MODE, "0")) & 0xFF);
return new EarFunPacket(EarFunPacket.Command.SET_TRANSPARENCY_MODE, transparencyMode).encode();
case PREF_EARFUN_IN_EAR_DETECTION_MODE:
int disableInEarDetectionMode = prefs.getBoolean(PREF_EARFUN_IN_EAR_DETECTION_MODE, false) ? 0 : 1;
return new EarFunPacket(EarFunPacket.Command.SET_DISABLE_IN_EAR_DETECTION, (byte) disableInEarDetectionMode).encode();
case PREF_EARFUN_TOUCH_MODE:
// 0 = both, 1 = none
int touchMode = prefs.getBoolean(PREF_EARFUN_TOUCH_MODE, false) ? 0 : 1;
return new EarFunPacket(EarFunPacket.Command.SET_TOUCH_MODE, (byte) touchMode).encode();
case PREF_EARFUN_CONNECT_TWO_DEVICES_MODE:
int connectTwoDevicesMode = prefs.getBoolean(PREF_EARFUN_CONNECT_TWO_DEVICES_MODE, false) ? 1 : 0;
return joinPackets(
new EarFunPacket(EarFunPacket.Command.SET_CONNECT_TWO_DEVICES, (byte) connectTwoDevicesMode).encode(),
new EarFunPacket(EarFunPacket.Command.COMMAND_REBOOT).encode()
);
case PREF_EARFUN_ADVANCED_AUDIO_MODE:
byte advancedAudioMode = (byte) (Integer.parseInt(prefs.getString(PREF_EARFUN_ADVANCED_AUDIO_MODE, "0")) & 0xFF);
return new EarFunPacket(EarFunPacket.Command.SET_ADVANCED_AUDIO_MODE, advancedAudioMode).encode();
case PREF_EARFUN_MICROPHONE_MODE:
byte microphoneMode = (byte) (Integer.parseInt(prefs.getString(PREF_EARFUN_MICROPHONE_MODE, "0")) & 0xFF);
return new EarFunPacket(EarFunPacket.Command.SET_MICROPHONE_MODE, microphoneMode).encode();
case PREF_EARFUN_FIND_DEVICE:
byte findDeviceMode = (byte) (Integer.parseInt(prefs.getString(PREF_EARFUN_FIND_DEVICE, "0")) & 0xFF);
return new EarFunPacket(EarFunPacket.Command.SET_FIND_DEVICE, findDeviceMode).encode();
case PREF_EARFUN_VOICE_PROMPT_VOLUME:
// the volume has a scale from 4 to 0, where 0 is the highest volume and 4 is off,
// to make it nicer for a slider, we reverse it
int voicePromptVolume = prefs.getInt(PREF_EARFUN_VOICE_PROMPT_VOLUME, 0);
voicePromptVolume = Math.max(0, 4 - voicePromptVolume);
return new EarFunPacket(EarFunPacket.Command.SET_VOICE_PROMPT_VOLUME, (byte) voicePromptVolume).encode();
case PREF_EARFUN_AUDIO_CODEC:
byte audioCodec = (byte) (Integer.parseInt(prefs.getString(PREF_EARFUN_AUDIO_CODEC, "0")) & 0xFF);
return new EarFunPacket(EarFunPacket.Command.SET_AUDIO_CODEC, audioCodec).encode();
default:
LOG.error("unhandled send configuration {}", config);
}
return null;
}
public byte[] encodeBatteryReq() {
return EarFunPacketEncoder.encodeBatteryReq();
}
public byte[] encodeSoundReq() {
return EarFunPacketEncoder.encodeSoundReq();
}
public byte[] encodeTouchActionReq() {
return EarFunPacketEncoder.encodeTouchActionReq();
public byte[] encodeSettingsReq() {
return EarFunPacketEncoder.encodeCommonSettingsReq();
}
@Override
@@ -2,8 +2,16 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_AMBIENT_SOUND_CONTROL;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_ANC_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_AUDIO_CODEC;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_CONNECT_TWO_DEVICES_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_GAME_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_ADVANCED_AUDIO_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_IN_EAR_DETECTION_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_MICROPHONE_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_FIND_DEVICE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_TOUCH_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_TRANSPARENCY_MODE;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_VOICE_PROMPT_VOLUME;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Interactions.interactionPrefs;
import java.nio.ByteBuffer;
@@ -47,6 +55,34 @@ public class EarFunResponseHandler {
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_GAME_MODE, gameMode);
}
public static GBDeviceEvent handleInEarDetectionModeInfo(byte[] payload) {
// "0" means in ear detection is enabled
boolean inEarDetectionMode = payload[1] == 0x00;
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_IN_EAR_DETECTION_MODE, inEarDetectionMode);
}
public static GBDeviceEvent handleTouchModeInfo(byte[] payload) {
// "0" means touch is enabled
boolean touchMode = payload[1] == 0x00;
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_TOUCH_MODE, touchMode);
}
public static GBDeviceEvent handleConnectTwoDevicesModeInfo(byte[] payload) {
boolean touchMode = payload[1] == 0x01;
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_CONNECT_TWO_DEVICES_MODE, touchMode);
}
public static GBDeviceEvent handleAdvancedAudioModeInfo(byte[] payload) {
// 00 = Google Fast Pair, 01 = LE Audio
String advancedAudioMode = Integer.toString(payload[1]);
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_ADVANCED_AUDIO_MODE, advancedAudioMode);
}
public static GBDeviceEvent handleAudioCodecInfo(byte[] payload) {
String audioCodec = Integer.toString(payload[1]);
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_AUDIO_CODEC, audioCodec);
}
public static GBDeviceEvent handleAmbientSoundInfo(byte[] payload) {
String ambientSoundMode = Integer.toString(payload[1]);
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_AMBIENT_SOUND_CONTROL, ambientSoundMode);
@@ -62,6 +98,24 @@ public class EarFunResponseHandler {
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_TRANSPARENCY_MODE, transparencyMode);
}
public static GBDeviceEvent handleMicrophoneModeInfo(byte[] payload) {
String microphoneMode = Integer.toString(payload[1]);
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_MICROPHONE_MODE, microphoneMode);
}
public static GBDeviceEvent handleFindDeviceInfo(byte[] payload) {
String findDeviceMode = Integer.toString(payload[1]);
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_FIND_DEVICE, findDeviceMode);
}
public static GBDeviceEvent handleVoicePromptVolumeInfo(byte[] payload) {
// the volume has a scale from 4 to 0, where 0 is the highest volume and 4 is off,
// to make it nicer for a slider, we reverse it
int voicePromptVolume = payload[1];
voicePromptVolume = Math.max(0, 4 - voicePromptVolume);
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_VOICE_PROMPT_VOLUME, voicePromptVolume);
}
public static GBDeviceEvent handleTouchActionInfo(byte[] payload) {
GBDeviceEventUpdatePreferences updateEvent = new GBDeviceEventUpdatePreferences();
ByteBuffer buf = ByteBuffer.wrap(payload);
@@ -8,10 +8,12 @@ import android.os.Parcel;
import android.text.InputFilter;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.SeekBarPreference;
import androidx.preference.SwitchPreferenceCompat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,7 +60,7 @@ public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomize
@Override
public void customizeSettings(DeviceSpecificSettingsHandler handler, Prefs prefs, String rootKey) {
handler.addPreferenceHandlerFor(PREF_EARFUN_DEVICE_NAME);
addConfirmablePreferenceHandlerFor(handler, PREF_EARFUN_DEVICE_NAME, R.string.prefs_device_name, R.string.earfun_change_device_name_confirm_message);
handler.addPreferenceHandlerFor(PREF_EARFUN_AMBIENT_SOUND_CONTROL);
handler.addPreferenceHandlerFor(PREF_EARFUN_TRANSPARENCY_MODE);
handler.addPreferenceHandlerFor(PREF_EARFUN_ANC_MODE);
@@ -83,6 +85,14 @@ public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomize
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_8000);
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_15000);
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_16000);
handler.addPreferenceHandlerFor(PREF_EARFUN_IN_EAR_DETECTION_MODE);
handler.addPreferenceHandlerFor(PREF_EARFUN_TOUCH_MODE);
addConfirmablePreferenceHandlerFor(handler, PREF_EARFUN_CONNECT_TWO_DEVICES_MODE, R.string.earfun_connect_two_devices, R.string.earfun_reboot_confirm_message);
addConfirmablePreferenceHandlerFor(handler, PREF_EARFUN_ADVANCED_AUDIO_MODE, R.string.earfun_advanced_audio_mode, R.string.earfun_reboot_confirm_message);
addConfirmablePreferenceHandlerFor(handler, PREF_EARFUN_MICROPHONE_MODE, R.string.earfun_microphone_mode, R.string.earfun_reboot_confirm_message);
handler.addPreferenceHandlerFor(PREF_EARFUN_FIND_DEVICE);
handler.addPreferenceHandlerFor(PREF_EARFUN_VOICE_PROMPT_VOLUME);
addConfirmablePreferenceHandlerFor(handler, PREF_EARFUN_AUDIO_CODEC, R.string.audio_codec, R.string.earfun_reboot_confirm_message);
EditTextPreference editTextDeviceName = handler.findPreference(PREF_EARFUN_DEVICE_NAME);
if (editTextDeviceName != null) {
@@ -93,6 +103,42 @@ public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomize
}
}
protected void addConfirmablePreferenceHandlerFor(DeviceSpecificSettingsHandler handler, final String preferenceKey, int alertPreferenceNAme, int alertMessage) {
Preference pref = handler.findPreference(preferenceKey);
Context context = handler.getContext();
if (pref != null) {
pref.setOnPreferenceChangeListener((preference, newValue) -> {
String preferenceName = context.getString(alertPreferenceNAme);
String title = context.getString(R.string.earfun_change_confirm_title, preferenceName);
String message = context.getString(alertMessage);
new AlertDialog.Builder(context)
.setTitle(title)
.setMessage(message)
.setPositiveButton(handler.getContext().getString(R.string.ok), (dialog, which) -> {
if (pref instanceof EditTextPreference) {
((EditTextPreference) pref).setText(newValue.toString());
}
if (pref instanceof ListPreference) {
((ListPreference) pref).setValue(newValue.toString());
}
if (pref instanceof SwitchPreferenceCompat) {
((SwitchPreferenceCompat) pref).setChecked((Boolean) newValue);
} else {
LOG.error("unsupported preference type for confirmable handler: {}", pref);
}
handler.notifyPreferenceChanged(preferenceKey);
})
.setNegativeButton(handler.getContext().getString(R.string.Cancel), (dialog, which) -> {
dialog.dismiss();
})
.create()
.show();
return false;
});
}
}
protected void initializeEqualizerPresetListPreference(DeviceSpecificSettingsHandler handler,
Equalizer.EqualizerPreset[] equalizerPresets) {
ListPreference equalizerPresetListPreference = handler.findPreference(PREF_EARFUN_EQUALIZER_PRESET);
@@ -1,5 +1,7 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunPacketEncoder.joinPackets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,6 +16,13 @@ public class EarFunAirPro4Protocol extends EarFunProtocol {
private static final Logger LOG = LoggerFactory.getLogger(EarFunAirPro4Protocol.class);
@Override
public byte[] encodeTestNewFunction() {
return joinPackets(
// new EarFunPacket(EarFunPacket.Command.COMMAND_REBOOT).encode()
);
}
@Override
public byte[] encodeSendConfiguration(String config) {
if (Equalizer.containsKey(Equalizer.TenBandEqualizer, config)) {
@@ -23,6 +32,11 @@ public class EarFunAirPro4Protocol extends EarFunProtocol {
return super.encodeSendConfiguration(config);
}
@Override
public byte[] encodeSettingsReq() {
return EarFunPacketEncoder.encodeAirPro4SettingsReq();
}
protected EarFunAirPro4Protocol(GBDevice device) {
super(device);
DeviceType type = device.getType();
@@ -27,4 +27,12 @@ public class EarFunSettingsPreferenceConst {
public static final String PREF_EARFUN_EQUALIZER_BAND_15000 = "pref_earfun_equalizer_band_15000";
public static final String PREF_EARFUN_EQUALIZER_BAND_16000 = "pref_earfun_equalizer_band_16000";
public static final String PREF_EARFUN_EQUALIZER_PRESET = "pref_earfun_equalizer_preset";
public static final String PREF_EARFUN_IN_EAR_DETECTION_MODE = "pref_earfun_in_ear_detection_mode";
public static final String PREF_EARFUN_TOUCH_MODE = "pref_earfun_touch_mode";
public static final String PREF_EARFUN_CONNECT_TWO_DEVICES_MODE = "pref_earfun_connect_two_devices";
public static final String PREF_EARFUN_ADVANCED_AUDIO_MODE = "pref_earfun_advanced_audio_mode";
public static final String PREF_EARFUN_MICROPHONE_MODE = "pref_earfun_microphone_mode";
public static final String PREF_EARFUN_FIND_DEVICE = "pref_earfun_find_device";
public static final String PREF_EARFUN_VOICE_PROMPT_VOLUME = "pref_earfun_voice_prompt_volume";
public static final String PREF_EARFUN_AUDIO_CODEC = "pref_earfun_audio_codec";
}
@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#7E7E7E">
<path
android:fillColor="@android:color/white"
android:strokeWidth="1.2"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="M12,15c2,0 3.5,-1.5 3.5,-3.5V6.5c0,-2 -1.5,-3.5 -3.5,-3.5s-3.5,1.5 -3.5,3.5v5c0,2 1.5,3.5 3.5,3.5zM12,16.5c-3,0 -5.5,-2.5 -5.5,-5.5H5c0,3.5 2.6,6.5 6.1,6.9V20h-3v2h8v-2h-3v-2.1c3.5,-0.4 6.1,-3.4 6.1,-6.9h-1.5c0,3 -2.5,5.5 -5.5,5.5z"/>
</vector>
+52
View File
@@ -3666,6 +3666,58 @@
<item>1</item>
</string-array>
<string-array name="earfun_microphone_mode_names">
<item>@string/earfun_earbud_mode_automatic</item>
<item>@string/earfun_earbud_mode_left</item>
<item>@string/earfun_earbud_mode_right</item>
</string-array>
<string-array name="earfun_microphone_mode_values">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="earfun_audio_codec_names">
<item>@string/pref_title_stable_connection</item>
<item>aptX</item>
<item>aptX Adaptive</item>
<item>aptX Adaptive Lossless</item>
<item>LDAC</item>
</string-array>
<string-array name="earfun_audio_codec_values">
<item>0</item>
<item>1</item>
<item>3</item>
<item>19</item>
<item>8</item>
</string-array>
<string-array name="earfun_advanced_audio_mode_names">
<item>Google Fast Pair</item>
<item>LE Audio</item>
</string-array>
<string-array name="earfun_advanced_audio_mode_values">
<item>0</item>
<item>1</item>
</string-array>
<string-array name="earfun_find_device_names">
<item>@string/off</item>
<item>@string/earfun_earbud_mode_left</item>
<item>@string/earfun_earbud_mode_right</item>
<item>@string/earfun_earbud_mode_both</item>
</string-array>
<string-array name="earfun_find_device_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string-array name="earfun_air_s_tap_action_names">
<item>@string/none</item>
<item>@string/pref_media_volumeup</item>
+24
View File
@@ -444,6 +444,7 @@
<string name="pref_display_add_device_fab_off">Visible only if no device is added</string>
<string name="pref_title_huami_force_new_protocol">New Auth Protocol</string>
<string name="pref_summary_huami_force_new_protocol">Enable if your device no longer connects after a firmware upgrade</string>
<string name="pref_title_stable_connection">Stable Connection</string>
<!-- HPlus Preferences -->
<string name="pref_title_unit_system">Units</string>
<string name="pref_title_timeformat">Time format</string>
@@ -3748,6 +3749,29 @@
<string name="earfun_tap_action_noise_control_normal_ambient">Noise Control (Normal, Ambient Sound)</string>
<string name="earfun_tap_action_noise_control_wind_ambient">Noise Control (Wind Noise, Ambient Sound)</string>
<string name="earfun_tap_action_redial">Redial</string>
<string name="earfun_change_device_name_confirm_message">1) Once you\'ve updated the name, place the earbuds in the charging case, close the lid, and wait for about 10 seconds.\n\n2) In your device settings, remove the original device.\n\n3) Open the charging case again, search for the device, and the name change should be applied successfully.</string>
<string name="earfun_change_confirm_title">Change %s?</string>
<string name="earfun_reboot_confirm_message">After applying this setting, your device will restart. Once it powers back on, navigate to the \'Devices\' screen and reconnect.</string>
<string name="earfun_low_latency_mode">Low Latency Mode</string>
<string name="earfun_low_latency_mode_hint">Low latency mode reduces audio lag by utilizing a smaller buffer, though this can sometimes result in a less stable connection.</string>
<string name="earfun_connect_two_devices">Dual device connection</string>
<string name="earfun_connect_two_devices_hint">Connect to two Bluetooth devices simultaneously. For a stable signal, avoid using Dual Device Connection and LDAP mode at the same time.</string>
<string name="earfun_advanced_audio_mode">Advanced Audio Mode</string>
<string name="earfun_advanced_audio_mode_hint"><b>Google Fast Pair</b> offers instant and seamless device pairing.\n<b>LE Audio</b> provides improved sound quality, lower power consumption, and support for multiple audio streams.</string>
<string name="earfun_microphone_mode">Microphone Mode</string>
<string name="earfun_microphone_mode_hint">When using the headphone microphone, it can either automatically switch between the left and right earbud or remain fixed on one side based on your preference.</string>
<string name="earfun_earbud_mode_automatic">Choose best earbud automatically</string>
<string name="earfun_earbud_mode_right">Right earbud only</string>
<string name="earfun_earbud_mode_left">Left earbud only</string>
<string name="earfun_earbud_mode_both">Both earbuds</string>
<string name="earfun_find_headphones">Find Headphones</string>
<string name="earfun_find_headphones_hint">This option plays a loud sound through the headphones to help locate them. Avoid wearing the headphones to prevent potential hearing damage.</string>
<string name="earfun_in_ear_detection_hint">When enabled, In-Ear detection automatically resumes audio when the earbuds are worn and pauses playback when they are removed.</string>
<string name="earfun_audio_codec_hint"><b>LDAC</b> is a high-resolution codec for superior sound quality.\n<b>aptX</b> ensures low latency and high-fidelity audio streaming.\n<b>aptX Adaptive</b> adjusts bitrate dynamically based on content and connection.\n<b>aptX Adaptive Lossless</b> delivers lossless audio for high-resolution music.\n<b>Stable Connection</b> minimizes sound stuttering or skipping.</string>
<string name="earfun_voice_prompt_volume">Voice Prompt Volume</string>
<string name="earfun_header_audio_experience">Audio Experience</string>
<string name="earfun_header_connectivity">Audio Quality &amp; Connectivity</string>
<string name="earfun_header_system_settings">System Settings</string>
<string name="ultrahuman_airplane_mode_activated">airplane mode activated</string>
<string name="ultrahuman_airplane_mode_on_charger">ring is charging - airplane mode NOT activated</string>
<string name="ultrahuman_airplane_mode_too_full">ring is fully charged - airplane mode NOT activated</string>
@@ -7,11 +7,13 @@
android:key="pref_earfun_10_band_equalizer"
android:persistent="false"
android:title="@string/pref_header_equalizer">
<ListPreference
android:icon="@drawable/ic_graphic_eq"
android:icon="@drawable/ic_equalizer"
android:key="pref_earfun_equalizer_preset"
android:summary="%s"
android:title="@string/prefs_equalizer_preset" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -20,6 +22,7 @@
android:title="31.5 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -28,6 +31,7 @@
android:title="63 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -36,6 +40,7 @@
android:title="125 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -44,6 +49,7 @@
android:title="250 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -52,6 +58,7 @@
android:title="500 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -60,6 +67,7 @@
android:title="1 kHz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -68,6 +76,7 @@
android:title="2 kHz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -76,6 +85,7 @@
android:title="4 kHz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -84,6 +94,7 @@
android:title="8 kHz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -92,6 +103,7 @@
android:title="16 kHz"
app:min="-10"
app:showSeekBarValue="true" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>
@@ -7,11 +7,13 @@
android:key="pref_earfun_6_band_equalizer"
android:persistent="false"
android:title="@string/pref_header_equalizer">
<ListPreference
android:icon="@drawable/ic_graphic_eq"
android:icon="@drawable/ic_equalizer"
android:key="pref_earfun_equalizer_preset"
android:summary="%s"
android:title="@string/prefs_equalizer_preset" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -20,6 +22,7 @@
android:title="63 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -28,6 +31,7 @@
android:title="180 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -36,6 +40,7 @@
android:title="500 Hz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -44,6 +49,7 @@
android:title="1 kHz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -52,6 +58,7 @@
android:title="8 kHz"
app:min="-10"
app:showSeekBarValue="true" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_graphic_eq"
@@ -60,6 +67,7 @@
android:title="15 kHz"
app:min="-10"
app:showSeekBarValue="true" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_videogame"
android:key="pref_earfun_game_mode"
android:layout="@layout/preference_checkbox"
android:summary="@string/earfun_low_latency_mode_hint"
android:title="@string/earfun_low_latency_mode" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_bluetooth_searching"
android:key="pref_earfun_connect_two_devices"
android:layout="@layout/preference_checkbox"
android:summary="@string/earfun_connect_two_devices_hint"
android:title="@string/earfun_connect_two_devices" />
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_audio_codec_names"
android:entryValues="@array/earfun_audio_codec_values"
android:icon="@drawable/ic_equalizer"
android:key="pref_earfun_audio_codec"
android:summary="%s"
android:title="@string/audio_codec" />
<Preference
android:key="pref_earfun_audio_codec_hint"
android:selectable="false"
android:summary="@string/earfun_audio_codec_hint" />
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_advanced_audio_mode_names"
android:entryValues="@array/earfun_advanced_audio_mode_values"
android:icon="@drawable/ic_bluetooth"
android:key="pref_earfun_advanced_audio_mode"
android:summary="%s"
android:title="@string/earfun_advanced_audio_mode" />
<Preference
android:key="pref_earfun_advanced_audio_mode_hint"
android:selectable="false"
android:summary="@string/earfun_advanced_audio_mode_hint" />
</androidx.preference.PreferenceScreen>
@@ -1,87 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_earfun_air_pro_4_system"
android:title="@string/pref_header_system">
<PreferenceScreen
android:icon="@drawable/ic_touch"
android:key="pref_screen_earfun_touch_options"
android:persistent="false"
android:title="@string/prefs_galaxy_touch_options">
<PreferenceScreen
android:icon="@drawable/ic_touch"
android:key="pref_screen_earfun_touch_options"
android:persistent="false"
android:title="@string/prefs_galaxy_touch_options">
<PreferenceCategory android:title="@string/single_tap">
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_filter_1"
android:defaultValue="16"
android:key="pref_earfun_single_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_single_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_touch_mode"
android:layout="@layout/preference_checkbox"
android:title="@string/prefs_touch_lock_buds2" />
<PreferenceCategory android:title="@string/double_tap">
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_filter_2"
android:defaultValue="16"
android:key="pref_earfun_double_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_double_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/single_tap">
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_filter_1"
android:key="pref_earfun_single_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:key="pref_earfun_single_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/triple_tap">
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_filter_3"
android:defaultValue="16"
android:key="pref_earfun_tripple_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_tripple_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/double_tap">
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_filter_2"
android:key="pref_earfun_double_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:key="pref_earfun_double_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/long_press">
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_touch"
android:defaultValue="16"
android:key="pref_earfun_long_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_long_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
</PreferenceScreen>
<PreferenceCategory android:title="@string/triple_tap">
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_filter_3"
android:key="pref_earfun_tripple_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:key="pref_earfun_tripple_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/long_press">
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:icon="@drawable/ic_touch"
android:key="pref_earfun_long_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:defaultValue="16"
android:dependency="pref_earfun_touch_mode"
android:entries="@array/earfun_air_pro_4_tap_action_names"
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
android:key="pref_earfun_long_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_earfun_air_pro_4_ambient_sound_control"
android:title="@string/pref_header_sony_sound_control">
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_ambient_sound_control_names"
android:entryValues="@array/earfun_ambient_sound_control_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_ambient_sound_control"
android:summary="%s"
android:title="@string/pref_header_sony_ambient_sound_control" />
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_transparency_mode_names"
android:entryValues="@array/earfun_transparency_mode_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_transparency_mode"
android:summary="%s"
android:title="@string/earfun_transparency_mode" />
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_anc_mode_names"
android:entryValues="@array/earfun_anc_mode_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_anc_mode"
android:summary="%s"
android:title="@string/earfun_anc_mode" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_videogame"
android:key="pref_earfun_game_mode"
android:layout="@layout/preference_checkbox"
android:summary="@string/prefs_game_mode_summary"
android:title="@string/prefs_game_mode" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,54 @@
<?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">
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_ambient_sound_control_names"
android:entryValues="@array/earfun_ambient_sound_control_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_ambient_sound_control"
android:summary="%s"
android:title="@string/pref_header_sony_ambient_sound_control" />
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_transparency_mode_names"
android:entryValues="@array/earfun_transparency_mode_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_transparency_mode"
android:summary="%s"
android:title="@string/earfun_transparency_mode" />
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_anc_mode_names"
android:entryValues="@array/earfun_anc_mode_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_anc_mode"
android:summary="%s"
android:title="@string/earfun_anc_mode" />
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_microphone_mode_names"
android:entryValues="@array/earfun_microphone_mode_values"
android:icon="@drawable/ic_microphone"
android:key="pref_earfun_microphone_mode"
android:summary="%s"
android:title="@string/earfun_microphone_mode" />
<Preference
android:key="pref_earfun_microphone_mode_hint"
android:selectable="false"
android:summary="@string/earfun_microphone_mode_hint" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_volume_up"
android:key="pref_earfun_voice_prompt_volume"
android:max="4"
android:title="@string/earfun_voice_prompt_volume"
app:min="0"
app:showSeekBarValue="true" />
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_videogame"
android:key="pref_earfun_game_mode"
android:layout="@layout/preference_checkbox"
android:summary="@string/earfun_low_latency_mode_hint"
android:title="@string/earfun_low_latency_mode" />
</androidx.preference.PreferenceScreen>
@@ -1,87 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_earfun_air_s_system"
android:title="@string/pref_header_system">
<PreferenceScreen
android:icon="@drawable/ic_touch"
android:key="pref_screen_earfun_touch_options"
android:persistent="false"
android:title="@string/prefs_galaxy_touch_options">
<PreferenceScreen
android:icon="@drawable/ic_touch"
android:key="pref_screen_earfun_touch_options"
android:persistent="false"
android:title="@string/prefs_galaxy_touch_options">
<PreferenceCategory android:title="@string/single_tap">
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_filter_1"
android:defaultValue="16"
android:key="pref_earfun_single_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_single_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/single_tap">
<PreferenceCategory android:title="@string/double_tap">
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_filter_2"
android:defaultValue="16"
android:key="pref_earfun_double_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_double_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_filter_1"
android:key="pref_earfun_single_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<PreferenceCategory android:title="@string/triple_tap">
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_filter_3"
android:defaultValue="16"
android:key="pref_earfun_tripple_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_tripple_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:key="pref_earfun_single_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
<PreferenceCategory android:title="@string/long_press">
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_touch"
android:defaultValue="16"
android:key="pref_earfun_long_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:defaultValue="16"
android:key="pref_earfun_long_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory android:title="@string/double_tap">
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_filter_2"
android:key="pref_earfun_double_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:key="pref_earfun_double_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/triple_tap">
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_filter_3"
android:key="pref_earfun_tripple_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:key="pref_earfun_tripple_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/long_press">
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:icon="@drawable/ic_touch"
android:key="pref_earfun_long_tap_left_action"
android:summary="%s"
android:title="@string/prefs_left" />
<ListPreference
android:defaultValue="16"
android:entries="@array/earfun_air_s_tap_action_names"
android:entryValues="@array/earfun_air_s_tap_action_values"
android:key="pref_earfun_long_tap_right_action"
android:summary="%s"
android:title="@string/prefs_right" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_earfun_air_s_ambient_sound_control"
android:title="@string/pref_header_sony_sound_control">
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_ambient_sound_control_names"
android:entryValues="@array/earfun_ambient_sound_control_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_ambient_sound_control"
android:summary="%s"
android:title="@string/pref_header_sony_ambient_sound_control" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_videogame"
android:key="pref_earfun_game_mode"
android:layout="@layout/preference_checkbox"
android:summary="@string/prefs_game_mode_summary"
android:title="@string/prefs_game_mode" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_ambient_sound_control_names"
android:entryValues="@array/earfun_ambient_sound_control_values"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_ambient_sound_control"
android:summary="%s"
android:title="@string/pref_header_sony_ambient_sound_control" />
</androidx.preference.PreferenceScreen>
@@ -1,10 +1,12 @@
<?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">
<EditTextPreference
android:icon="@drawable/ic_bluetooth"
android:inputType="text"
android:key="pref_earfun_device_name"
android:title="@string/prefs_device_name"
app:useSimpleSummaryProvider="true" />
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue="0"
android:entries="@array/earfun_find_device_names"
android:entryValues="@array/earfun_find_device_values"
android:icon="@drawable/ic_action_find_lost_device"
android:key="pref_earfun_find_device"
android:summary="%s"
android:title="@string/earfun_find_headphones" />
<Preference
android:key="pref_earfun_find_device_hint"
android:selectable="false"
android:summary="@string/earfun_find_headphones_hint" />
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_earfun_audio_experience"
android:title="@string/earfun_header_audio_experience" />
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_earfun_connectivity"
android:title="@string/earfun_header_connectivity" />
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_earfun_system_settings"
android:title="@string/earfun_header_system_settings" />
</androidx.preference.PreferenceScreen>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_hearing"
android:key="pref_earfun_in_ear_detection_mode"
android:layout="@layout/preference_checkbox"
android:summary="@string/earfun_in_ear_detection_hint"
android:title="@string/nothing_prefs_inear_title" />
</androidx.preference.PreferenceScreen>
@@ -56,30 +56,6 @@ public class EarFunPacketEncoderTest {
assertEquals(170, result.length);
}
@Test
public void testEncodeBatteryReq() {
byte[] encoded = EarFunPacketEncoder.encodeBatteryReq();
assertNotNull(encoded);
assertEquals(24, encoded.length);
}
@Test
public void testEncodeSoundReq() {
byte[] encoded = EarFunPacketEncoder.encodeSoundReq();
assertNotNull(encoded);
assertEquals(32, encoded.length);
}
@Test
public void testEncodeTouchActionReq() {
byte[] encoded = EarFunPacketEncoder.encodeTouchActionReq();
assertNotNull(encoded);
assertEquals(8, encoded.length);
}
@Test
public void testEncodeFirmwareVersionReq() {
byte[] encoded = EarFunPacketEncoder.encodeFirmwareVersionReq();