From 4d7cfc4e7abd6ee22e0965c0f520122194138916 Mon Sep 17 00:00:00 2001 From: Igor Olshevskyi Date: Thu, 25 May 2017 17:22:45 +0300 Subject: [PATCH] TizenRefApp-8592 [Call UI] Implement Call volume level controling feature Change-Id: Icf02b5475a5868a862b00606cd51a33378ac6671 --- edc/accessory.edc | 67 ------ edc/volume_control.edc | 98 ++++---- inc/model/ISoundManager.h | 13 +- inc/model/types.h | 1 + inc/presenters/AccessoryPresenter.h | 84 +++++++ inc/presenters/MainPage.h | 3 + inc/presenters/types.h | 2 + inc/resources.h | 2 + src/model/BluetoothManager.cpp | 150 ++++++++++++ src/model/BluetoothManager.h | 53 +++++ src/model/Call.cpp | 8 +- src/model/Call.h | 1 + src/model/CallClient.cpp | 2 +- src/model/SoundManager.cpp | 154 +++++++++++-- src/model/SoundManager.h | 33 ++- src/model/implTypes.h | 5 + src/presenters/AcceptRejectPresenter.cpp | 6 +- src/presenters/AccessoryPresenter.cpp | 282 +++++++++++++++++++++++ src/presenters/MainPage.cpp | 34 ++- src/presenters/common.h | 2 + src/resources.cpp | 2 + src/view/common.h | 1 - tizen-manifest.xml | 2 +- 23 files changed, 854 insertions(+), 151 deletions(-) create mode 100644 inc/presenters/AccessoryPresenter.h create mode 100644 src/model/BluetoothManager.cpp create mode 100644 src/model/BluetoothManager.h create mode 100644 src/presenters/AccessoryPresenter.cpp diff --git a/edc/accessory.edc b/edc/accessory.edc index 740a4e0..9579bbb 100644 --- a/edc/accessory.edc +++ b/edc/accessory.edc @@ -14,73 +14,6 @@ * limitations under the License. */ -/* -#define CU_BTN_VOLUME_EFF_DEFAULT_SIZE 160 160 -#define CU_BTN_VOLUME_EFF_PRESSED_SIZE 144 144 -#define CU_BTN_VOLUME_ICON_SIZE 64 64 - -#define CU_BTN_VOLUME_CONTROL(_name, _icon) - group { "elm/button/base/"_name; - images { - image: "w_call_button_press_circle.png" COMP; - image: _icon COMP; - } - parts { - image { "image.bg"; - nomouse; - scale; - desc { "default"; - fixed: 1 1; - min: CU_BTN_VOLUME_EFF_DEFAULT_SIZE; - image.normal: "w_call_button_press_circle.png"; - color: 255 255 255 0; - } - desc { "pressed_effect"; - inherit: "default"; - min: CU_BTN_VOLUME_EFF_PRESSED_SIZE; - color: 255 255 255 33; - } - desc { "pressed"; - inherit: "pressed_effect"; - min: CU_BTN_VOLUME_EFF_DEFAULT_SIZE; - } - } - image { "icon"; - scale; - desc { "default"; - min: CU_BTN_VOLUME_ICON_SIZE; - max: CU_BTN_VOLUME_ICON_SIZE; - image.normal: _icon; - } - } - } - programs { - program { - signal: "mouse,clicked,*"; - source: "icon"; - action: SIGNAL_EMIT "elm,action,click" ""; - } - program { - signal: "mouse,down,*"; - source: "icon"; - sequence { - action: STATE_SET "pressed_effect"; - target: "image.bg"; - action: STATE_SET "pressed"; - target: "image.bg"; - transition: TRANSITION_GLIDE(0.3); - } - } - program { - signal: "mouse,up,*"; - source: "icon"; - action: STATE_SET "default"; - target: "image.bg"; - transition: LINEAR 0.535; - } - } -*/ - group { "elm/layout/callui/accessory"; parts { swallow { "swl.volume_control" diff --git a/edc/volume_control.edc b/edc/volume_control.edc index 8c9632e..25ce08d 100644 --- a/edc/volume_control.edc +++ b/edc/volume_control.edc @@ -62,17 +62,17 @@ group { "elm/layout/callui/volume_control"; } } spacer { "pad.top"; - scale; - desc { "default"; + scale; + desc { "default"; min: CU_VOLUME_PAD_TOP_MIN; fixed: 0 1; rel1 { relative: 0.0 0.0; to: "sizer"; } rel2 { relative: 1.0 0.0; to: "sizer"; } align: 0.0 0.0; - } - } - spacer { "pad.bottom"; - scale; + } + } + spacer { "pad.bottom"; + scale; desc { "default"; min: CU_VOLUME_PAD_BOTTOM_MIN; fixed: 0 1; @@ -80,20 +80,20 @@ group { "elm/layout/callui/volume_control"; rel2 { relative: 1.0 1.0; to: "sizer"; } align: 0.0 1.0; } - } - spacer { "action_zone"; - scale; - desc { "default"; - min: CU_VOLUME_ACTION_ZONE_MIN; - max: CU_VOLUME_ACTION_ZONE_MIN; - fixed: 1 1; - rel1 { relative: 0.0 1.0; to_y: "pad.top"; } - rel2 { relative: 1.0 1.0; to_y: "pad.top"; } - align: 0.5 0.0; - } - } - spacer { "txt.value.pad.bottom"; - scale; + } + spacer { "action_zone"; + scale; + desc { "default"; + min: CU_VOLUME_ACTION_ZONE_MIN; + max: CU_VOLUME_ACTION_ZONE_MIN; + fixed: 1 1; + rel1 { relative: 0.0 1.0; to_y: "pad.top"; } + rel2 { relative: 1.0 1.0; to_y: "pad.top"; } + align: 0.5 0.0; + } + } + spacer { "txt.value.pad.bottom"; + scale; desc { "default"; min: CU_VOLUME_ACTION_BTN_MIN; fixed: 0 1; @@ -117,49 +117,49 @@ group { "elm/layout/callui/volume_control"; } } swallow { "swl.minus"; - scale; - desc { "default"; - min: CU_VOLUME_ACTION_BTN_SIZE; - max: CU_VOLUME_ACTION_BTN_SIZE; - fixed: 1 1; - rel1 { relative: 0.0 0.0; to: "action_zone"; } - rel2 { relative: 0.0 1.0; to: "action_zone"; } - align: 0.0 0.5; - } - desc { "hide"; + scale; + desc { "default"; + min: CU_VOLUME_ACTION_BTN_SIZE; + max: CU_VOLUME_ACTION_BTN_SIZE; + fixed: 1 1; + rel1 { relative: 0.0 0.0; to: "action_zone"; } + rel2 { relative: 0.0 1.0; to: "action_zone"; } + align: 0.0 0.5; + } + desc { "hide"; hid; } } swallow { "swl.plus"; - scale; - desc { "default"; - min: CU_VOLUME_ACTION_BTN_SIZE; - max: CU_VOLUME_ACTION_BTN_SIZE; - fixed: 1 1; - rel1 { relative: 1.0 0.0; to: "action_zone"; } - rel2 { relative: 1.0 1.0; to: "action_zone"; } - align: 1.0 0.5; - } - desc { "hide"; + scale; + desc { "default"; + min: CU_VOLUME_ACTION_BTN_SIZE; + max: CU_VOLUME_ACTION_BTN_SIZE; + fixed: 1 1; + rel1 { relative: 1.0 0.0; to: "action_zone"; } + rel2 { relative: 1.0 1.0; to: "action_zone"; } + align: 1.0 0.5; + } + desc { "hide"; hid; } - } + } spacer { "txt.info.zone"; scale; - desc { "default"; - min: CU_VOLUME_INFO_TXT_ZONE_MIN; - fixed: 1 1; - rel1 { relative: 0.5 0.0; to_y: "pad.bottom"; } + desc { "default"; + min: CU_VOLUME_INFO_TXT_ZONE_MIN; + fixed: 1 1; + rel1 { relative: 0.5 0.0; to_y: "pad.bottom"; } rel2 { relative: 0.5 0.0; to_y: "pad.bottom"; } align: 0.5 1.0; } } textblock { "txt.info"; scale; - desc { "default"; - min: CU_VOLUME_INFO_TXT_MIN; - fixed: 1 1; - rel1 { relative: 0.5 0.5; to_y: "txt.info.zone"; } + desc { "default"; + min: CU_VOLUME_INFO_TXT_MIN; + fixed: 1 1; + rel1 { relative: 0.5 0.5; to_y: "txt.info.zone"; } rel2 { relative: 0.5 0.5; to_y: "txt.info.zone"; } text.style: "volume_info"; } diff --git a/inc/model/ISoundManager.h b/inc/model/ISoundManager.h index 1ac209f..8114ae2 100644 --- a/inc/model/ISoundManager.h +++ b/inc/model/ISoundManager.h @@ -30,10 +30,15 @@ namespace callui { virtual bool getMuteState() const = 0; virtual ucl::Result startDtmf(const unsigned char dtmfDigit) = 0; virtual ucl::Result stopDtmf() = 0; - virtual void addAudioStateChangeHandler(AudioStateHandler handler) = 0; - virtual void removeAudioStateChangeHandler(AudioStateHandler handler) = 0; - virtual void addMuteStateChangeHandler(MuteStateHandler handler) = 0; - virtual void removeMuteStateChangeHandler(MuteStateHandler handler) = 0; + virtual void addAudioStateHandler(AudioStateHandler handler) = 0; + virtual void removeAudioStateHandler(AudioStateHandler handler) = 0; + virtual void addMuteStateHandler(MuteStateHandler handler) = 0; + virtual void removeMuteStateHandler(MuteStateHandler handler) = 0; + virtual void addVolumeStateHandler(VolumeLevelHandler handler) = 0; + virtual void removeVolumeStateHandler(VolumeLevelHandler handler) = 0; + virtual int getMaxVolume() const = 0; + virtual int getVolume() const = 0; + virtual ucl::Result setVolume(int value) = 0; }; } diff --git a/inc/model/types.h b/inc/model/types.h index 3d4b92c..e6b1f57 100644 --- a/inc/model/types.h +++ b/inc/model/types.h @@ -162,6 +162,7 @@ namespace callui { using AudioStateHandler = ucl::Delegate; using MuteStateHandler = ucl::Delegate; + using VolumeLevelHandler = ucl::Delegate; using ConfMemberList = std::vector; using RejectMsgList = std::vector; diff --git a/inc/presenters/AccessoryPresenter.h b/inc/presenters/AccessoryPresenter.h new file mode 100644 index 0000000..8d2a568 --- /dev/null +++ b/inc/presenters/AccessoryPresenter.h @@ -0,0 +1,84 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CALLUI_PRESENTERS_ACCESSORY_PRESENTER_H__ +#define __CALLUI_PRESENTERS_ACCESSORY_PRESENTER_H__ + +#include "Presenter.h" + +#include "ucl/gui/Layout.h" + +#include "types.h" + +namespace callui { + + class AccessoryPresenter final : public Presenter { + public: + class Builder { + public: + Builder(); + Builder &setSoundManager(const ISoundManagerSRef &sm); + AccessoryPresenterSRef build(ucl::ElmWidget &parent) const; + + private: + ISoundManagerSRef m_sm; + }; + + public: + virtual ~AccessoryPresenter(); + + ucl::Widget &getWidget(); + void hideVolumeControls(); + + private: + friend class ucl::RefCountObj; + AccessoryPresenter(ucl::RefCountObjBase &rc, + const ISoundManagerSRef &sm); + + ucl::Result prepare(ucl::ElmWidget &parent); + + ucl::Result createWidget(ucl::ElmWidget &parent); + ucl::Result createVolumeControl(); + void registerCallbacks(); + void unregisterCallbacks(); + void onVolumeControlEventCb(VolumeControlEvent event); + Eina_Bool onRotaryEvent(Eext_Rotary_Event_Info *info); + + void tryIncreaseVolume(); + void tryDecreaseVolume(); + + void onAudioStateChanged(AudioStateType state); + void onVolumeLevelChanged(int value); + + Eina_Bool onVCTimerCb(); + void startVCTimer(); + void restartVCTimer(); + void stopVCTimer(); + + void updateVolume(int value); + + private: + ucl::LayoutSRef m_widget; + VolumeControlSRef m_vc; + ISoundManagerSRef m_sm; + Ecore_Timer *m_vcTimer; + AudioStateType m_audioState; + }; +} + + + +#endif // __CALLUI_PRESENTERS_ACCESSORY_PRESENTER_H__ diff --git a/inc/presenters/MainPage.h b/inc/presenters/MainPage.h index 97fe7d9..1db58b1 100644 --- a/inc/presenters/MainPage.h +++ b/inc/presenters/MainPage.h @@ -61,6 +61,8 @@ namespace callui { ucl::Result createCallInfoPresenter(CallMode mode); + ucl::Result createAccessoryPresenter(); + ucl::Result createBottomBtn(const ucl::ElmStyle &style); void onBottomBtnClicked(ucl::Widget &widget, void *eventInfo); @@ -95,6 +97,7 @@ namespace callui { CallInfoPresenterSRef m_callInfoPrs; AcceptRejectPresenterSRef m_acceptRejectPrs; RejectMsgPresenterSRef m_rmPrs; + AccessoryPresenterSRef m_accessoryPrs; CallMode m_mode; Ecore_Timer *m_ecTimer; bool m_ecTimerBtnReq; diff --git a/inc/presenters/types.h b/inc/presenters/types.h index 182b515..e2a558d 100644 --- a/inc/presenters/types.h +++ b/inc/presenters/types.h @@ -56,6 +56,8 @@ namespace callui { UCL_DECLARE_REF_ALIASES(RejectMsgPresenter); + UCL_DECLARE_REF_ALIASES(AccessoryPresenter); + using AcceptDialogHandler = ucl::WeakDelegate; using RejectMsgStateHandler = ucl::WeakDelegate; using RejectMsgSelectHandler = ucl::WeakDelegate; diff --git a/inc/resources.h b/inc/resources.h index 84f9354..4c24744 100644 --- a/inc/resources.h +++ b/inc/resources.h @@ -42,6 +42,8 @@ namespace callui { extern const ucl::TString STR_CALL_ENDED; extern const ucl::TString STR_DECLINE_MESSAGES; + + extern const ucl::TString STR_VOLUME; } #endif // __CALLUI_RESOURCES_H__ diff --git a/src/model/BluetoothManager.cpp b/src/model/BluetoothManager.cpp new file mode 100644 index 0000000..0e8dee7 --- /dev/null +++ b/src/model/BluetoothManager.cpp @@ -0,0 +1,150 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BluetoothManager.h" + +#include +#include +#include +#include + +#include "common.h" + +namespace callui { namespace { namespace impl { + + using namespace ucl; + + constexpr auto BT_VOLUME_MAX = 15; + +}}} + +namespace callui { + + using namespace ucl; + + BluetoothManagerSRef BluetoothManager::newInstance() + { + auto result = makeShared(); + FAIL_RETURN_VALUE(result->prepare(), {}, "result->prepare() failed!"); + return result; + } + + BluetoothManager::BluetoothManager(): + m_btInitialized(false), + m_btAudioInitialized(false) + { + } + + BluetoothManager::~BluetoothManager() + { + unregisterAudioHandling(); + + bt_deinitialize(); + } + + Result BluetoothManager::prepare() + { + if (BT_ERROR_NONE != bt_initialize()) { + LOG_RETURN(RES_FAIL, "BT initialize failed"); + } + m_btInitialized = true; + + FAIL_RETURN(registerAudioHandling(), + "registerAudioHandling() failed"); + + return RES_OK; + } + + int BluetoothManager::getVolume() + { + auto vol = 0; + auto ret = bt_ag_get_speaker_gain(&vol); + if (ret != BT_ERROR_NONE) { + LOG_RETURN_VALUE(RES_FAIL, -1, "bt_ag_get_speaker_gain() failed!"); + } + DLOG("BT Volume level [%d]", vol); + return vol; + } + + int BluetoothManager::getMaxVolume() + { + return impl::BT_VOLUME_MAX; + } + + Result BluetoothManager::setVolume(int volume) + { + auto ret = bt_ag_notify_speaker_gain(volume); + if (ret != BT_ERROR_NONE) { + LOG_RETURN(RES_FAIL, "bt_ag_notify_speaker_gain() failed!"); + } + return RES_OK; + } + + void BluetoothManager::setVolumeStateHandler( + const BluetoothVolumeHandler &handler) + { + m_handler = handler; + } + + Result BluetoothManager::registerAudioHandling() + { + auto ret = bt_audio_initialize(); + if (ret != BT_ERROR_NONE) { + LOG_RETURN(RES_FAIL, "bt_audio_initialize() failed"); + } + m_btAudioInitialized = true; + + ret = bt_ag_set_speaker_gain_changed_cb( + CALLBACK_B(BluetoothManager::onVolumeChanged), this); + if (ret != BT_ERROR_NONE) { + LOG_RETURN(RES_FAIL, "bt_ag_set_speaker_gain_changed_cb() failed"); + } + + return RES_OK; + } + + void BluetoothManager::unregisterAudioHandling() + { + bt_ag_unset_speaker_gain_changed_cb(); + + if (m_btAudioInitialized) { + bt_audio_deinitialize(); + m_btAudioInitialized = false; + } + } + + void BluetoothManager::onVolumeChanged(int volume) + { + sound_type_e soundType = SOUND_TYPE_SYSTEM; + auto ret = sound_manager_get_current_sound_type(&soundType); + if (ret != SOUND_MANAGER_ERROR_NONE) { + LOG_RETURN_VOID(RES_FAIL, "sound_manager_get_current_sound_type() failed"); + } + + bool isSCOOpened = false; + ret = bt_ag_is_sco_opened(&isSCOOpened); + if (ret != BT_ERROR_NONE) { + LOG_RETURN_VOID(RES_FAIL, "sound_manager_get_current_sound_type() failed"); + } + + if (isSCOOpened && soundType == SOUND_TYPE_CALL) { + if (m_handler) { + m_handler(volume); + } + } + } + +} diff --git a/src/model/BluetoothManager.h b/src/model/BluetoothManager.h new file mode 100644 index 0000000..25b41ee --- /dev/null +++ b/src/model/BluetoothManager.h @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CALLUI_MODEL_BLUETOOTH_MANAGER_H__ +#define __CALLUI_MODEL_BLUETOOTH_MANAGER_H__ + +#include "implTypes.h" + +namespace callui { + + class BluetoothManager final { + public: + static BluetoothManagerSRef newInstance(); + virtual ~BluetoothManager(); + + int getVolume(); + int getMaxVolume(); + ucl::Result setVolume(int volume); + + void setVolumeStateHandler(const BluetoothVolumeHandler &handler); + + private: + friend class ucl::RefCountObj; + BluetoothManager(); + + ucl::Result prepare(); + + ucl::Result registerAudioHandling(); + void unregisterAudioHandling(); + void onVolumeChanged(int volume); + + private: + BluetoothVolumeHandler m_handler; + bool m_btInitialized; + bool m_btAudioInitialized; + }; + +} + +#endif // __CALLUI_MODEL_BLUETOOTH_MANAGER_H__ diff --git a/src/model/Call.cpp b/src/model/Call.cpp index a54f03a..7d54f7b 100644 --- a/src/model/Call.cpp +++ b/src/model/Call.cpp @@ -34,6 +34,7 @@ #include "BatteryStateSource.h" #include "SimSlotStateSource.h" #include "HdVoiceStateSource.h" +#include "BluetoothManager.h" #include "common.h" @@ -127,6 +128,11 @@ namespace callui { Result Call::prepare() { + m_btManager = BluetoothManager::newInstance(); + if (!m_btManager) { + ELOG("BluetoothManager::newInstance() failed!"); + } + auto callClient = CallClient::newInstance(); if (!callClient) { LOG_RETURN(RES_FAIL, "Client::newInstance() failed!"); @@ -137,7 +143,7 @@ namespace callui { LOG_RETURN(RES_FAIL, "CallManager::newInstance() failed!"); } - m_soundManager = SoundManager::newInstance(callClient); + m_soundManager = SoundManager::newInstance(callClient, m_btManager); if (!m_soundManager) { LOG_RETURN(RES_FAIL, "SoundManage::newInstance() failed!"); } diff --git a/src/model/Call.h b/src/model/Call.h index ff5a406..08429c0 100644 --- a/src/model/Call.h +++ b/src/model/Call.h @@ -67,6 +67,7 @@ namespace callui { private: CallManagerSRef m_callManager; SoundManagerSRef m_soundManager; + BluetoothManagerSRef m_btManager; ICallListenerWRef m_listener; SimSlotStateSourceSRef m_simSlotStSrc; HdVoiceStateSourceSRef m_hdCallStSrc; diff --git a/src/model/CallClient.cpp b/src/model/CallClient.cpp index 34b4b0d..f496bc2 100644 --- a/src/model/CallClient.cpp +++ b/src/model/CallClient.cpp @@ -44,7 +44,7 @@ namespace callui { ucl::Result CallClient::prepare() { cm_client_h client; - FAIL_RETURN_VALUE(convertCMResult(cm_init(&client)), {}, "cm_init() failed!"); + FAIL_RETURN(convertCMResult(cm_init(&client)), "cm_init() failed!"); m_client = client; return RES_OK; diff --git a/src/model/SoundManager.cpp b/src/model/SoundManager.cpp index ce60020..b7bc96f 100644 --- a/src/model/SoundManager.cpp +++ b/src/model/SoundManager.cpp @@ -17,6 +17,7 @@ #include "SoundManager.h" #include "CallClient.h" +#include "BluetoothManager.h" #include "common.h" @@ -24,20 +25,29 @@ namespace callui { using namespace ucl; - SoundManager::SoundManager(const CallClientSRef &client): - m_client(client) + SoundManager::SoundManager(RefCountObjBase &rc, + const CallClientSRef &client, + const BluetoothManagerSRef &btManager): + RefCountAware(&rc), + m_client(client), + m_btManager(btManager), + m_deviceVolumeCbID(-1) { } SoundManager::~SoundManager() { + if (m_deviceVolumeCbID >= 0) { + sound_manager_remove_volume_changed_cb(m_deviceVolumeCbID); + } cm_unset_audio_state_changed_cb(*m_client); cm_unset_mute_status_cb(*m_client); } - SoundManagerSRef SoundManager::newInstance(const CallClientSRef &client) + SoundManagerSRef SoundManager::newInstance(const CallClientSRef &client, + const BluetoothManagerSRef &btManager) { - auto result = makeShared(client); + auto result = makeShared(client, btManager); FAIL_RETURN_VALUE(result->prepare(), {}, "result->prepare() failed!"); return result; } @@ -48,7 +58,6 @@ namespace callui { ILOG("Ignore. Unhandled state [%d]", state); return; } - m_audioStateEvent.dispatch(convertCMAudioState(state)); } @@ -59,15 +68,40 @@ namespace callui { Result SoundManager::prepare() { - Result res = convertCMResult(cm_set_audio_state_changed_cb(*m_client, CALLBACK_B(SoundManager::audioStateChangedCb), this)); + Result res = convertCMResult(cm_set_audio_state_changed_cb(*m_client, + CALLBACK_B(SoundManager::audioStateChangedCb), this)); FAIL_RETURN(res, "cm_set_audio_state_changed_cb() failed!"); - res = convertCMResult(cm_set_mute_status_cb(*m_client, CALLBACK_B(SoundManager::muteStateChangedCb), this)); - FAIL_RETURN(res, "__callui_mute_state_changed_cb() failed!"); + res = convertCMResult(cm_set_mute_status_cb(*m_client, + CALLBACK_B(SoundManager::muteStateChangedCb), this)); + FAIL_RETURN(res, "cm_set_mute_status_cb() failed!"); + + FAIL_RETURN(registerVolumeCallbacks(), + "registerVolumeCallbacks() failed!"); return res; } + Result SoundManager::registerVolumeCallbacks() + { + int ret = sound_manager_add_volume_changed_cb( + CALLBACK_B(SoundManager::onDeviceVolumeChanged), + this, + &m_deviceVolumeCbID); + if (ret != SOUND_MANAGER_ERROR_NONE) { + LOG_RETURN(RES_FAIL, + "sound_manager_add_volume_changed_cb() failed"); + } + + if (m_btManager) { + m_btManager->setVolumeStateHandler( + WEAK_DELEGATE(SoundManager::onBluetoothVolumeChanged, + asWeak(*this))); + } + + return RES_OK; + } + Result SoundManager::setSpeakerState(bool isEnable) { if (isEnable) { @@ -90,7 +124,8 @@ namespace callui { { cm_audio_state_type_e state = CM_AUDIO_STATE_NONE_E; Result res = convertCMResult(cm_get_audio_state(*m_client, &state)); - FAIL_RETURN_VALUE(res, AudioStateType::NONE, "cm_get_audio_state() failed!"); + FAIL_RETURN_VALUE(res, AudioStateType::NONE, + "cm_get_audio_state() failed!"); return convertCMAudioState(state); } @@ -119,24 +154,117 @@ namespace callui { return convertCMResult(cm_stop_dtmf(*m_client)); } - void SoundManager::addAudioStateChangeHandler(AudioStateHandler handler) + void SoundManager::addAudioStateHandler(AudioStateHandler handler) { m_audioStateEvent += handler; } - void SoundManager::removeAudioStateChangeHandler(AudioStateHandler handler) + void SoundManager::removeAudioStateHandler(AudioStateHandler handler) { m_audioStateEvent -= handler; } - void SoundManager::addMuteStateChangeHandler(MuteStateHandler handler) + void SoundManager::addMuteStateHandler(MuteStateHandler handler) { m_muteStateEvent += handler; } - void SoundManager::removeMuteStateChangeHandler(MuteStateHandler handler) + void SoundManager::removeMuteStateHandler(MuteStateHandler handler) { m_muteStateEvent -= handler; } + void SoundManager::addVolumeStateHandler( + VolumeLevelHandler handler) + { + m_volumeLevelEvent += handler; + } + + void SoundManager::removeVolumeStateHandler( + VolumeLevelHandler handler) + { + m_volumeLevelEvent -= handler; + } + + int SoundManager::getMaxVolume() const + { + int maxVol = 0; + if (getAudioState() == AudioStateType::BT) { + if (m_btManager) { + maxVol = m_btManager->getMaxVolume(); + } else { + ELOG("BT is not set"); + } + } else { + auto ret = sound_manager_get_max_volume(SOUND_TYPE_CALL, &maxVol); + if (ret != SOUND_MANAGER_ERROR_NONE) { + LOG_RETURN_VALUE(RES_FAIL, 0, + "Get max volume failed. ret[%d]", ret); + } + } + DLOG("Max volume [%d]", maxVol); + return maxVol; + } + + int SoundManager::getVolume() const + { + int vol = 0; + if (getAudioState() == AudioStateType::BT) { + if (m_btManager) { + vol = m_btManager->getVolume(); + } else { + ELOG("BT is not set"); + } + } else { + auto ret = sound_manager_get_volume(SOUND_TYPE_CALL, &vol); + if (ret != SOUND_MANAGER_ERROR_NONE) { + LOG_RETURN_VALUE(RES_FAIL, 0, + "Get volume failed. ret[%d]", ret); + } + } + DLOG("Current volume [%d]", vol); + return vol; + } + + Result SoundManager::setVolume(int value) + { + if (getAudioState() == AudioStateType::BT) { + if (m_btManager) { + return m_btManager->setVolume(value); + } else { + LOG_RETURN(RES_FAIL, "BT is not set"); + } + } else { + auto ret = sound_manager_set_volume(SOUND_TYPE_CALL, value); + if (ret != SOUND_MANAGER_ERROR_NONE) { + LOG_RETURN(RES_FAIL, + "sound_manager_set_volume() failed. ret[%d]", ret); + } + } + return RES_OK; + } + + + void SoundManager::onDeviceVolumeChanged(sound_type_e type, unsigned int volume) + { + DLOG("Volume [%d]", volume); + if (type != SOUND_TYPE_CALL) { + ILOG("Ignored. Not type Call."); + return; + } + + if (getAudioState() != AudioStateType::BT) { + m_volumeLevelEvent.dispatch(volume); + } + } + + void SoundManager::onBluetoothVolumeChanged(int volume) + { + DLOG("Volume [%d]", volume); + + if (getAudioState() == AudioStateType::BT) { + m_volumeLevelEvent.dispatch(volume); + } + } + } diff --git a/src/model/SoundManager.h b/src/model/SoundManager.h index b027744..2e1766a 100644 --- a/src/model/SoundManager.h +++ b/src/model/SoundManager.h @@ -18,6 +18,7 @@ #define __CALLUI_MODEL_SOUND_MANAGER_H__ #include +#include #include "model/ISoundManager.h" @@ -25,9 +26,12 @@ namespace callui { - class SoundManager final : public ISoundManager { + class SoundManager final : + public ucl::RefCountAware, + public ISoundManager { public: - static SoundManagerSRef newInstance(const CallClientSRef &client); + static SoundManagerSRef newInstance(const CallClientSRef &client, + const BluetoothManagerSRef &btManager); virtual ~SoundManager(); // ISoundManager @@ -39,23 +43,38 @@ namespace callui { virtual bool getMuteState() const override final; virtual ucl::Result startDtmf(const unsigned char dtmfDigit) override final; virtual ucl::Result stopDtmf() override final; - virtual void addAudioStateChangeHandler(AudioStateHandler handler) override final; - virtual void removeAudioStateChangeHandler(AudioStateHandler handler) override final; - virtual void addMuteStateChangeHandler(MuteStateHandler handler) override final; - virtual void removeMuteStateChangeHandler(MuteStateHandler handler) override final; + virtual void addAudioStateHandler(AudioStateHandler handler) override final; + virtual void removeAudioStateHandler(AudioStateHandler handler) override final; + virtual void addMuteStateHandler(MuteStateHandler handler) override final; + virtual void removeMuteStateHandler(MuteStateHandler handler) override final; + virtual void addVolumeStateHandler(VolumeLevelHandler handler) override final; + virtual void removeVolumeStateHandler(VolumeLevelHandler handler) override final; + virtual int getMaxVolume() const override final; + virtual int getVolume() const override final; + virtual ucl::Result setVolume(int value) override final; private: friend class ucl::RefCountObj; - SoundManager(const CallClientSRef &client); + SoundManager(ucl::RefCountObjBase &rc, + const CallClientSRef &client, + const BluetoothManagerSRef &btManager); ucl::Result prepare(); + ucl::Result registerVolumeCallbacks(); + void audioStateChangedCb(cm_audio_state_type_e state); void muteStateChangedCb(cm_mute_status_e status); + void onBluetoothVolumeChanged(int volume); + void onDeviceVolumeChanged(sound_type_e type, unsigned int volume); + private: CallClientSRef m_client; + BluetoothManagerSRef m_btManager; AudioStateEvent m_audioStateEvent; MuteStateEvent m_muteStateEvent; + VolumeLevelEvent m_volumeLevelEvent; + int m_deviceVolumeCbID; }; } diff --git a/src/model/implTypes.h b/src/model/implTypes.h index 952af96..ad3e1ef 100644 --- a/src/model/implTypes.h +++ b/src/model/implTypes.h @@ -67,10 +67,15 @@ namespace callui { UCL_DECLARE_REF_ALIASES(SimSlotStateSource); UCL_DECLARE_REF_ALIASES(HdVoiceStateSource); + UCL_DECLARE_REF_ALIASES(BluetoothManager); + using AudioStateEvent = ucl::Event; using MuteStateEvent = ucl::Event; + using VolumeLevelEvent = ucl::Event; using StateChangeHandler = ucl::WeakDelegate; + + using BluetoothVolumeHandler = ucl::WeakDelegate; } #endif // __CALLUI_MODEL_IMPL_TYPES_H__ diff --git a/src/presenters/AcceptRejectPresenter.cpp b/src/presenters/AcceptRejectPresenter.cpp index 93c18fa..318b897 100644 --- a/src/presenters/AcceptRejectPresenter.cpp +++ b/src/presenters/AcceptRejectPresenter.cpp @@ -57,7 +57,7 @@ namespace callui { // IncomingCallPresenter::Builder AcceptRejectPresenter::Builder::Builder(): - m_callMask(0) + m_callMask(CALL_FLAG_NONE) { } @@ -101,8 +101,8 @@ namespace callui { m_popup->dispose(); } - delRotaryEventHandler(CALLBACK_A( - AcceptRejectPresenter::onRotaryEvent), this); + delRotaryEventHandler( + CALLBACK_A(AcceptRejectPresenter::onRotaryEvent), this); } Result AcceptRejectPresenter::prepare(ElmWidget &parent) diff --git a/src/presenters/AccessoryPresenter.cpp b/src/presenters/AccessoryPresenter.cpp new file mode 100644 index 0000000..8865444 --- /dev/null +++ b/src/presenters/AccessoryPresenter.cpp @@ -0,0 +1,282 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "presenters/AccessoryPresenter.h" + +#include "model/ISoundManager.h" +#include "view/VolumeControl.h" +#include "resources.h" +#include "common.h" + +namespace callui { namespace { namespace impl { + + using namespace ucl; + + constexpr auto CALL_VC_TIMER_INTERVAL = 1.5; + constexpr auto VOLUME_LEVEL_MIN = 1; + + constexpr LayoutTheme LAYOUT_ACCESSORY_WIDGET + {"layout", "callui", "accessory"}; + + constexpr EdjePart PART_SWL_VOLUME_CONTROL {"swl.volume_control"}; + +}}} + +namespace callui { + + using namespace ucl; + + AccessoryPresenter::Builder::Builder() + { + } + + AccessoryPresenter::Builder &AccessoryPresenter::Builder::setSoundManager(const ISoundManagerSRef &sm) + { + m_sm = sm; + return *this; + } + + AccessoryPresenterSRef AccessoryPresenter::Builder::build(ElmWidget &parent) const + { + if (!m_sm) { + LOG_RETURN_VALUE(RES_FAIL, {}, "Sound manager is NULL"); + } + + auto result = makeShared(m_sm); + FAIL_RETURN_VALUE(result->prepare(parent), {}, "result->prepare() failed!"); + return result; + } + + AccessoryPresenter::AccessoryPresenter(RefCountObjBase &rc, + const ISoundManagerSRef &sm): + Presenter(rc), + m_sm(sm), + m_vcTimer(nullptr), + m_audioState(m_sm->getAudioState()) + { + + } + + AccessoryPresenter::~AccessoryPresenter() + { + stopVCTimer(); + unregisterCallbacks(); + } + + Result AccessoryPresenter::prepare(ElmWidget &parent) + { + FAIL_RETURN(Presenter::prepare(parent), "Presenter::prepare() failed"); + + FAIL_RETURN(createWidget(parent), "createWidget() failed"); + + FAIL_RETURN(createVolumeControl(), "createVolumeControl() failed"); + + registerCallbacks(); + + return RES_OK; + } + + Widget &AccessoryPresenter::getWidget() + { + return *m_widget; + } + + void AccessoryPresenter::hideVolumeControls() + { + stopVCTimer(); + m_vc->hideControls(); + } + + Result AccessoryPresenter::createWidget(ElmWidget &parent) + { + m_widget = Layout::Builder(). + setTheme(impl::LAYOUT_ACCESSORY_WIDGET). + setIsOwner(true). + build(parent); + if (!m_widget) { + LOG_RETURN(RES_FAIL, "Layout::build() failed!"); + } + + return RES_OK; + } + + Result AccessoryPresenter::createVolumeControl() + { + m_vc = VolumeControl::Builder(). + setInfoText(STR_VOLUME). + setMaxSliderValue(m_sm->getMaxVolume()). + setShowControls(false). + setEventHandler(WEAK_DELEGATE( + AccessoryPresenter::onVolumeControlEventCb, + asWeak(*this))). + build(*m_widget); + if (!m_vc) { + LOG_RETURN(RES_FAIL, "VolumeControl::build() failed"); + } + updateVolume(m_sm->getVolume()); + + m_widget->setContent(*m_vc, impl::PART_SWL_VOLUME_CONTROL); + + return RES_OK; + } + + void AccessoryPresenter::registerCallbacks() + { + addRotaryEventHandler(CALLBACK_A( + AccessoryPresenter::onRotaryEvent), this); + + m_sm->addAudioStateHandler(DELEGATE( + AccessoryPresenter::onAudioStateChanged, this)); + + m_sm->addVolumeStateHandler(DELEGATE( + AccessoryPresenter::onVolumeLevelChanged, this)); + } + + void AccessoryPresenter::unregisterCallbacks() + { + delRotaryEventHandler( + CALLBACK_A(AccessoryPresenter::onRotaryEvent), this); + + m_sm->removeAudioStateHandler(DELEGATE( + AccessoryPresenter::onAudioStateChanged, this)); + + m_sm->removeVolumeStateHandler(DELEGATE( + AccessoryPresenter::onVolumeLevelChanged, this)); + } + + Eina_Bool AccessoryPresenter::onVCTimerCb() + { + m_vc->hideControls(); + m_vcTimer = nullptr; + + return ECORE_CALLBACK_CANCEL; + } + + void AccessoryPresenter::startVCTimer() + { + stopVCTimer(); + + m_vcTimer = ecore_timer_add(impl::CALL_VC_TIMER_INTERVAL, + CALLBACK_B(AccessoryPresenter::onVCTimerCb), + this); + } + + void AccessoryPresenter::restartVCTimer() + { + if (m_vcTimer) { + ecore_timer_reset(m_vcTimer); + } + } + + void AccessoryPresenter::stopVCTimer() + { + if (m_vcTimer) { + ecore_timer_del(m_vcTimer); + m_vcTimer = nullptr; + } + } + + Eina_Bool AccessoryPresenter::onRotaryEvent(Eext_Rotary_Event_Info *info) + { + if (m_vcTimer) { + restartVCTimer(); + } else { + m_vc->showControls(); + startVCTimer(); + } + + if (info->direction == EEXT_ROTARY_DIRECTION_CLOCKWISE) { + tryIncreaseVolume(); + } else { + tryDecreaseVolume(); + } + + return EINA_TRUE; + } + + void AccessoryPresenter::onVolumeControlEventCb(VolumeControlEvent event) + { + restartVCTimer(); + + switch (event) { + case VolumeControlEvent::INCREASE: + tryIncreaseVolume(); + break; + case VolumeControlEvent::DECREASE: + tryDecreaseVolume(); + break; + default: + break; + } + } + + void AccessoryPresenter::tryIncreaseVolume() + { + auto max = m_sm->getMaxVolume(); + auto cur = m_sm->getVolume(); + + if (max != cur) { + m_sm->setVolume(cur + 1); + } + } + + void AccessoryPresenter::tryDecreaseVolume() + { + auto cur = m_sm->getVolume(); + + if (cur - 1 >= impl::VOLUME_LEVEL_MIN) { + m_sm->setVolume(cur - 1); + } + } + + void AccessoryPresenter::onAudioStateChanged(AudioStateType state) + { + if ((m_audioState != AudioStateType::BT && + state == AudioStateType::BT) || + (m_audioState == AudioStateType::BT && + state != AudioStateType::BT)) { + m_audioState = state; + m_vc->setSliderValue(0); + m_vc->setMaxSliderValue(m_sm->getMaxVolume()); + updateVolume(m_sm->getVolume()); + } + } + + void AccessoryPresenter::updateVolume(int value) + { + m_vc->setSliderValue(value); + + auto max = m_sm->getMaxVolume(); + auto cur = m_sm->getVolume(); + + if (cur == max) { + m_vc->setIncreaseBtnEnable(false); + m_vc->setDecreaseBtnEnable(true); + } else if (cur <= impl::VOLUME_LEVEL_MIN) { + m_vc->setIncreaseBtnEnable(true); + m_vc->setDecreaseBtnEnable(false); + } else { + m_vc->setIncreaseBtnEnable(true); + m_vc->setDecreaseBtnEnable(true); + } + } + + void AccessoryPresenter::onVolumeLevelChanged(int value) + { + updateVolume(value); + } +} + diff --git a/src/presenters/MainPage.cpp b/src/presenters/MainPage.cpp index fc1b848..97973ac 100644 --- a/src/presenters/MainPage.cpp +++ b/src/presenters/MainPage.cpp @@ -34,6 +34,7 @@ #include "presenters/AcceptRejectPresenter.h" #include "presenters/CallInfoPresenter.h" #include "presenters/RejectMsgPresenter.h" +#include "presenters/AccessoryPresenter.h" #include "resources.h" #include "common.h" @@ -62,8 +63,6 @@ namespace callui { namespace { namespace impl { constexpr ElmStyle STYLE_BB_END_CALL {"callui/end_call"}; constexpr ElmStyle STYLE_BB_RECALL {"callui/call_back"}; - - constexpr SmartEvent EVENT_CLICKED {"clicked"}; }}} namespace callui { @@ -217,6 +216,9 @@ namespace callui { requestExit(); break; default: + if (m_accessoryPrs) { + m_accessoryPrs->hideVolumeControls(); + } break; } } @@ -351,7 +353,7 @@ namespace callui { elm_button_add(*m_widget), true); m_bottomBtn->setStyle(style); - m_bottomBtn->addEventHandler(impl::EVENT_CLICKED, + m_bottomBtn->addEventHandler(BTN_CLICKED, WEAK_DELEGATE(MainPage::onBottomBtnClicked, asWeak(*this))); @@ -406,6 +408,7 @@ namespace callui { m_bottomBtn.reset(); if (m_mode == CallMode::INCOMING) { + m_accessoryPrs.reset(); FAIL_RETURN_VOID(processIncomingCall(), "processIncomingCall() failed!"); } else { @@ -414,8 +417,10 @@ namespace callui { m_rmLy.reset(); if (m_mode == CallMode::END) { + m_accessoryPrs.reset(); startEndCallTimer(); } else { + createAccessoryPresenter(); createBottomBtn(impl::STYLE_BB_END_CALL); } } @@ -424,6 +429,27 @@ namespace callui { "createCallInfoPresenter() failed!"); } + Result MainPage::createAccessoryPresenter() + { + if (m_accessoryPrs) { + return RES_OK; + } + + m_accessoryPrs = AccessoryPresenter::Builder(). + setSoundManager(m_call->getSoundManager()). + build(*m_widget); + + if (!m_accessoryPrs) { + LOG_RETURN(RES_FAIL, + "AccessoryPresenter::build() failed!"); + } + + m_widget->setContent(m_accessoryPrs->getWidget().getEo(), + impl::PART_SWL_OVERLAY); + + return RES_OK; + } + Result MainPage::createAcceptRejectPresenter() { if (m_acceptRejectPrs) { @@ -438,7 +464,7 @@ namespace callui { if (!m_acceptRejectPrs) { LOG_RETURN(RES_FAIL, - "AcceptRejectPresenter::Builder().build() failed!"); + "AcceptRejectPresenter::build() failed!"); } m_widget->setContent(m_acceptRejectPrs->getWidget().getEo(), diff --git a/src/presenters/common.h b/src/presenters/common.h index 2a17171..18bd36f 100644 --- a/src/presenters/common.h +++ b/src/presenters/common.h @@ -23,4 +23,6 @@ #include "../common.h" +#include "../view/common.h" + #endif // __CALLUI_PRESENTERS_COMMON_H__ diff --git a/src/resources.cpp b/src/resources.cpp index 285e48a..6b666d9 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -37,4 +37,6 @@ namespace callui { const ucl::TString STR_CALL_ENDED {"Call ended"}; const ucl::TString STR_DECLINE_MESSAGES {"Decline messages"}; + + const ucl::TString STR_VOLUME {"Volume"}; } diff --git a/src/view/common.h b/src/view/common.h index bc05de3..68cc4c4 100644 --- a/src/view/common.h +++ b/src/view/common.h @@ -27,7 +27,6 @@ namespace callui { constexpr ucl::SmartEvent BTN_CLICKED {"clicked"}; - constexpr ucl::SmartEvent POPUP_DELETE {"delete"}; } #endif // __CALLUI_VIEW_COMMON_H__ diff --git a/tizen-manifest.xml b/tizen-manifest.xml index b972fd7..d8c27cb 100644 --- a/tizen-manifest.xml +++ b/tizen-manifest.xml @@ -14,11 +14,11 @@ http://tizen.org/privilege/telephony http://tizen.org/privilege/telephony.admin - http://tizen.org/privilege/keygrab http://tizen.org/privilege/appmanager.launch http://tizen.org/privilege/window.priority.set http://tizen.org/privilege/systemsettings.admin http://tizen.org/privilege/contact.read + http://tizen.org/privilege/volume.set http://tizen.org/privilege/message.write http://tizen.org/privilege/notification http://tizen.org/privilege/display -- 2.34.1