diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java index f313dca26..5bf18d994 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java @@ -121,6 +121,10 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_SONY_AMBIENT_SOUND_CONTROL = "pref_sony_ambient_sound_control"; 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"; + public static final String PREF_SONY_NOISE_OPTIMIZER_CANCEL = "pref_sony_noise_optimizer_cancel"; + public static final String PREF_SONY_NOISE_OPTIMIZER_STATUS = "pref_sony_noise_optimizer_status"; + public static final String PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE = "pref_sony_noise_optimizer_state_pressure"; public static final String PREF_SONY_SOUND_POSITION = "pref_sony_sound_position"; public static final String PREF_SONY_SURROUND_MODE = "pref_sony_surround_mode"; public static final String PREF_SONY_EQUALIZER = "pref_sony_equalizer"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index e53d66586..ccdc0e024 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -195,6 +195,19 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp getListView().post(runnable); } + /* + * delayed execution so that the preferences are applied first + */ + @Override + public void notifyPreferenceChanged(final String preferenceKey) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(preferenceKey); + } + }); + } + private void setChangeListener() { final Prefs prefs = new Prefs(getPreferenceManager().getSharedPreferences()); String disconnectNotificationState = prefs.getString(PREF_DISCONNECT_NOTIFICATION, PREF_DO_NOT_DISTURB_OFF); @@ -206,12 +219,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp disconnectNotificationStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_START); - } - }); + notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION_START); return true; } }); @@ -223,12 +231,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp disconnectNotificationEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_END); - } - }); + notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION_END); return true; } }); @@ -243,12 +246,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp Objects.requireNonNull(disconnectNotificationStart).setEnabled(scheduled); Objects.requireNonNull(disconnectNotificationEnd).setEnabled(scheduled); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION); - } - }); + notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION); return true; } }); @@ -264,12 +262,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp nightModeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_START); - } - }); + notifyPreferenceChanged(PREF_NIGHT_MODE_START); return true; } }); @@ -281,12 +274,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp nightModeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_END); - } - }); + notifyPreferenceChanged(PREF_NIGHT_MODE_END); return true; } }); @@ -303,12 +291,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp Objects.requireNonNull(nightModeStart).setEnabled(scheduled); Objects.requireNonNull(nightModeEnd).setEnabled(scheduled); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE); - } - }); + notifyPreferenceChanged(PREF_NIGHT_MODE); return true; } }); @@ -324,12 +307,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp doNotDisturbStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_START); - } - }); + notifyPreferenceChanged(PREF_DO_NOT_DISTURB_START); return true; } }); @@ -341,12 +319,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp doNotDisturbEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_END); - } - }); + notifyPreferenceChanged(PREF_DO_NOT_DISTURB_END); return true; } }); @@ -362,12 +335,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp Objects.requireNonNull(doNotDisturbStart).setEnabled(scheduled); Objects.requireNonNull(doNotDisturbEnd).setEnabled(scheduled); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB); - } - }); + notifyPreferenceChanged(PREF_DO_NOT_DISTURB); return true; } }); @@ -495,12 +463,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp sleepTimeInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME); return true; } }); @@ -512,12 +475,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp sleepTimeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_START); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME_START); return true; } }); @@ -529,12 +487,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp sleepTimeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_END); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME_END); return true; } }); @@ -551,12 +504,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp if (sleepTimeInfo != null) { //sleepTimeInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString())); } - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME); return true; } }); @@ -570,12 +518,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp rotateWristCycleInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); - } - }); + notifyPreferenceChanged(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); return true; } }); @@ -587,12 +530,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp displayOnLiftStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_START); - } - }); + notifyPreferenceChanged(PREF_DISPLAY_ON_LIFT_START); return true; } }); @@ -604,12 +542,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp displayOnLiftEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_END); - } - }); + notifyPreferenceChanged(PREF_DISPLAY_ON_LIFT_END); return true; } }); @@ -626,12 +559,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp if (rotateWristCycleInfo != null) { rotateWristCycleInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString())); } - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT); - } - }); + notifyPreferenceChanged(PREF_ACTIVATE_DISPLAY_ON_LIFT); return true; } }); @@ -802,12 +730,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp if (pref != null) { pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(preferenceKey); - } - }); + notifyPreferenceChanged(preferenceKey); if (extraListener != null) { return extraListener.onPreferenceChange(preference, newVal); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java index cb2b6ef9c..47d4a4095 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java @@ -39,6 +39,13 @@ public interface DeviceSpecificSettingsHandler { */ void addPreferenceHandlerFor(final String preferenceKey); + /** + * Notify the device that a preference changed. + * + * @param preferenceKey the preference key. + */ + void notifyPreferenceChanged(final String preferenceKey); + /** * Adds a preference handler for a preference key. On change, this handler calls the provided extra listener, and then sends the preference to the device. * diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java index 8ff6e9731..3bb3148dc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java @@ -28,12 +28,21 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BASS; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_FOCUS_VOICE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_CANCEL; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_START; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATUS; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SURROUND_MODE; +import android.app.AlertDialog; +import android.content.Context; import android.os.Parcel; import androidx.preference.EditTextPreference; + +import android.app.ProgressDialog; +import android.content.DialogInterface; + import androidx.preference.ListPreference; import androidx.preference.Preference; @@ -41,13 +50,17 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus; public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsCustomizer { + private ProgressDialog ancOptimizerProgressDialog; + final GBDevice device; public SonyHeadphonesSettingsCustomizer(final GBDevice device) { @@ -80,6 +93,24 @@ public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC } } } + + // Handle ANC Optimizer status + if (preference.getKey().equals(PREF_SONY_NOISE_OPTIMIZER_STATUS)) { + final EditTextPreference optimizerStatusPreference = (EditTextPreference) preference; + final NoiseCancellingOptimizerStatus optimizerStatus = NoiseCancellingOptimizerStatus.valueOf(optimizerStatusPreference.getText().toUpperCase(Locale.ROOT)); + + if (ancOptimizerProgressDialog != null) { + switch (optimizerStatus) { + case FINISHED: + case NOT_RUNNING: + ancOptimizerProgressDialog.dismiss(); + ancOptimizerProgressDialog = null; + break; + default: + ancOptimizerProgressDialog.setMessage(optimizerStatus.i18n(preference.getContext())); + } + } + } } @Override @@ -104,6 +135,54 @@ public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC ambientSoundControlPrefListener.onPreferenceChange(ambientSoundControl, ambientSoundControl.getValue()); handler.addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_CONTROL, ambientSoundControlPrefListener); } + + // ANC Optimizer + + final Preference ancOptimizer = handler.findPreference("pref_sony_anc_optimizer"); + + if (ancOptimizer != null) { + ancOptimizer.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + if (ancOptimizerProgressDialog != null) { + // Already optimizing + return true; + } + + final Context context = preference.getContext(); + + new AlertDialog.Builder(context) + .setTitle(R.string.sony_anc_optimize_confirmation_title) + .setMessage(R.string.sony_anc_optimize_confirmation_description) + .setIcon(R.drawable.ic_hearing) + .setPositiveButton(R.string.start, new DialogInterface.OnClickListener() { + public void onClick(final DialogInterface dialog, final int whichButton) { + handler.notifyPreferenceChanged(PREF_SONY_NOISE_OPTIMIZER_START); + + ancOptimizerProgressDialog = new ProgressDialog(context); + ancOptimizerProgressDialog.setCancelable(false); + ancOptimizerProgressDialog.setMessage(context.getString(R.string.sony_anc_optimizer_status_starting)); + ancOptimizerProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + ancOptimizerProgressDialog.setProgress(0); + ancOptimizerProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.Cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int which) { + dialog.dismiss(); + ancOptimizerProgressDialog = null; + handler.notifyPreferenceChanged(PREF_SONY_NOISE_OPTIMIZER_CANCEL); + } + }); + + ancOptimizerProgressDialog.show(); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + + return true; + } + }); + } } public static final Creator CREATOR = new Creator() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java index f61ebf2d8..16a9ea508 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java @@ -44,6 +44,7 @@ public class SonyWH1000XM3Coordinator extends SonyHeadphonesCoordinator { public int[] getSupportedDeviceSpecificSettings(final GBDevice device) { return new int[]{ R.xml.devicesettings_sony_headphones_ambient_sound_control_wind_noise_reduction, + R.xml.devicesettings_sony_headphones_anc_optimizer, R.xml.devicesettings_header_other, R.xml.devicesettings_sony_warning_wh1000xm3, R.xml.devicesettings_sony_headphones_equalizer, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java index d6a6a613d..fac10a307 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java @@ -45,6 +45,7 @@ public class SonyWH1000XM4Coordinator extends SonyHeadphonesCoordinator { return new int[]{ // TODO: Function of [CUSTOM] button R.xml.devicesettings_sony_headphones_ambient_sound_control_wind_noise_reduction, + R.xml.devicesettings_sony_headphones_anc_optimizer, R.xml.devicesettings_header_other, R.xml.devicesettings_sony_headphones_equalizer, R.xml.devicesettings_sony_headphones_audio_upsampling, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java index 8c28ca291..4e19a517a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java @@ -152,6 +152,12 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { case DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL: configRequest = protocolImpl.setAmbientSoundControl(AmbientSoundControl.fromPreferences(prefs)); break; + case DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_START: + configRequest = protocolImpl.startNoiseCancellingOptimizer(true); + break; + case DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_CANCEL: + configRequest = protocolImpl.startNoiseCancellingOptimizer(false); + break; case DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION: configRequest = protocolImpl.setSoundPosition(SoundPosition.fromPreferences(prefs)); break; @@ -211,10 +217,6 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { public byte[] encodeTestNewFunction() { //return Request.fromHex(MessageType.COMMAND_1, "c40100").encode(sequenceNumber); - if (protocolImpl != null) { - return protocolImpl.startNoiseCancellingOptimizer().encode(sequenceNumber); - } - return null; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java index 3af5befc4..a1be9a21e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java @@ -50,6 +50,8 @@ public abstract class AbstractSonyProtocolImpl { public abstract Request setAmbientSoundControl(final AmbientSoundControl config); + public abstract Request getNoiseCancellingOptimizerState(); + public abstract Request getAudioCodec(); public abstract Request getBattery(final BatteryType batteryType); @@ -94,7 +96,7 @@ public abstract class AbstractSonyProtocolImpl { public abstract Request setVoiceNotifications(final VoiceNotifications config); - public abstract Request startNoiseCancellingOptimizer(); + public abstract Request startNoiseCancellingOptimizer(final boolean start); public abstract Request powerOff(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java index c79b410d5..881407c7a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java @@ -53,7 +53,12 @@ public enum PayloadType { AMBIENT_SOUND_CONTROL_SET(MessageType.COMMAND_1, 0x68), AMBIENT_SOUND_CONTROL_NOTIFY(MessageType.COMMAND_1, 0x69), - NOISE_CANCELLING_OPTIMIZER_START_REQUEST(MessageType.COMMAND_1, 0x84), + NOISE_CANCELLING_OPTIMIZER_START(MessageType.COMMAND_1, 0x84), + NOISE_CANCELLING_OPTIMIZER_STATUS(MessageType.COMMAND_1, 0x85), + + NOISE_CANCELLING_OPTIMIZER_STATE_GET(MessageType.COMMAND_1, 0x86), + NOISE_CANCELLING_OPTIMIZER_STATE_RET(MessageType.COMMAND_1, 0x87), + NOISE_CANCELLING_OPTIMIZER_STATE_NOTIFY(MessageType.COMMAND_1, 0x89), TOUCH_SENSOR_GET(MessageType.COMMAND_1, 0xd6), TOUCH_SENSOR_RET(MessageType.COMMAND_1, 0xd7), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java index 01e36d578..9f46600df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java @@ -16,6 +16,9 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATUS; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,6 +56,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.params.AudioCodec; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.VirtualSoundParam; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -130,6 +134,17 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { return new Request(PayloadType.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array()); } + @Override + public Request getNoiseCancellingOptimizerState() { + return new Request( + PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getMessageType(), + new byte[]{ + PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getCode(), + (byte) 0x01 + } + ); + } + @Override public Request getAudioCodec() { return new Request( @@ -399,14 +414,14 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { } @Override - public Request startNoiseCancellingOptimizer() { + public Request startNoiseCancellingOptimizer(final boolean start) { return new Request( - PayloadType.NOISE_CANCELLING_OPTIMIZER_START_REQUEST.getMessageType(), + PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getMessageType(), new byte[]{ - PayloadType.NOISE_CANCELLING_OPTIMIZER_START_REQUEST.getCode(), + PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getCode(), (byte) 0x01, (byte) 0x00, - (byte) 0x01 + (byte) (start ? 0x01 : 0x00) } ); } @@ -447,6 +462,11 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { case AMBIENT_SOUND_CONTROL_RET: case AMBIENT_SOUND_CONTROL_NOTIFY: return handleAmbientSoundControl(payload); + case NOISE_CANCELLING_OPTIMIZER_STATUS: + return handleNoiseCancellingOptimizerStatus(payload); + case NOISE_CANCELLING_OPTIMIZER_STATE_RET: + case NOISE_CANCELLING_OPTIMIZER_STATE_NOTIFY: + return handleNoiseCancellingOptimizerState(payload); case TOUCH_SENSOR_RET: case TOUCH_SENSOR_NOTIFY: return handleTouchSensor(payload); @@ -486,6 +506,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { capabilityRequests.add(getBattery(BatteryType.SINGLE)); capabilityRequests.add(getAudioCodec()); capabilityRequests.add(getAmbientSoundControl()); + capabilityRequests.add(getNoiseCancellingOptimizerState()); capabilityRequests.add(getAudioUpsampling()); capabilityRequests.add(getVoiceNotifications()); capabilityRequests.add(getAutomaticPowerOff()); @@ -499,6 +520,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { capabilityRequests.add(getBattery(BatteryType.SINGLE)); capabilityRequests.add(getAudioCodec()); capabilityRequests.add(getAmbientSoundControl()); + capabilityRequests.add(getNoiseCancellingOptimizerState()); capabilityRequests.add(getAudioUpsampling()); capabilityRequests.add(getVoiceNotifications()); capabilityRequests.add(getAutomaticPowerOff()); @@ -592,6 +614,50 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { return Collections.singletonList(eventUpdatePreferences); } + public List handleNoiseCancellingOptimizerStatus(final byte[] payload) { + if (payload.length != 4) { + LOG.warn("Unexpected payload length {}", payload.length); + return Collections.emptyList(); + } + + final NoiseCancellingOptimizerStatus status = NoiseCancellingOptimizerStatus.fromCode(payload[3]); + + if (status == null) { + LOG.warn("Unable to determine noise cancelling opptimizer status from {}", GB.hexdump(payload)); + return Collections.emptyList(); + } + + LOG.info("Noise Cancelling Optimizer status: {}", status); + + final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() + .withPreference(PREF_SONY_NOISE_OPTIMIZER_STATUS, status.name().toLowerCase(Locale.ROOT)); + + return Collections.singletonList(event); + } + + public List handleNoiseCancellingOptimizerState(final byte[] payload) { + // 89 01 01 01 01 0A + if (payload.length != 6) { + LOG.warn("Unexpected payload length {}", payload.length); + return Collections.emptyList(); + } + + final float pressure = payload[5] / 10.0f; + + if (pressure <= 0 || pressure > 1.0f) { + LOG.warn("Invalid Noise Cancelling Optimizer pressure: {} atm, ignoring", pressure); + return Collections.emptyList(); + } + + LOG.info("Noise Cancelling Optimizer pressure: {} atm", pressure); + + final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() + .withPreference(PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE, String.format(Locale.getDefault(), "%.2f atm", pressure)); + + return Collections.singletonList(event); + } + + public List handleAudioUpsampling(final byte[] payload) { if (payload.length != 4) { LOG.warn("Unexpected payload length {}", payload.length); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/NoiseCancellingOptimizerStatus.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/NoiseCancellingOptimizerStatus.java new file mode 100644 index 000000000..c41ec4925 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/NoiseCancellingOptimizerStatus.java @@ -0,0 +1,56 @@ +/* Copyright (C) 2021 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 . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params; + +import android.content.Context; + +import java.util.Locale; + +public enum NoiseCancellingOptimizerStatus { + NOT_RUNNING(0x00), + WEARING_CONDITION(0x01), + ATMOSPHERIC_PRESSURE(0x02), + ANALYZING(0x10), + FINISHED(0x11), + ; + + private final byte code; + + NoiseCancellingOptimizerStatus(final int code) { + this.code = (byte) code; + } + + public byte getCode() { + return this.code; + } + + public static NoiseCancellingOptimizerStatus fromCode(final byte code) { + for (final NoiseCancellingOptimizerStatus audioCodec : values()) { + if (audioCodec.code == code) { + return audioCodec; + } + } + + return null; + } + + public String i18n(final Context context) { + final String stringName = String.format("sony_anc_optimizer_status_%s", name().toLowerCase(Locale.ROOT)); + final int stringId = context.getResources().getIdentifier(stringName, "string", context.getPackageName()); + return context.getString(stringId); + } +} diff --git a/app/src/main/res/drawable/ic_auto_awesome.xml b/app/src/main/res/drawable/ic_auto_awesome.xml new file mode 100644 index 000000000..d900f58fb --- /dev/null +++ b/app/src/main/res/drawable/ic_auto_awesome.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_pressure.xml b/app/src/main/res/drawable/ic_pressure.xml new file mode 100644 index 000000000..0b293a8b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_pressure.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8511f6fac..01d03871f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -355,6 +355,7 @@ Connected Unknown state (unknown) + Unknown Test Test notification This is a test notification from Gadgetbridge @@ -771,6 +772,7 @@ Cancel Delete OK + Start Set Export/Import directory content Show Export/Import directory content @@ -1393,6 +1395,12 @@ Ambient Sound Focus on Voice Ambient Sound Level + Noise Cancelling Optimizer + Optimize + Click to start the noise cancelling optimizer. + Noise Cancelling Optimizer + Use the headphones as you normally would. If the wearing condition or atmospheric pressure change, run the optimizer again. + Atmospheric pressure Sound Position Off Front @@ -1408,6 +1416,12 @@ Concert Hall Warning: The equalizer, audio position and surround settings only work for the SBC audio codec. Equalizer + Starting… + Not Running + Measuring wearing condition… + Measuring atmostpheric pressure… + Analyzing… + Finishing… Off Bright Excited diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_anc_optimizer.xml b/app/src/main/res/xml/devicesettings_sony_headphones_anc_optimizer.xml new file mode 100644 index 000000000..860e19302 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_anc_optimizer.xml @@ -0,0 +1,31 @@ + + + + + + + + + + +