/* Copyright (C) 2021-2024 Damien Gaignon, Daniel Dakhno, José Rebelo, Petr Vaněk 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.devices.sony.headphones; import androidx.annotation.NonNull; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLClassicDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.SonyHeadphonesSupport; public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceCoordinator { @Override public String getManufacturer() { return "Sony"; } @Override public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) { return new SonyHeadphonesSettingsCustomizer(device); } @Override public boolean suggestUnbindBeforePair() { return false; } @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { } @Override public boolean supportsPowerOff() { return supports(SonyHeadphonesCapabilities.PowerOffFromPhone); } @Override public int getBatteryCount() { if (supports(SonyHeadphonesCapabilities.BatterySingle)) { if (supports(SonyHeadphonesCapabilities.BatteryDual) || supports(SonyHeadphonesCapabilities.BatteryDual2)) { throw new IllegalStateException("A device can't have both single and dual battery"); } else if (supports(SonyHeadphonesCapabilities.BatteryCase)) { throw new IllegalStateException("Devices with single battery + case are not supported by the protocol"); } } int batteryCount = 0; if (supports(SonyHeadphonesCapabilities.BatterySingle)) { batteryCount += 1; } if (supports(SonyHeadphonesCapabilities.BatteryCase)) { batteryCount += 1; } if (supports(SonyHeadphonesCapabilities.BatteryDual) || supports(SonyHeadphonesCapabilities.BatteryDual2)) { batteryCount += 2; } return batteryCount; } @Override public BatteryConfig[] getBatteryConfig(final GBDevice device) { final List batteries = new ArrayList<>(3); if (supports(SonyHeadphonesCapabilities.BatterySingle)) { batteries.add(new BatteryConfig(batteries.size())); } if (supports(SonyHeadphonesCapabilities.BatteryCase)) { batteries.add(new BatteryConfig(batteries.size(), R.drawable.ic_tws_case, R.string.battery_case)); } if (supports(SonyHeadphonesCapabilities.BatteryDual) || supports(SonyHeadphonesCapabilities.BatteryDual2)) { batteries.add(new BatteryConfig(batteries.size(), R.drawable.ic_galaxy_buds_l, R.string.left_earbud)); batteries.add(new BatteryConfig(batteries.size(), R.drawable.ic_galaxy_buds_r, R.string.right_earbud)); } return batteries.toArray(new BatteryConfig[0]); } @Override public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) { final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); if (supports(SonyHeadphonesCapabilities.AmbientSoundControl) || supports(SonyHeadphonesCapabilities.AmbientSoundControl2)) { if (supports(SonyHeadphonesCapabilities.WindNoiseReduction)) { deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_ambient_sound_control_wind_noise_reduction); } else { deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_ambient_sound_control); } if (supports(SonyHeadphonesCapabilities.AncOptimizer)) { deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_anc_optimizer); } } if (supports(SonyHeadphonesCapabilities.AdaptiveVolumeControl)) { deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_adaptive_volume_control); } if (supports(SonyHeadphonesCapabilities.SpeakToChatConfig)) { deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_speak_to_chat_with_settings); } else if (supports(SonyHeadphonesCapabilities.SpeakToChatEnabled)) { deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_speak_to_chat_simple); } addSettingsUnderHeader(deviceSpecificSettings, R.xml.devicesettings_header_other, new LinkedHashMap() {{ put(SonyHeadphonesCapabilities.AudioSettingsOnlyOnSbcCodec, R.xml.devicesettings_sony_warning_wh1000xm3); put(SonyHeadphonesCapabilities.EqualizerSimple, R.xml.devicesettings_sony_headphones_equalizer); put(SonyHeadphonesCapabilities.EqualizerWithCustomBands, R.xml.devicesettings_sony_headphones_equalizer_with_custom_bands); put(SonyHeadphonesCapabilities.SoundPosition, R.xml.devicesettings_sony_headphones_sound_position); put(SonyHeadphonesCapabilities.SurroundMode, R.xml.devicesettings_sony_headphones_surround_mode); put(SonyHeadphonesCapabilities.AudioUpsampling, R.xml.devicesettings_sony_headphones_audio_upsampling); put(SonyHeadphonesCapabilities.Volume, R.xml.devicesettings_volume); }}); final List callsAndNotif = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS); callsAndNotif.add(R.xml.devicesettings_headphones); addSettingsUnderHeader(deviceSpecificSettings, R.xml.devicesettings_header_system, new LinkedHashMap() {{ put(SonyHeadphonesCapabilities.WideAreaTap, R.xml.devicesettings_sony_headphones_wide_area_tap); put(SonyHeadphonesCapabilities.ButtonModesLeftRight, R.xml.devicesettings_sony_headphones_button_modes_left_right); put(SonyHeadphonesCapabilities.AmbientSoundControlButtonMode, R.xml.devicesettings_sony_headphones_ambient_sound_control_button_modes); put(SonyHeadphonesCapabilities.QuickAccess, R.xml.devicesettings_sony_headphones_quick_access); put(SonyHeadphonesCapabilities.TouchSensorSingle, R.xml.devicesettings_sony_headphones_touch_sensor_single); put(SonyHeadphonesCapabilities.PauseWhenTakenOff, R.xml.devicesettings_sony_headphones_pause_when_taken_off); put(SonyHeadphonesCapabilities.AutomaticPowerOffWhenTakenOff, R.xml.devicesettings_automatic_power_off_when_taken_off); put(SonyHeadphonesCapabilities.AutomaticPowerOffByTime, R.xml.devicesettings_automatic_power_off_by_time); put(SonyHeadphonesCapabilities.VoiceNotifications, R.xml.devicesettings_sony_headphones_notifications_voice_guide); }}); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_header_developer); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_protocol_version); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sony_headphones_device_info); return deviceSpecificSettings; } public List getCapabilities() { return Collections.emptyList(); } public boolean supports(final SonyHeadphonesCapabilities capability) { return getCapabilities().contains(capability); } /** * Add the preference screens for capabilities under a header. The header is also only added if at least one capability is supported by the device. * * @param deviceSpecificSettings the device specific settings * @param header the header to add, if any capability supported * @param capabilities the map of capability to preference screen */ private void addSettingsUnderHeader(final DeviceSpecificSettings deviceSpecificSettings, final int header, final Map capabilities) { final Set supportedCapabilities = new HashSet<>(capabilities.keySet()); for (SonyHeadphonesCapabilities capability : capabilities.keySet()) { if (!supports(capability)) { supportedCapabilities.remove(capability); } } if (supportedCapabilities.isEmpty()) { // None of the capabilities in the map are supported return; } deviceSpecificSettings.addRootScreen(header); for (Map.Entry capabilitiesSetting : capabilities.entrySet()) { if (supports(capabilitiesSetting.getKey())) { deviceSpecificSettings.addRootScreen(capabilitiesSetting.getValue()); } } } public boolean preferServiceV2() { return false; } @NonNull @Override public Class getDeviceSupportClass() { return SonyHeadphonesSupport.class; } @Override public int getDefaultIconResource() { return R.drawable.ic_device_sony_overhead; } @Override public int getDisabledIconResource() { return R.drawable.ic_device_sony_overhead_disabled; } }