M enabled_unittests => enabled_unittests +4 -0
@@ 467,4 467,8 @@ TESTS_LIST["catch2-module-bsp"]="
BatteryChargerUtilsTest;
"
#---------
+TESTS_LIST["catch2-audio-volume-scaler"]="
+ Scenario: Scale volume levels between system and bluetooth;
+"
+#---------
M module-apps/Application.cpp => module-apps/Application.cpp +19 -22
@@ 300,9 300,6 @@ namespace app
else if (dynamic_cast<sevm::SIMMessage *>(msgl) != nullptr) {
return handleSIMMessage(msgl);
}
- else if (dynamic_cast<AudioKeyPressedResponse *>(msgl) != nullptr) {
- return handleAudioKeyMessage(msgl);
- }
return sys::msgNotHandled();
}
@@ 588,25 585,6 @@ namespace app
return sys::msgHandled();
}
- sys::MessagePointer Application::handleAudioKeyMessage(sys::Message *msgl)
- {
- using namespace gui::popup;
- const auto msg = static_cast<AudioKeyPressedResponse *>(msgl);
- if (msg->showPopup == AudioKeyPressedResponse::ShowPopup::True) {
- LOG_INFO("Playback: %s, volume: %s",
- audio::str(msg->context.second).c_str(),
- std::to_string(msg->volume).c_str());
- auto data = std::make_unique<gui::VolumePopupData>(msg->volume, msg->context);
- if (getCurrentWindow()->getName() == window::volume_window) {
- updateWindow(window::volume_window, std::move(data));
- }
- else {
- switchWindow(window::volume_window, std::move(data));
- }
- }
- return sys::msgHandled();
- }
-
sys::ReturnCodes Application::InitHandler()
{
setState(State::INITIALIZING);
@@ 721,6 699,18 @@ namespace app
}
}
+ void Application::handleVolumeChanged(audio::Volume volume, audio::Context context)
+ {
+ using namespace gui::popup;
+ const auto popupName = resolveWindowName(gui::popup::ID::Volume);
+ if (const auto currentWindowName = getCurrentWindow()->getName(); currentWindowName == popupName) {
+ updateWindow(popupName, std::make_unique<gui::VolumePopupData>(volume, context));
+ }
+ else {
+ switchWindow(popupName, std::make_unique<gui::VolumePopupData>(volume, context));
+ }
+ }
+
void Application::attachPopups(const std::vector<gui::popup::ID> &popupsList)
{
using namespace gui::popup;
@@ 763,6 753,13 @@ namespace app
auto popupParams = static_cast<const gui::PhoneModePopupRequestParams *>(params);
handlePhoneModeChanged(popupParams->getPhoneMode());
}
+ else if (id == ID::Volume) {
+ auto volumeParams = static_cast<const gui::VolumePopupRequestParams *>(params);
+ LOG_INFO("Playback: %s, volume: %s",
+ audio::str(volumeParams->getAudioContext().second).c_str(),
+ std::to_string(volumeParams->getVolume()).c_str());
+ handleVolumeChanged(volumeParams->getVolume(), volumeParams->getAudioContext());
+ }
else {
switchWindow(gui::popup::resolveWindowName(id));
}
M module-apps/Application.hpp => module-apps/Application.hpp +5 -1
@@ 185,7 185,6 @@ namespace app
sys::MessagePointer handleGetDOM(sys::Message *msgl);
sys::MessagePointer handleAppFocusLost(sys::Message *msgl);
sys::MessagePointer handleSIMMessage(sys::Message *msgl);
- sys::MessagePointer handleAudioKeyMessage(sys::Message *msgl);
virtual bool isPopupPermitted(gui::popup::ID popupId) const;
@@ 331,6 330,11 @@ namespace app
/// @param tethering new tethering mode
void handlePhoneModeChanged(sys::phone_modes::PhoneMode mode);
+ /// Handles volume changed event
+ /// @param volume current volume level
+ /// @param context audio context which contains current profile and playback
+ void handleVolumeChanged(audio::Volume volume, audio::Context context);
+
/// @ingrup AppWindowStack
WindowsStack windowsStack;
WindowsFactory windowsFactory;
M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +3 -0
@@ 89,6 89,9 @@ namespace app
bool ApplicationCall::isPopupPermitted(gui::popup::ID popupId) const
{
+ if (popupId == gui::popup::ID::Volume) {
+ return true;
+ }
return getState() == call::State::IDLE;
}
M => +1 -1
@@ 67,7 67,7 @@ namespace gui
}
}
void VolumeWindow::showProperText(const AudioContext &audioContext, const audio::Volume volume) noexcept
void VolumeWindow::showProperText(const audio::Context &audioContext, const audio::Volume volume) noexcept
{
volumeText->setText(utils::translate(style::window::volume::base_title_key));
const auto [profileType, playbackType] = audioContext;
M => +2 -4
@@ 36,13 36,11 @@ namespace gui
{
class VolumeWindow : public WindowWithTimer
{
using AudioContext = std::pair<audio::Profile::Type, audio::PlaybackType>;
private:
audio::Volume volume = 0;
AudioContext audioContext;
audio::Context audioContext;
void showProperText(const AudioContext &audioContext, const audio::Volume volume) noexcept;
void showProperText(const audio::Context &audioContext, const audio::Volume volume) noexcept;
void showMultimediaPlayback() noexcept;
void showCalling() noexcept;
void showMuted() noexcept;
M => +3 -5
@@ 10,10 10,8 @@ namespace gui
{
class VolumePopupData : public SwitchData
{
using AudioContext = std::pair<audio::Profile::Type, audio::PlaybackType>;
public:
explicit VolumePopupData(const audio::Volume volume, const AudioContext audioContext)
explicit VolumePopupData(const audio::Volume volume, const audio::Context audioContext)
: SwitchData(), volume{volume}, audioContext{audioContext}
{}
@@ 22,14 20,14 @@ namespace gui
return volume;
}
[[nodiscard]] auto getAudioContext() const noexcept -> AudioContext
[[nodiscard]] auto getAudioContext() const noexcept -> audio::Context
{
return audioContext;
}
private:
const audio::Volume volume;
const AudioContext audioContext;
const audio::Context audioContext;
};
class ModesPopupData : public SwitchData
M => +23 -0
@@ 7,6 7,7 @@
#include <service-appmgr/Actions.hpp>
#include <module-sys/PhoneModes/Common.hpp>
#include <module-audio/Audio/AudioCommon.hpp>
namespace gui
{
@@ 41,4 42,26 @@ namespace gui
private:
sys::phone_modes::PhoneMode phoneMode;
};
class VolumePopupRequestParams : public PopupRequestParams
{
public:
VolumePopupRequestParams(audio::Volume volume, const audio::Context audioContext)
: PopupRequestParams{gui::popup::ID::Volume}, volume{volume}, audioContext{audioContext}
{}
[[nodiscard]] auto getVolume() const noexcept
{
return volume;
}
[[nodiscard]] auto getAudioContext() const noexcept
{
return audioContext;
}
private:
const audio::Volume volume;
const audio::Context audioContext;
};
} // namespace gui
M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +3 -0
@@ 67,6 67,9 @@ namespace audio
Last = Alarm,
};
+ /// Used to describe audio operations
+ using Context = std::pair<Profile::Type, PlaybackType>;
+
struct DbPathElement
{
Setting setting;
A module-audio/Audio/VolumeScaler.cpp => module-audio/Audio/VolumeScaler.cpp +24 -0
@@ 0,0 1,24 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "VolumeScaler.hpp"
+
+namespace audio::volume::scaler
+{
+ Volume toSystemVolume(std::uint8_t avrcpVolume) noexcept
+ {
+ constexpr auto avrcpMaxVolume = float{0x7F}; // from AVRCP documentation
+ const auto systemVolume = (avrcpVolume / avrcpMaxVolume) * audio::maxVolume;
+ // prevents conversion to 0 while in fact sound is not muted
+ if (systemVolume > 0.01f && systemVolume < 1.0f) {
+ return 1;
+ }
+ return systemVolume;
+ }
+
+ std::uint8_t toAvrcpVolume(float systemVolume) noexcept
+ {
+ constexpr auto avrcpMaxVolume = std::uint8_t{0x7F}; // from AVRCP documentation
+ return std::round(systemVolume * avrcpMaxVolume / audio::maxVolume);
+ }
+} // namespace audio::volume::scaler
A module-audio/Audio/VolumeScaler.hpp => module-audio/Audio/VolumeScaler.hpp +18 -0
@@ 0,0 1,18 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+#include "AudioCommon.hpp"
+
+/// @brief Converts volume between system and bluetooth ranges.
+namespace audio::volume::scaler
+{
+ /// @brief Takes volume level and converts it to according one for the system.
+ /// @param avrcpVolume - AVRCP volume level.
+ /// @return Volume level scaled to satisfy system's range [audio::minVolume, audio::maxVolume].
+ Volume toSystemVolume(std::uint8_t avrcpVolume) noexcept;
+ /// @brief Takes volume level and converts it to according one for the AVRCP.
+ /// @param systemVolume - system volume level.
+ /// @return Volume level scaled to satisfy AVRCP's range [0, 127].
+ std::uint8_t toAvrcpVolume(float systemVolume) noexcept;
+} // namespace audio::volume::scaler
M module-audio/Audio/test/CMakeLists.txt => module-audio/Audio/test/CMakeLists.txt +9 -0
@@ 30,4 30,13 @@ add_gtest_executable(
module-audio
)
+add_catch2_executable(
+ NAME
+ audio-volume-scaler
+ SRCS
+ unittest_scaler.cpp
+ LIBS
+ module-audio
+)
+
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/testfiles" DESTINATION "${CMAKE_BINARY_DIR}")
A module-audio/Audio/test/unittest_scaler.cpp => module-audio/Audio/test/unittest_scaler.cpp +89 -0
@@ 0,0 1,89 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#define CATCH_CONFIG_MAIN
+
+#include <catch2/catch.hpp>
+
+#include <module-audio/Audio/VolumeScaler.hpp>
+
+SCENARIO("Scale volume levels between system and bluetooth")
+{
+ GIVEN("AVRCP volume")
+ {
+ WHEN("Volume is 127")
+ {
+ THEN("System volume is set to 10")
+ {
+ REQUIRE(audio::volume::scaler::toSystemVolume(127) == 10);
+ }
+ }
+ WHEN("Volume is 100")
+ {
+ THEN("System volume is set to 7")
+ {
+ REQUIRE(audio::volume::scaler::toSystemVolume(100) == 7);
+ }
+ }
+ WHEN("Volume is 89")
+ {
+ THEN("System volume is set to 7")
+ {
+ REQUIRE(audio::volume::scaler::toSystemVolume(89) == 7);
+ }
+ }
+ WHEN("Volume is 88")
+ {
+ THEN("System volume is set to 6")
+ {
+ REQUIRE(audio::volume::scaler::toSystemVolume(88) == 6);
+ }
+ }
+ WHEN("Volume is 13")
+ {
+ THEN("System volume is set to 1")
+ {
+ REQUIRE(audio::volume::scaler::toSystemVolume(13) == 1);
+ }
+ }
+ WHEN("Volume is 12")
+ {
+ THEN("System volume is set to 1")
+ {
+ REQUIRE(audio::volume::scaler::toSystemVolume(12) == 1);
+ }
+ }
+ WHEN("Volume is 0")
+ {
+ THEN("System volume is set to 0")
+ {
+ REQUIRE(audio::volume::scaler::toSystemVolume(0) == 0);
+ }
+ }
+ }
+
+ GIVEN("System volume")
+ {
+ WHEN("Volume is set to 10")
+ {
+ THEN("AVRCP volume is 127")
+ {
+ REQUIRE(audio::volume::scaler::toAvrcpVolume(10) == std::uint8_t{127});
+ }
+ }
+ WHEN("System volume is set to 7")
+ {
+ THEN("AVRCP volume is 89")
+ {
+ REQUIRE(audio::volume::scaler::toAvrcpVolume(7) == std::uint8_t{89});
+ }
+ }
+ WHEN("System volume is set to 1")
+ {
+ THEN("AVRCP volume is 13")
+ {
+ REQUIRE(audio::volume::scaler::toAvrcpVolume(1) == std::uint8_t{13});
+ }
+ }
+ }
+}
M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +1 -0
@@ 14,6 14,7 @@ set(SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioDeviceFactory.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioFormat.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioMux.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Audio/VolumeScaler.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/Decoder.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderMP3.cpp"
M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp +3 -3
@@ 5,6 5,7 @@
#include <Audio/AudioCommon.hpp>
#include <interface/profiles/A2DP/AVDTP.hpp>
+#include <module-audio/Audio/VolumeScaler.hpp>
#include <Audio/Stream.hpp>
@@ 45,9 46,8 @@ auto BluetoothAudioDevice::Stop() -> audio::AudioDevice::RetCode
auto BluetoothAudioDevice::OutputVolumeCtrl(float vol) -> audio::AudioDevice::RetCode
{
- constexpr auto avrcpMaxVolume = std::uint8_t{0x7F}; // from AVRCP documentation
- const auto volumeToSet = static_cast<std::uint8_t>((vol / audio::maxVolume) * avrcpMaxVolume);
- const auto status = avrcp_controller_set_absolute_volume(ctx->avrcp_cid, volumeToSet);
+ const auto volumeToSet = audio::volume::scaler::toAvrcpVolume(vol);
+ const auto status = avrcp_controller_set_absolute_volume(ctx->avrcp_cid, volumeToSet);
if (status != ERROR_CODE_SUCCESS) {
LOG_ERROR("Can't set volume level. Status %x", status);
return audio::AudioDevice::RetCode::Failure;
A module-bluetooth/doc/bt_volume_buttons_handling.puml => module-bluetooth/doc/bt_volume_buttons_handling.puml +22 -0
@@ 0,0 1,22 @@
+@startuml
+
+actor User
+participant "A2DP Sink" as sink
+participant "Source's AVRCP event handler" as source
+participant "Service Bluetooth" as bt
+participant "Service Audio" as audio
+participant "Settings" as settings
+participant "Application" as app
+
+
+User -> sink : Volume button pressed
+==A2DP==
+sink -> source : AVRCP volume changed
+== ==
+source -> bt : Volume changed message
+bt -> audio : Volume changed message
+audio -> settings : Set appropriate value
+audio -> app : Current volume changed
+app -> User : Show current volume level
+
+@enduml
A module-bluetooth/doc/bt_volume_buttons_handling.svg => module-bluetooth/doc/bt_volume_buttons_handling.svg +35 -0
@@ 0,0 1,35 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="468px" preserveAspectRatio="none" style="width:1087px;height:468px;" version="1.1" viewBox="0 0 1087 468" width="1087px" zoomAndPan="magnify"><defs><filter height="300%" id="f1ko217yxdo7w6" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="27" x2="27" y1="86.2969" y2="381.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="200.5" x2="200.5" y1="86.2969" y2="381.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="378" x2="378" y1="86.2969" y2="381.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="573" x2="573" y1="86.2969" y2="381.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="767.5" x2="767.5" y1="86.2969" y2="381.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="929.5" x2="929.5" y1="86.2969" y2="381.3594"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="1023.5" x2="1023.5" y1="86.2969" y2="381.3594"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="32" x="8" y="82.9951">User</text><ellipse cx="27" cy="13" fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" rx="8" ry="8" style="stroke: #A80036; stroke-width: 2.0;"/><path d="M27,21 L27,48 M14,29 L40,29 M27,48 L14,63 M27,48 L40,63 " fill="none" filter="url(#f1ko217yxdo7w6)" style="stroke: #A80036; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="32" x="8" y="393.3545">User</text><ellipse cx="27" cy="406.6563" fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" rx="8" ry="8" style="stroke: #A80036; stroke-width: 2.0;"/><path d="M27,414.6563 L27,441.6563 M14,422.6563 L40,422.6563 M27,441.6563 L14,456.6563 M27,441.6563 L40,456.6563 " fill="none" filter="url(#f1ko217yxdo7w6)" style="stroke: #A80036; stroke-width: 2.0;"/><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="85" x="156.5" y="51"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="71" x="163.5" y="70.9951">A2DP Sink</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="85" x="156.5" y="380.3594"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="71" x="163.5" y="400.3545">A2DP Sink</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="224" x="264" y="51"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="210" x="271" y="70.9951">Source's AVRCP event handler</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="224" x="264" y="380.3594"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="210" x="271" y="400.3545">Source's AVRCP event handler</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="136" x="503" y="51"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="122" x="510" y="70.9951">Service Bluetooth</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="136" x="503" y="380.3594"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="122" x="510" y="400.3545">Service Bluetooth</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="107" x="712.5" y="51"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="93" x="719.5" y="70.9951">Service Audio</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="107" x="712.5" y="380.3594"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="93" x="719.5" y="400.3545">Service Audio</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="71" x="892.5" y="51"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="57" x="899.5" y="70.9951">Settings</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="71" x="892.5" y="380.3594"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="57" x="899.5" y="400.3545">Settings</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="89" x="977.5" y="51"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="75" x="984.5" y="70.9951">Application</text><rect fill="#FEFECE" filter="url(#f1ko217yxdo7w6)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="89" x="977.5" y="380.3594"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="75" x="984.5" y="400.3545">Application</text><polygon fill="#A80036" points="189,113.4297,199,117.4297,189,121.4297,193,117.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="27" x2="195" y1="117.4297" y2="117.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="150" x="34" y="112.3638">Volume button pressed</text><rect fill="#EEEEEE" filter="url(#f1ko217yxdo7w6)" height="3" style="stroke: #EEEEEE; stroke-width: 1.0;" width="1072.5" x="3" y="145.9961"/><line style="stroke: #000000; stroke-width: 1.0;" x1="3" x2="1075.5" y1="145.9961" y2="145.9961"/><line style="stroke: #000000; stroke-width: 1.0;" x1="3" x2="1075.5" y1="148.9961" y2="148.9961"/><rect fill="#EEEEEE" filter="url(#f1ko217yxdo7w6)" height="23.1328" style="stroke: #000000; stroke-width: 2.0;" width="54" x="512.25" y="135.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="40" x="518.25" y="151.4966">A2DP</text><polygon fill="#A80036" points="366,185.6953,376,189.6953,366,193.6953,370,189.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="201" x2="372" y1="189.6953" y2="189.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="153" x="208" y="184.6294">AVRCP volume changed</text><rect fill="#EEEEEE" filter="url(#f1ko217yxdo7w6)" height="3" style="stroke: #EEEEEE; stroke-width: 1.0;" width="1072.5" x="3" y="210.6953"/><line style="stroke: #000000; stroke-width: 1.0;" x1="3" x2="1075.5" y1="210.6953" y2="210.6953"/><line style="stroke: #000000; stroke-width: 1.0;" x1="3" x2="1075.5" y1="213.6953" y2="213.6953"/><polygon fill="#A80036" points="561,242.8281,571,246.8281,561,250.8281,565,246.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="378" x2="567" y1="246.8281" y2="246.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="171" x="385" y="241.7622">Volume changed message</text><polygon fill="#A80036" points="756,271.9609,766,275.9609,756,279.9609,760,275.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="573" x2="762" y1="275.9609" y2="275.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="171" x="580" y="270.895">Volume changed message</text><polygon fill="#A80036" points="918,301.0938,928,305.0938,918,309.0938,922,305.0938" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="768" x2="924" y1="305.0938" y2="305.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="138" x="775" y="300.0278">Set appropriate value</text><polygon fill="#A80036" points="1012,330.2266,1022,334.2266,1012,338.2266,1016,334.2266" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="768" x2="1018" y1="334.2266" y2="334.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="158" x="775" y="329.1606">Current volume changed</text><polygon fill="#A80036" points="38,359.3594,28,363.3594,38,367.3594,34,363.3594" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="32" x2="1023" y1="363.3594" y2="363.3594"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="168" x="44" y="358.2935">Show current volume level</text><!--
+@startuml
+
+actor User
+participant "A2DP Sink" as sink
+participant "Source's AVRCP event handler" as source
+participant "Service Bluetooth" as bt
+participant "Service Audio" as audio
+participant "Settings" as settings
+participant "Application" as app
+
+
+User -> sink : Volume button pressed
+==A2DP==
+sink -> source : AVRCP volume changed
+== ==
+source -> bt : Volume changed message
+bt -> audio : Volume changed message
+audio -> settings : Set appropriate value
+audio -> app : Current volume changed
+app -> User : Show current volume level
+
+@enduml
+
+PlantUML version 1.2018.13(Mon Nov 26 18:11:51 CET 2018)
+(GPL source distribution)
+Java Runtime: OpenJDK Runtime Environment
+JVM: OpenJDK 64-Bit Server VM
+Java Version: 11.0.10+9-Ubuntu-0ubuntu1.20.04
+Operating System: Linux
+OS Version: 5.8.0-50-generic
+Default Encoding: UTF-8
+Language: en
+Country: US
+--></g></svg><
\ No newline at end of file
M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +3 -0
@@ 27,6 27,7 @@
#include <service-gui/Common.hpp>
#include <service-desktop/DesktopMessages.hpp>
#include <service-appmgr/StartupType.hpp>
+#include <module-services/service-audio/service-audio/AudioMessage.hpp>
#include <algorithm>
#include <limits>
@@ 137,6 138,7 @@ namespace app::manager
autoLockTimer = sys::TimerFactory::createSingleShotTimer(
this, timerBlock, sys::timer::InfiniteTimeout, [this](sys::Timer &) { onPhoneLocked(); });
bus.channels.push_back(sys::BusChannel::PhoneModeChanges);
+ bus.channels.push_back(sys::BusChannel::ServiceAudioNotifications);
registerMessageHandlers();
}
@@ 402,6 404,7 @@ namespace app::manager
connect(typeid(sys::TetheringQuestionRequest), convertibleToActionHandler);
connect(typeid(sys::TetheringQuestionAbort), convertibleToActionHandler);
connect(typeid(sys::TetheringPhoneModeChangeProhibitedMessage), convertibleToActionHandler);
+ connect(typeid(VolumeChanged), convertibleToActionHandler);
}
sys::ReturnCodes ApplicationManager::SwitchPowerModeHandler(const sys::ServicePowerMode mode)
M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +15 -11
@@ 7,6 7,7 @@
#include <Audio/Operation/IdleOperation.hpp>
#include <Audio/Operation/PlaybackOperation.hpp>
#include <Bluetooth/audio/BluetoothAudioDevice.hpp>
+#include <module-audio/Audio/VolumeScaler.hpp>
#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/ServiceBluetoothCommon.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>
@@ 453,7 454,7 @@ void ServiceAudio::HandleNotification(const AudioNotificationMessage::Type &type
}
}
-auto ServiceAudio::HandleKeyPressed(const int step) -> std::unique_ptr<AudioKeyPressedResponse>
+auto ServiceAudio::HandleKeyPressed(const int step) -> sys::MessagePointer
{
auto context = getCurrentContext();
@@ 464,12 465,10 @@ auto ServiceAudio::HandleKeyPressed(const int step) -> std::unique_ptr<AudioKeyP
// active system sounds can be only muted, no volume control is possible
if (step < 0) {
MuteCurrentOperation();
- return std::make_unique<AudioKeyPressedResponse>(
- audio::RetCode::Success, 0, AudioKeyPressedResponse::ShowPopup::False, context);
+ return sys::msgHandled();
}
else {
- return std::make_unique<AudioKeyPressedResponse>(
- audio::RetCode::Success, currentVolume, AudioKeyPressedResponse::ShowPopup::False, context);
+ return sys::msgHandled();
}
}
@@ 484,8 483,8 @@ auto ServiceAudio::HandleKeyPressed(const int step) -> std::unique_ptr<AudioKeyP
// update volume of currently active sound
setSetting(Setting::Volume, std::to_string(newVolume));
}
- return std::make_unique<AudioKeyPressedResponse>(
- audio::RetCode::Success, newVolume, AudioKeyPressedResponse::ShowPopup::True, context);
+ bus.sendMulticast(std::make_unique<VolumeChanged>(newVolume, context), sys::BusChannel::ServiceAudioNotifications);
+ return sys::msgHandled();
}
void ServiceAudio::MuteCurrentOperation()
@@ 509,7 508,7 @@ sys::MessagePointer ServiceAudio::DataReceivedHandler(sys::DataMessage *msgl, sy
{
sys::MessagePointer responseMsg;
const auto isBusy = IsBusy();
- auto &msgType = typeid(*msgl);
+ auto &msgType = typeid(*msgl);
if (msgType == typeid(AudioNotificationMessage)) {
auto *msg = static_cast<AudioNotificationMessage *>(msgl);
@@ 689,7 688,7 @@ void ServiceAudio::setSetting(const Setting &setting,
}
}
-const std::pair<audio::Profile::Type, audio::PlaybackType> ServiceAudio::getCurrentContext()
+const audio::Context ServiceAudio::getCurrentContext()
{
const auto activeInput = audioMux.GetActiveInput();
if (!activeInput.has_value()) {
@@ 715,7 714,12 @@ void ServiceAudio::settingsChanged(const std::string &name, std::string value)
}
auto ServiceAudio::handleVolumeChangedOnBluetoothDevice(sys::Message *msgl) -> sys::MessagePointer
{
- auto *msg = static_cast<BluetoothDeviceVolumeChanged *>(msgl);
- LOG_WARN("Volume chnged on bt device to %u. Handler to be done", msg->getVolume());
+ auto *msg = static_cast<BluetoothDeviceVolumeChanged *>(msgl);
+ const auto volume = volume::scaler::toSystemVolume(msg->getVolume());
+ const auto [profileType, playbackType] = getCurrentContext();
+ settingsProvider->setValue(dbPath(Setting::Volume, playbackType, profileType), std::to_string(volume));
+ settingsCache[dbPath(Setting::Volume, playbackType, profileType)] = std::to_string(volume);
+ bus.sendMulticast(std::make_unique<VolumeChanged>(volume, std::make_pair(profileType, playbackType)),
+ sys::BusChannel::ServiceAudioNotifications);
return sys::msgHandled();
}
M module-services/service-audio/service-audio/AudioMessage.hpp => module-services/service-audio/service-audio/AudioMessage.hpp +16 -14
@@ 6,6 6,10 @@
#include <Audio/AudioCommon.hpp>
#include <Audio/decoder/Decoder.hpp>
#include <MessageType.hpp>
+#include <service-appmgr/service-appmgr/messages/ActionRequest.hpp>
+#include <module-services/service-appmgr/service-appmgr/Actions.hpp>
+#include <module-apps/popups/data/PopupRequestParams.hpp>
+
#include <Service/Message.hpp>
#include <memory>
@@ 251,24 255,22 @@ class AudioKeyPressedRequest : public AudioMessage
const int step{};
};
-class AudioKeyPressedResponse : public sys::DataMessage
+class VolumeChanged : public sys::DataMessage, public app::manager::actions::ConvertibleToAction
{
public:
- enum class ShowPopup : bool
- {
- True,
- False
- };
- AudioKeyPressedResponse(audio::RetCode retCode,
- const audio::Volume &volume,
- const ShowPopup showPopup,
- const std::pair<audio::Profile::Type, audio::PlaybackType> &context)
- : sys::DataMessage(MessageType::AudioMessage), volume(volume), showPopup(showPopup), context(context)
+ VolumeChanged(audio::Volume volume, audio::Context context)
+ : sys::DataMessage{MessageType::MessageTypeUninitialized}, volume{volume}, context{context}
{}
- const audio::Volume volume{};
- const ShowPopup showPopup = ShowPopup::False;
- std::pair<audio::Profile::Type, audio::PlaybackType> context;
+ [[nodiscard]] auto toAction() const -> std::unique_ptr<app::manager::ActionRequest> override
+ {
+ return std::make_unique<app::manager::ActionRequest>(
+ sender, app::manager::actions::ShowPopup, std::make_unique<gui::VolumePopupRequestParams>(volume, context));
+ }
+
+ private:
+ const audio::Volume volume;
+ audio::Context context;
};
class BluetoothDeviceVolumeChanged : public AudioMessage
M module-services/service-audio/service-audio/AudioServiceAPI.hpp => module-services/service-audio/service-audio/AudioServiceAPI.hpp +1 -1
@@ 208,7 208,7 @@ namespace AudioServiceAPI
* @param serv - requesting service.
* @param step - step that will be added to current volume.
* @return True if request has been sent successfully, false otherwise
- * @note Response will come as message AudioKeyPressedResponse
+ * @note If volume popup should be shown then Application Manager is informed.
*/
bool KeyPressed(sys::Service *serv, const int step);
M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +2 -2
@@ 78,7 78,7 @@ class ServiceAudio : public sys::Service
auto HandleResume(const audio::Token &token) -> std::unique_ptr<AudioResponseMessage>;
auto HandleGetFileTags(const std::string &fileName) -> std::unique_ptr<AudioResponseMessage>;
void HandleNotification(const AudioNotificationMessage::Type &type, const audio::Token &token);
- auto HandleKeyPressed(const int step) -> std::unique_ptr<AudioKeyPressedResponse>;
+ auto HandleKeyPressed(const int step) -> sys::MessagePointer;
void MuteCurrentOperation();
void VibrationUpdate(const audio::PlaybackType &type = audio::PlaybackType::None,
std::optional<audio::AudioMux::Input *> input = std::nullopt);
@@ 106,7 106,7 @@ class ServiceAudio : public sys::Service
const audio::Profile::Type &profileType,
const audio::PlaybackType &playbackType);
- const std::pair<audio::Profile::Type, audio::PlaybackType> getCurrentContext();
+ const audio::Context getCurrentContext();
void settingsChanged(const std::string &name, std::string value);
auto handleVolumeChangedOnBluetoothDevice(sys::Message *msgl) -> sys::MessagePointer;
};