~aleteoryx/muditaos

bb76fe408007871d6526f4a607b93f959c18436b — Hubert Chrzaniuk 5 years ago ea7264d
[EGD-5713] Change volume hardware buttons behaviour

The volume button policy has been redesigned to be less
ambiguous for the user.
M module-audio/Audio/AudioCommon.cpp => module-audio/Audio/AudioCommon.cpp +48 -7
@@ 37,6 37,30 @@ namespace audio
        return utils::enumToString(setting);
    }

    bool isSystemSound(const PlaybackType &playbackType) noexcept
    {
        switch (playbackType) {

        case PlaybackType::Notifications:
            [[fallthrough]];
        case PlaybackType::KeypadSound:
            [[fallthrough]];
        case PlaybackType::CallRingtone:
            [[fallthrough]];
        case PlaybackType::TextMessageRingtone:
            [[fallthrough]];
        case PlaybackType::Meditation:
            return true;
        case PlaybackType::None:
            [[fallthrough]];
        case PlaybackType::Alarm:
            [[fallthrough]];
        case PlaybackType::Multimedia:
            return false;
        }
        return false;
    }

    const std::string dbPath(const sys::phone_modes::PhoneMode &phoneMode,
                             const Setting &setting,
                             const PlaybackType &playbackType,


@@ 46,20 70,37 @@ namespace audio
            return std::string();
        }

        constexpr auto separator = '/';
        std::string path(audioDbPrefix);
        std::vector<std::string> pathElements;
        std::string path;

        pathElements.emplace_back(audioDbPrefix);
        pathElements.emplace_back(utils::enumToString(phoneMode));

        path.append(utils::enumToString(phoneMode));
        path.append(1, separator);
        if (auto s = str(profileType); !s.empty()) {
            path.append(s.append(1, separator));
            pathElements.emplace_back(s);
        }

        if (auto s = str(playbackType); !s.empty()) {
            path.append(s.append(1, separator));

            if (setting == Setting::Volume && isSystemSound(playbackType)) {
                pathElements.emplace_back(str(PlaybackType::System));
            }
            else {
                pathElements.emplace_back(s);
            }
        }

        if (auto s = str(setting); !s.empty()) {
            path.append(s);
            pathElements.emplace_back(s);
        }

        for (size_t idx = 0; idx < pathElements.size(); idx++) {
            path.append(pathElements[idx]);
            if (idx != pathElements.size() - 1) {
                path.append(1, dbPathSeparator);
            }
        }

        return path;
    }


M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +7 -1
@@ 9,6 9,7 @@
#include <Service/Message.hpp>
#include <PhoneModes/Common.hpp>
#include <Utils.hpp>
#include <PhoneModes/Common.hpp>

#include <map>
#include <bitset>


@@ 34,7 35,9 @@ namespace audio

    inline constexpr auto audioOperationTimeout = 1000U;

    inline constexpr auto audioDbPrefix = "audio/";
    inline constexpr auto audioDbPrefix   = "audio";
    inline constexpr auto systemDbPrefix  = "system";
    inline constexpr auto dbPathSeparator = '/';

    enum class Setting
    {


@@ 49,6 52,7 @@ namespace audio
        None,
        Multimedia,
        Notifications,
        System = Notifications,
        KeypadSound,
        CallRingtone,
        TextMessageRingtone,


@@ 66,6 70,8 @@ namespace audio
                                           const PlaybackType &playbackType,
                                           const Profile::Type &profileType);

    [[nodiscard]] bool isSystemSound(const PlaybackType &playbackType) noexcept;

    enum class EventType
    {
        // HW state change notifications

M module-audio/Audio/test/unittest_audio.cpp => module-audio/Audio/test/unittest_audio.cpp +59 -0
@@ 17,6 17,7 @@
#include "Audio/Audio.hpp"
#include "Audio/Operation/Operation.hpp"

using namespace audio;

TEST_CASE("Test audio tags")
{


@@ 76,6 77,64 @@ TEST_CASE("Audio settings string creation")
                                       audio::Profile::Type::Idle);
        REQUIRE(str.empty());
    }

    SECTION("System settings change")
    {
        struct TestCase
        {
            PlaybackType playbackType;
            Setting setting;
            std::string path;
        };

        std::vector<TestCase> testCases = {
            // system volume
            {PlaybackType::System, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
            {PlaybackType::Meditation, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
            {PlaybackType::CallRingtone, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
            {PlaybackType::KeypadSound, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
            {PlaybackType::TextMessageRingtone,
             Setting::Volume,
             "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
            {PlaybackType::Notifications, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
            // other types volume
            {PlaybackType::Alarm, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Alarm/Volume"},
            {PlaybackType::Multimedia, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Multimedia/Volume"},
            {PlaybackType::None, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Volume"},

            // EnableSound
            {PlaybackType::System, Setting::EnableSound, "audio/Offline/RecordingBuiltInMic/Notifications/EnableSound"},
            {PlaybackType::Meditation,
             Setting::EnableSound,
             "audio/Offline/RecordingBuiltInMic/Meditation/EnableSound"},
            {PlaybackType::CallRingtone,
             Setting::EnableSound,
             "audio/Offline/RecordingBuiltInMic/CallRingtone/EnableSound"},
            {PlaybackType::KeypadSound,
             Setting::EnableSound,
             "audio/Offline/RecordingBuiltInMic/KeypadSound/EnableSound"},
            {PlaybackType::TextMessageRingtone,
             Setting::EnableSound,
             "audio/Offline/RecordingBuiltInMic/TextMessageRingtone/EnableSound"},
            {PlaybackType::Notifications,
             Setting::EnableSound,
             "audio/Offline/RecordingBuiltInMic/Notifications/EnableSound"},
            {PlaybackType::Alarm, Setting::EnableSound, "audio/Offline/RecordingBuiltInMic/Alarm/EnableSound"},
            {PlaybackType::Multimedia,
             Setting::EnableSound,
             "audio/Offline/RecordingBuiltInMic/Multimedia/EnableSound"},
            {PlaybackType::None, Setting::EnableSound, "audio/Offline/RecordingBuiltInMic/EnableSound"},
        };

        for (auto &testCase : testCases) {
            const auto str = audio::dbPath(sys::phone_modes::PhoneMode::Offline,
                                           testCase.setting,
                                           testCase.playbackType,
                                           audio::Profile::Type::RecordingBuiltInMic);
            REQUIRE_FALSE(str.empty());
            REQUIRE(str == testCase.path);
        }
    }
}

class MockAudio : public audio::Audio

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +39 -153
@@ 74,103 74,25 @@ sys::ReturnCodes ServiceAudio::InitHandler()
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Multimedia, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackHeadphones),
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(
             PhoneMode::Connected, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackBluetoothA2DP),
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(
             PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(PhoneMode::DoNotDisturb,
                Setting::Volume,
                PlaybackType::Notifications,
                Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(
             PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackLoudspeaker),
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackHeadphones),
        {dbPath(PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackBluetoothA2DP),
        {dbPath(PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Notifications, Profile::Type::PlaybackLoudspeaker),
        {dbPath(PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackHeadphones),
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackBluetoothA2DP),
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackHeadphones),
         defaultVolumeMuted},
        {dbPath(
             PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeMuted},
        {dbPath(
             PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeMuted},

        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackHeadphones),
         defaultVolumeMuted},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeMuted},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::KeypadSound, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeMuted},

        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(
             PhoneMode::Connected, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(
             PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackHeadphones),
         defaultVolumeMuted},
        {dbPath(PhoneMode::DoNotDisturb,
                Setting::Volume,
                PlaybackType::CallRingtone,
                Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeMuted},
        {dbPath(
             PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeMuted},

        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackHeadphones),
         defaultVolumeMuted},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeMuted},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::CallRingtone, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeMuted},

        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(
             PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::DoNotDisturb, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackLoudspeaker),
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::System, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::Connected, Setting::Volume, PlaybackType::Alarm, Profile::Type::PlaybackHeadphones),


@@ 194,52 116,6 @@ sys::ReturnCodes ServiceAudio::InitHandler()
        {dbPath(PhoneMode::Offline, Setting::Volume, PlaybackType::Alarm, Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::Connected,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackHeadphones),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeLow},
        {dbPath(PhoneMode::Connected,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackLoudspeaker),
         defaultVolumeHigh},

        {dbPath(PhoneMode::DoNotDisturb,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackHeadphones),
         defaultVolumeMuted},
        {dbPath(PhoneMode::DoNotDisturb,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeMuted},
        {dbPath(PhoneMode::DoNotDisturb,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackLoudspeaker),
         defaultVolumeMuted},

        {dbPath(
             PhoneMode::Offline, Setting::Volume, PlaybackType::TextMessageRingtone, Profile::Type::PlaybackHeadphones),
         defaultVolumeMuted},
        {dbPath(PhoneMode::Offline,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackBluetoothA2DP),
         defaultVolumeMuted},
        {dbPath(PhoneMode::Offline,
                Setting::Volume,
                PlaybackType::TextMessageRingtone,
                Profile::Type::PlaybackLoudspeaker),
         defaultVolumeMuted},

        // ROUTING
        {dbPath(PhoneMode::Connected, Setting::Gain, PlaybackType::None, Profile::Type::RoutingBluetoothHSP), "20"},
        {dbPath(PhoneMode::Connected, Setting::Gain, PlaybackType::None, Profile::Type::RoutingEarspeaker), "3"},


@@ 689,23 565,38 @@ void ServiceAudio::HandleNotification(const AudioNotificationMessage::Type &type

auto ServiceAudio::HandleKeyPressed(const int step) -> std::unique_ptr<AudioKeyPressedResponse>
{
    const std::vector<audio::PlaybackType> typesToMute = {audio::PlaybackType::Notifications,
                                                          audio::PlaybackType::CallRingtone,
                                                          audio::PlaybackType::TextMessageRingtone};

    // mute if 0 and return with parameter shouldn't popup
    bool muted         = false;
    const auto context = getCurrentContext();
    if (step < 0) {
        HandleStop(typesToMute, Token(), muted);
        if (muted) {
            return std::make_unique<AudioKeyPressedResponse>(audio::RetCode::Success, 0, muted, context);
    auto context       = getCurrentContext();

    const auto currentVolume =
        utils::getNumericValue<int>(getSetting(Setting::Volume, Profile::Type::Idle, PlaybackType::None));

    if (isSystemSound(context.second)) {
        // active system sounds can be only muted, no volume control is possible
        if (step < 0) {
            HandleStop({context.second}, Token(), muted);
            if (muted) {
                return std::make_unique<AudioKeyPressedResponse>(audio::RetCode::Success, 0, muted, context);
            }
        }
        else {
            return std::make_unique<AudioKeyPressedResponse>(audio::RetCode::Success, currentVolume, muted, context);
        }
    }
    const auto volume =
        utils::getNumericValue<int>(getSetting(Setting::Volume, Profile::Type::Idle, PlaybackType::None));
    const auto newVolume = std::clamp(volume + step, static_cast<int>(minVolume), static_cast<int>(maxVolume));
    setSetting(Setting::Volume, std::to_string(newVolume), Profile::Type::Idle, PlaybackType::None);

    const auto newVolume = std::clamp(currentVolume + step, static_cast<int>(minVolume), static_cast<int>(maxVolume));
    if (auto input = audioMux.GetIdleInput(); input) {
        // when no active input change volume of system sounds
        auto updatedProfile = (*input)->audio->GetPriorityPlaybackProfile();
        setSetting(Setting::Volume, std::to_string(newVolume), updatedProfile, PlaybackType::System);
        context.second = PlaybackType::CallRingtone;
    }
    else {
        // update volume of currently active sound
        setSetting(Setting::Volume, std::to_string(newVolume));
    }
    return std::make_unique<AudioKeyPressedResponse>(audio::RetCode::Success, newVolume, muted, context);
}



@@ 864,18 755,15 @@ void ServiceAudio::setSetting(const Setting &setting,

    std::optional<AudioMux::Input *> activeInput;

    // request changing currently active audio playback
    if (profileType == Profile::Type::Idle && playbackType == PlaybackType::None) {
        if (activeInput = audioMux.GetActiveInput(); activeInput.has_value()) {
            const auto &currentOperation = (*activeInput)->audio->GetCurrentOperation();
            updatedProfile               = currentOperation.GetProfile()->GetType();
            updatedPlayback              = (*activeInput)->audio->GetCurrentOperationPlaybackType();
        }
        else if (auto input = audioMux.GetIdleInput(); input && (setting == audio::Setting::Volume)) {
            updatedProfile  = (*input)->audio->GetPriorityPlaybackProfile();
            updatedPlayback = PlaybackType::CallRingtone;
            valueToSet      = std::clamp(utils::getNumericValue<audio::Volume>(value), minVolume, maxVolume);
        }
        else {
            LOG_DEBUG("%s has not been set - no active playback.", utils::enumToString(setting).c_str());
            return;
        }
    }


@@ 915,9 803,7 @@ const std::pair<audio::Profile::Type, audio::PlaybackType> ServiceAudio::getCurr
    const auto activeInput = audioMux.GetActiveInput();
    if (!activeInput.has_value()) {
        const auto idleInput = audioMux.GetIdleInput();
        if (idleInput.has_value()) {
            return {(*idleInput)->audio->GetPriorityPlaybackProfile(), audio::PlaybackType::CallRingtone};
        }
        return {(*idleInput)->audio->GetPriorityPlaybackProfile(), audio::PlaybackType::None};
    }
    auto &audio                  = (*activeInput)->audio;
    const auto &currentOperation = audio->GetCurrentOperation();

M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +9 -2
@@ 92,10 92,17 @@ class ServiceAudio : public sys::Service
    constexpr auto ShouldLoop(const std::optional<audio::PlaybackType> &type) const -> bool;
    auto IsBusy() -> bool;

    //! Setter for settings
    //! \param setting Setting be controlled
    //! \param value New value of setting
    //! \param profileType Audio profile to be controlled
    //! \param playbackType Playback type to be controlled
    //! \note when profileType and playbackType are not set currently active sound will be controlled
    void setSetting(const audio::Setting &setting,
                    const std::string &value,
                    const audio::Profile::Type &profileType,
                    const audio::PlaybackType &playbackType);
                    const audio::Profile::Type &profileType = audio::Profile::Type::Idle,
                    const audio::PlaybackType &playbackType = audio::PlaybackType::None);

    [[nodiscard]] std::string getSetting(const audio::Setting &setting,
                                         const audio::Profile::Type &profileType,
                                         const audio::PlaybackType &playbackType);