RingPresenter(view::RingView *view, model::Alarm *alarm);
~RingPresenter();
- void DismissButtonClicked(void);
- void SnoozeButtonClicked(void);
-
private:
view::RingView *view_;
model::Ring *model_;
common::CounterAnimator animator_;
+ /**
+ * @brief Function called on animator's timeout request.
+ *
+ * This function updates the Ring's time counter on every
+ * animator's tick.
+ */
void TimeUpdateRequest(void);
+
+ /**
+ * @brief Callback function called on dismiss button click.
+ */
+ void DismissButtonClicked(void);
+
+ /**
+ * @brief Callback function called on snooze button click.
+ */
+ void SnoozeButtonClicked(void);
+
+ /**
+ * @brief Callback function called on sound stream focus change.
+ */
+ void SoundStreamChanged(void);
+
+ static const char *SOUND_DEFAULT;
};
} //namespace presenter
* @note returns statically allocated string
*/
static const char *GetAppResourcePath(enum AppSubdirectory dir, const char *relative);
+
+ /**
+ * @brief Checks whether file with passed path exists.
+ *
+ * @param[in] path The path to the file.
+ */
+ static bool FileExists(const char *path);
};
}
#include <Elementary.h>
#include <vector>
#include <functional>
+#include <player.h>
#include "View/CounterView.h"
BUTTON_DISMISS_CLICKED,
BUTTON_SNOOZE_CLICKED,
+ SOUND_STREAM_FOCUS_CHANGED,
+
MAX,
};
class RingView : public ui::IView {
public:
+ /**
+ * @brief Creates class object and ring view.
+ */
RingView();
+
+ /**
+ * @brief Destroys class object and ring view.
+ */
~RingView();
+ /**
+ * @brief Shows countdown counter with timer's elapsed time.
+ */
void ShowCounter(void);
+
+ /**
+ * @brief Sets title of the inocming ring view.
+ *
+ * @param[in] title The title to be set
+ */
void SetTitle(const char *title);
+
+ /**
+ * @brief Sets text for main content view with alarm's time and current date.
+ *
+ * @param[in] ampm The AM or PM string to be shown
+ * @param[in] hour The alarm hour to be shown
+ * @param[in] date The current date string
+ */
void SetTimeLabel(const char *ampm, const char *hour, const char *date);
+ /**
+ * @brief Displays time in a countdown time counter.
+ *
+ * @param[in] hour The hour to be displayed
+ * @param[in] min The minute to be displayed
+ * @param[in] sec The second to be displayed
+ */
void DisplayTime(int hour, int min, int sec);
+ /**
+ * @brief Registers signal with the RingSignal type.
+ *
+ * @param[in] func The function to be called on passed type signal
+ * @param[in] type The signal type on which callback should be called
+ */
void RegisterSignal(std::function<void(void)>func, RingSignal type);
+
+ /**
+ * @brief Emits signal with the assigned type.
+ *
+ * @param[in] type The type of the signal whose callback must be called
+ */
void EmitSignal(RingSignal type);
+
+ /**
+ * @brief Shows or hides snooze swipe button.
+ *
+ * @param[in] enabled True if snooze button should be visible, otherwise false.
+ */
void EnableSnooze(bool enabled);
+ /**
+ * @brief Creates sound stream and plays music with the passed uri
+ * path and relative to system volume value.
+ *
+ * @param[in] uri The uri path to the sound
+ * @param[in] volume The relative volume of the sound to be played
+ */
+ void PlayMusic(const char *uri, double volume);
+
+ /**
+ * @brief Stops the currently playing music and destroys created with PlayMusic
+ * sound stream. After music is stopped, to play it againg it is necessary
+ * to call PlayMusic method.
+ */
+ void StopMusic(void);
+
+ /**
+ * @brief Pauses the currently playing music without destroying sound stream.
+ */
+ void PauseMusic(void);
+
+ /**
+ * @brief Resumes paused music.
+ */
+ void ResumeMusic(void);
+
+ /**
+ * @brief Starts device vibration.
+ */
+ void StartVibration(void);
+
+ /**
+ * @brief Stops device vibration.
+ */
+ void StopVibration(void);
+
+ /**
+ * @brief Destroys and zeroes player handler.
+ */
+ void DestroyPlayer(void);
+
+ /**
+ * @brief Destroys and zeroes stream info handler.
+ */
+ void DestroyStreamInfo(void);
+
+ /**
+ * @brief Returns current player state.
+ */
+ player_state_e GetPlayerState(void);
+
+ /**
+ * @brief Returns current player focus state.
+ */
+ sound_stream_focus_state_e GetStreamFocusState(void);
+
+ /**
+ * @brief Returns the Evas_Object of the view.
+ */
Evas_Object *GetEvasObject();
private:
Evas_Object *layout_;
view::CounterView *counter_;
+ player_h player_;
+ sound_stream_info_h stream_info_;
+
std::vector<std::function<void(void)>> signals_
= std::vector<std::function<void(void)>>((int)RingSignal::MAX, nullptr);
static const char *GROUP;
static const char *EDJE_FILE;
+ /**
+ * @brief Callback function to be called on dismiss button swipe.
+ */
static void DismissCb(void *data, Evas_Object *obj, const char *emission, const char *source);
+
+ /**
+ * @brief Callback function to be called on snooze button swipe.
+ */
static void SnoozeCb(void *data, Evas_Object *obj, const char *emission, const char *source);
+ /**
+ * @brief Callback function called when the sound stream is interrupted and
+ * playback device handles other application or stream sound.
+ */
+ static void PlayerInterruptedCb(player_interrupted_code_e code, void *data);
+
+ /**
+ * @brief Callback function called on stream focus change.
+ */
+ static void StreamFocusChangedCb(sound_stream_info_h stream_info, sound_stream_focus_change_reason_e reason, const char *extra_info, void *user_data);
+
+
+ /**
+ * @brief Sets the main content for the view from the following:
+ * - time counter,
+ * - alarm info view,
+ *
+ * @param[in] ly The layout of the ring view where content must be placed.
+ */
void SetMainContent(Evas_Object *ly);
};
# EDC Resource Path
USER_EDCS_IMAGE_DIRS = res/edje/images
USER_EDCS_IMAGE_DIRS_ABS =
-USER_EDCS_SOUND_DIRS = res/edje/sounds
+USER_EDCS_SOUND_DIRS = res/edje/sounds res/edje/settings
USER_EDCS_SOUND_DIRS_ABS =
USER_EDCS_FONT_DIRS = res/edje/fonts
USER_EDCS_FONT_DIRS_ABS =
#include "Presenter/RingPresenter.h"
#include "Utils/Time.h"
+#include "Utils/Utils.h"
#include "Controller/RingController.h"
namespace presenter {
+const char *SOUND_DEFAULT = "settings/ringtone_sdk.mp3";
+
RingPresenter::RingPresenter(view::RingView *view, model::Ring *model)
: view_(view), model_(model)
{
view::RingSignal::BUTTON_DISMISS_CLICKED);
view_->RegisterSignal(std::bind(&RingPresenter::SnoozeButtonClicked, this),
view::RingSignal::BUTTON_SNOOZE_CLICKED);
+ view_->RegisterSignal(std::bind(&RingPresenter::SoundStreamChanged, this),
+ view::RingSignal::SOUND_STREAM_FOCUS_CHANGED);
model_->Run();
animator_.Start();
view_->EnableSnooze(false);
+ view_->PlayMusic(utils::Utils::GetAppResourcePath(utils::Utils::APP_DIR_RESOURCE, presenter::SOUND_DEFAULT), 1.0);
+ view_->StartVibration();
}
RingPresenter::RingPresenter(view::RingView *view, model::Alarm *alarm)
: view_(view), model_(NULL), alarm_(alarm)
{
+ if (!alarm)
+ return;
view_->RegisterSignal(std::bind(&RingPresenter::DismissButtonClicked, this),
view::RingSignal::BUTTON_DISMISS_CLICKED);
view_->RegisterSignal(std::bind(&RingPresenter::SnoozeButtonClicked, this),
view::RingSignal::BUTTON_SNOOZE_CLICKED);
+ view_->RegisterSignal(std::bind(&RingPresenter::SoundStreamChanged, this),
+ view::RingSignal::SOUND_STREAM_FOCUS_CHANGED);
+
+ utils::Time time = alarm_->GetTime();
- utils::Time time = alarm->GetTime();
+ view_->SetTitle(alarm_->GetName().c_str());
+ view_->SetTimeLabel(time.Meridiem().c_str(), time.GetFormattedTime("HH:mm").c_str(), time.GetDate().c_str());
+ if (utils::Utils::FileExists(alarm_->GetMelody().c_str()))
+ view_->PlayMusic(alarm_->GetMelody().c_str(), alarm_->GetVolume());
+ else {
+ DBG("Invalid alarm's sound path. Playing default.");
+ view_->PlayMusic(utils::Utils::GetAppResourcePath(
+ utils::Utils::APP_DIR_RESOURCE, presenter::SOUND_DEFAULT),
+ alarm_->GetVolume());
+ }
- view->SetTitle(alarm->GetName().c_str());
- view->SetTimeLabel(time.Meridiem().c_str(), time.GetFormattedTime("HH:mm").c_str(), time.GetDate().c_str());
+ view_->StartVibration();
- if (alarm->CanSnooze())
+ if (alarm_->CanSnooze())
view_->EnableSnooze(true);
else
view_->EnableSnooze(false);
RingPresenter::~RingPresenter()
{
+ view_->StopMusic();
+ view_->StopVibration();
+ animator_.Remove();
}
void RingPresenter::TimeUpdateRequest()
void RingPresenter::DismissButtonClicked()
{
- alarm_->Dismiss();
- animator_.Remove();
+ if (alarm_)
+ alarm_->Dismiss();
+
controller::RingController::GetInstance().ShutDown();
}
void RingPresenter::SnoozeButtonClicked()
{
- alarm_->Snooze();
+ if (alarm_)
+ alarm_->Snooze();
+
animator_.Remove();
controller::RingController::GetInstance().ShutDown();
}
+void RingPresenter::SoundStreamChanged()
+{
+ switch (view_->GetStreamFocusState()) {
+ case SOUND_STREAM_FOCUS_STATE_ACQUIRED:
+ if (view_->GetPlayerState() == PLAYER_STATE_PAUSED)
+ view_->ResumeMusic();
+ else {
+ view_->PlayMusic(
+ alarm_ ? alarm_->GetMelody().c_str() : "/opt/share/settings/Ringtones/ringtone_sdk.mp3",
+ alarm_ ? alarm_->GetVolume() : 1.0);
+ }
+
+ view_->StartVibration();
+ break;
+ case SOUND_STREAM_FOCUS_STATE_RELEASED:
+ if (view_->GetPlayerState() == PLAYER_STATE_PLAYING)
+ view_->PauseMusic();
+ else
+ view_->StopMusic();
+ view_->StopVibration();
+ break;
+ default:
+ ERR("Invalid stream state");
+ return;
+ }
+}
+
} //namespace presenter
#include <app_common.h>
#include <Elementary.h>
#include <limits.h>
+#include <Ecore.h>
using namespace utils;
return &buf[0];
}
+
+bool Utils::FileExists(const char *path)
+{
+ return ecore_file_exists(path);
+}
*/
#include <efl_util.h>
+#include <sound_manager.h>
+#include <feedback.h>
#include "View/RingView.h"
#include "View/MainView.h"
const char *RingView::EDJE_FILE = "edje/ring.edj";
const char *RingView::GROUP = "main";
-RingView::RingView() : win_(NULL), layout_(NULL), counter_(NULL)
+RingView::RingView()
+ : win_(nullptr), layout_(nullptr), counter_(nullptr), player_(nullptr), stream_info_(nullptr)
{
win_ = elm_win_add(NULL, "Ring", ELM_WIN_NOTIFICATION);
if (!win_) {
signals_.at((int)type)();
}
+void RingView::PlayMusic(const char *uri, double volume)
+{
+ if (player_ || stream_info_)
+ StopMusic();
+
+ DBG("Play music from: %s", uri);
+ int ret = sound_manager_create_stream_information(SOUND_STREAM_TYPE_ALARM,
+ StreamFocusChangedCb, this, &stream_info_);
+ if (ret != SOUND_MANAGER_ERROR_NONE) {
+ ERR("sound_manager_create_stream_information failed[%d]: %s", ret, get_error_message(ret));
+ return;
+ }
+
+ ret = sound_manager_acquire_focus(stream_info_, SOUND_STREAM_FOCUS_FOR_PLAYBACK, NULL);
+ if (ret != SOUND_MANAGER_ERROR_NONE) {
+ DestroyStreamInfo();
+ ERR("sound_manager_acquire_focus failed[%d]: %s", ret, get_error_message(ret));
+ return;
+ }
+
+ int maxVolume = 0;
+ ret = sound_manager_get_max_volume(SOUND_TYPE_ALARM, &maxVolume);
+ if (ret != SOUND_MANAGER_ERROR_NONE) {
+ ERR("sound_manager_get_max_volume failed[%d]: %s", ret, get_error_message(ret));
+ DestroyStreamInfo();
+ return;
+ }
+
+ ret = sound_manager_set_volume(SOUND_TYPE_ALARM, maxVolume);
+ if (ret != SOUND_MANAGER_ERROR_NONE) {
+ ERR("sound_manager_set_volume failed[%d]: %s", ret, get_error_message(ret));
+ DestroyStreamInfo();
+ return;
+ }
+
+ ret = player_create(&player_);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_create failed[%d]: %s", ret, get_error_message(ret));
+ DestroyStreamInfo();
+ return;
+ }
+
+ ret = player_set_uri(player_, uri);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_set_uri failed[%d]: %s", ret, get_error_message(ret));
+ DestroyPlayer();
+ DestroyStreamInfo();
+ return;
+ }
+
+ ret = player_set_audio_policy_info(player_, stream_info_);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_set_audio_policy_info failed[%d]: %s", ret, get_error_message(ret));
+ DestroyPlayer();
+ DestroyStreamInfo();
+ return;
+ }
+
+ ret = player_prepare(player_);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_prepare failed[%d]: %s", ret, get_error_message(ret));
+ DestroyPlayer();
+ DestroyStreamInfo();
+ return;
+ }
+
+ ret = player_set_looping(player_, true);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_set_looping failed[%d]: %s", ret, get_error_message(ret));
+ DestroyPlayer();
+ DestroyStreamInfo();
+ return;
+ }
+
+ ret = player_set_volume(player_, volume, volume);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_set_volume failed[%d]: %s", ret, get_error_message(ret));
+ DestroyPlayer();
+ DestroyStreamInfo();
+ return;
+ }
+
+ if (GetPlayerState() == PLAYER_STATE_READY) {
+ ret = player_start(player_);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_start failed[%d]: %s", ret, get_error_message(ret));
+ StopMusic();
+ return;
+ }
+ }
+}
+
+void RingView::DestroyPlayer()
+{
+ player_destroy(player_);
+ player_ = nullptr;
+}
+
+void RingView::DestroyStreamInfo()
+{
+ sound_manager_destroy_stream_information(stream_info_);
+ stream_info_ = nullptr;
+}
+
+void RingView::StreamFocusChangedCb(sound_stream_info_h streamInfo,
+ sound_stream_focus_change_reason_e reason, const char *extraInfo, void *userData)
+{
+ RingView *object = static_cast<RingView *>(userData);
+
+ object->EmitSignal(view::RingSignal::SOUND_STREAM_FOCUS_CHANGED);
+}
+
+void RingView::PauseMusic()
+{
+ if (!player_) {
+ DBG("Player is not created");
+ return;
+ }
+
+ if (GetPlayerState() == PLAYER_STATE_PLAYING)
+ player_pause(player_);
+ else {
+ ERR("Player is not playing.");
+ return;
+ }
+}
+
+void RingView::ResumeMusic()
+{
+ if (!player_) {
+ DBG("Player is not created");
+ return;
+ }
+
+ if (GetPlayerState() == PLAYER_STATE_PAUSED)
+ player_start(player_);
+ else {
+ ERR("Player is not paused.");
+ return;
+ }
+}
+
+void RingView::StopMusic()
+{
+ int ret = 0;
+
+ if (player_) {
+ ret = player_stop(player_);
+ if (ret != PLAYER_ERROR_NONE)
+ ERR("player_stop failed[%d]: %s", ret, get_error_message(ret));
+
+ ret = player_unprepare(player_);
+ if (ret != PLAYER_ERROR_NONE)
+ ERR("player_unprepare failed[%d]: %s", ret, get_error_message(ret));
+
+ DestroyPlayer();
+ }
+
+ if (stream_info_) {
+ ret = sound_manager_release_focus(stream_info_, SOUND_STREAM_FOCUS_FOR_PLAYBACK, NULL);
+ if (ret != SOUND_MANAGER_ERROR_NONE)
+ ERR("sound_manager_release_focus failed[%d]: %s", ret, get_error_message(ret));
+
+ DestroyStreamInfo();
+ }
+}
+
+player_state_e RingView::GetPlayerState()
+{
+ if (!player_)
+ return PLAYER_STATE_NONE;
+
+ player_state_e playerState;
+ int ret = player_get_state(player_, &playerState);
+ if (ret != PLAYER_ERROR_NONE) {
+ ERR("player_get_state failed[%d]: %s ", ret, get_error_message(ret));
+ return PLAYER_STATE_NONE;
+ }
+
+ return playerState;
+}
+
+sound_stream_focus_state_e RingView::GetStreamFocusState()
+{
+ sound_stream_focus_state_e focusState;
+
+ int ret = sound_manager_get_focus_state(stream_info_, &focusState, NULL);
+ if (ret != SOUND_MANAGER_ERROR_NONE) {
+ ERR("sound_manager_get_focus_state failed[%d]: %s", ret, get_error_message(ret));
+ return SOUND_STREAM_FOCUS_STATE_RELEASED;
+ }
+
+ return focusState;
+}
+
+void RingView::StartVibration()
+{
+ int ret = feedback_initialize();
+ if (ret != FEEDBACK_ERROR_NONE) {
+ ERR("feedback_initialize failed[%d]: %s", ret, get_error_message(ret));
+ return;
+ }
+
+ bool status = false;
+ ret = feedback_is_supported_pattern(FEEDBACK_TYPE_VIBRATION, FEEDBACK_PATTERN_TIMER, &status);
+ if (ret != FEEDBACK_ERROR_NONE || !status) {
+ ERR("feedback_is_supported_pattern failed[%d]: %s", ret, get_error_message(ret));
+ feedback_deinitialize();
+ return;
+ }
+
+ ret = feedback_play_type(FEEDBACK_TYPE_VIBRATION, FEEDBACK_PATTERN_TIMER);
+ if (ret != FEEDBACK_ERROR_NONE) {
+ ERR("feedback_play_type failed[%d]: %s", ret, get_error_message(ret));
+ feedback_deinitialize();
+ }
+}
+
+void RingView::StopVibration(void)
+{
+ int ret = feedback_stop();
+ if (ret != FEEDBACK_ERROR_NONE) {
+ ERR("feedback_stop failed[%d]: %s", ret, get_error_message(ret));
+ return;
+ }
+
+ ret = feedback_deinitialize();
+ if (ret != FEEDBACK_ERROR_NONE)
+ ERR("feedback_deinitialize failed[%d]: %s", ret, get_error_message(ret));
+}
+
} //namespace view
<icon>clock.png</icon>
</ui-application>
<privileges>
- <privilege>http://tizen.org/privilege/window.priority.set</privilege>
- <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
<privilege>http://tizen.org/privilege/alarm.set</privilege>
+ <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+ <privilege>http://tizen.org/privilege/window.priority.set</privilege>
+ <privilege>http://tizen.org/privilege/haptic</privilege>
+ <privilege>http://tizen.org/privilege/volume.set</privilege>
</privileges>
</manifest>