Add notifications of state changes for Alarm objects.
Change-Id: I3156261944de2b220c37541feb2831b422dcab7c
#include "Utils/Serialization.h"
#include "Utils/Time.h"
#include "Utils/Log.h"
+#include "Utils/ObservableObject.h"
namespace model {
typedef int AlarmId;
* It implements scheduling, rescheduling, canceling alarms and also
* managing snoozing feature.
*/
- class Alarm : public utils::ISerializable {
+ class Alarm : public utils::ObservableObject, public utils::ISerializable {
public:
/**
* @brief Alarm type
bool CanSnooze() const;
/**
+ * @brief Check if alarm is currently snoozed.
+ *
+ * @return true if alarm is currently snoozed, false otherwise.
+ */
+ bool IsSnoozed() const;
+
+ /**
* @brief Sets time interval between snooze alarms
* @param[in] seconds number of seconds for Snooze to be scheduled.
*/
inline bool operator==(const Alarm &a) { return (time == a.time) && (a.name == name); }
/** Two alarms are considered same if they have same AlarmId */
inline bool operator==(const AlarmId id) { return (this->alarm_id == id) || (this->snooze.alarm_id == id); }
+
private:
int alarm_id;
std::string name;
unsigned int attempts_max;
} snooze;
utils::Time time;
+ static void CancelPlatformAlarm(int &id);
+ static bool IsPlatformAlarmCancelled(int id);
};
void Deserialize(utils::IReader &w, Alarm::Type &type);
void Serialize(utils::IWriter &w, Alarm::Type type);
void OnAlarmEditedEvent(model::AlarmList::Iterator e);
void OnDeleteItemClicked();
void UpdateBackgroundLabel();
+ void AddItem(model::AlarmList::Iterator iterator);
std::map<int, model::AlarmList::Iterator> alarms_;
std::vector<utils::Connection> connections_;
+ std::vector<utils::Connection> object_connections_;
bool CheckModelSizeLimit();
void OnMenuButtonClicked();
};
--- /dev/null
+/*
+* 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 <limits>
+
+static inline bool AreEqual(double v1, double v2)
+{
+ return std::abs(v1 - v2) < std::numeric_limits<double>::epsilon();
+}
+
--- /dev/null
+/*
+* 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 OBSERVABLEOBJECT_H_
+#define OBSERVABLEOBJECT_H_
+
+#include "Utils/Signal.h"
+
+namespace utils {
+ /*
+ * @brief Base class for objects required to broadcast
+ * notifications about changes of their states.
+ */
+ class ObservableObject {
+ public:
+ ObservableObject() {}
+ ObservableObject(const ObservableObject &) {}
+ ObservableObject(ObservableObject &&r)
+ {
+ OnChanged = std::move(r.OnChanged);
+ }
+ void operator=(const ObservableObject &) {}
+ virtual ~ObservableObject() {}
+
+ /**
+ * @brief Signal that should be triggered
+ * when any property of the object changed.
+ */
+ utils::Signal<void(void)> OnChanged;
+ };
+} /* utils */
+
+#endif /* end of include guard: OBSERVABLEOBJECT_H_ */
*/
class AlarmListView : public ui::IView {
public:
+ typedef uintptr_t ItemId;
/**
* @brief Alarm view constructor
*
*
* @return return item identiefier.
*/
- int ItemPrepend(utils::Time time, const char *name,
- const model::WeekFlags flags, bool active);
+ ItemId ItemPrepend(utils::Time time, const char *name,
+ const model::WeekFlags flags, bool active, bool is_snoozed);
/**
* @brief Remove item from view.
- * @param idx item identifier.
+ * @param id item identifier.
*/
- void RemoveItem(int idx);
+ void RemoveItem(ItemId id);
/**
* @brief Updates item.
* @param[in] name name to be displayed.
* @param[in] flags weekflags to be displayed.
* @param[in] active item active status.
+ * @param[in] is_snoozed item snooze status.
*/
- void ItemUpdate(int idx, utils::Time time, const char *name,
- const model::WeekFlags flags, bool active);
+ void ItemUpdate(ItemId id, utils::Time time, const char *name,
+ const model::WeekFlags flags, bool active, bool is_snoozed);
/**
* @brief Signal emitted when user clicked "delete" item on Delete popup
utils::Connection time_format_change_listener_;
void TimeFormatChanged();
void CreateNoContentLayout(Evas_Object *parent);
+ static void SetItemCheckboxStatus(Elm_Object_Item *item, bool is_snoozed);
+ static void SetItemActiveStatus(Elm_Object_Item *it, bool active);
+ static void SetCheckboxStatus(Evas_Object *check, bool is_snoozed);
};
}
*/
#include <app_alarm.h>
+#include <cmath>
#include "Model/Alarm.h"
#include "Common/Defines.h"
#include "Utils/Log.h"
+#include "Utils/Comparators.h"
using namespace model;
using namespace utils;
}
app_control_destroy(control);
+ ObservableObject::OnChanged();
}
void Alarm::Deactivate()
if (!activated)
return;
- int err = alarm_cancel(alarm_id);
- if (err != ALARM_ERROR_NONE) {
- ERR("alarm_cancel failed: %s", get_error_message(err));
- }
- if (snooze.alarm_id != -1 ) {
- err = alarm_cancel(snooze.alarm_id);
- if (err != ALARM_ERROR_NONE) {
- ERR("alarm_cancel failed: %s", get_error_message(err));
- } else {
- snooze.alarm_id = -1;
- }
- }
+ Dismiss();
+ CancelPlatformAlarm(alarm_id);
activated = false;
+ ObservableObject::OnChanged();
}
-void Alarm::Snooze()
+bool Alarm::IsPlatformAlarmCancelled(int id)
+{
+ return id == -1;
+}
+
+void Alarm::CancelPlatformAlarm(int &id)
{
- if (!activated || !snooze_enabled)
+ if (id == -1)
return;
- if (snooze.attempt >= snooze.attempts_max)
+ int err = alarm_cancel(id);
+ if (err != ALARM_ERROR_NONE) {
+ INF("alarm_cancel failed: %s", get_error_message(err));
+ }
+ id = -1;
+}
+
+void Alarm::Snooze()
+{
+ if (!CanSnooze())
return;
app_control_h control = AppControlCreate();
if (!control) return;
+ CancelPlatformAlarm(snooze.alarm_id);
int err = alarm_schedule_once_after_delay(control, snooze.interval, &snooze.alarm_id);
if (err == ALARM_ERROR_NONE) {
snooze.attempt++;
}
app_control_destroy(control);
+ ObservableObject::OnChanged();
}
void Alarm::Dismiss()
{
- if (!activated || !snooze_enabled)
+ if (!IsActivated() || !IsSnoozeEnabled())
return;
+ CancelPlatformAlarm(snooze.alarm_id);
snooze.attempt = 0;
+ ObservableObject::OnChanged();
}
bool Alarm::CanSnooze() const
{
- if (!activated || !snooze_enabled)
+ if (!IsActivated() || !IsSnoozeEnabled())
return false;
if (snooze.attempt >= snooze.attempts_max)
void Alarm::EnableSnooze()
{
- snooze_enabled = true;
+ if (!snooze_enabled)
+ {
+ snooze_enabled = true;
+ ObservableObject::OnChanged();
+ }
}
void Alarm::DisableSnooze()
{
- snooze_enabled = false;
+ if (IsSnoozed())
+ Dismiss();
+
+ if (snooze_enabled)
+ {
+ snooze_enabled = false;
+ ObservableObject::OnChanged();
+ }
}
bool Alarm::IsSnoozeEnabled() const
return snooze_enabled;
}
+bool Alarm::IsSnoozed() const
+{
+ return !IsPlatformAlarmCancelled(snooze.alarm_id);
+}
+
void Alarm::SetSnoozeInterval(unsigned int seconds)
{
- snooze.interval = seconds;
+ if (snooze.interval != seconds)
+ {
+ snooze.interval = seconds;
+ ObservableObject::OnChanged();
+ }
}
unsigned int Alarm::GetSnoozeInterval() const
void Alarm::SetSnoozeMaxAttemps(unsigned int attemps)
{
- snooze.attempts_max = attemps;
+ if (snooze.attempts_max != attemps)
+ {
+ snooze.attempts_max = attemps;
+ ObservableObject::OnChanged();
+ }
}
unsigned int Alarm::GetSnoozeMaxAttempts() const
void Alarm::SetName(std::string nm)
{
- name = nm;
+ if (name != nm)
+ {
+ name = nm;
+ ObservableObject::OnChanged();
+ }
}
Time Alarm::GetTime() const
time = tm;
- if (activated) {
+ if (IsActivated()) {
Deactivate();
Activate();
}
+ ObservableObject::OnChanged();
}
std::string Alarm::GetMelody() const
void Alarm::SetMelody(std::string path)
{
- sound.melody = path;
+ if (sound.melody != path)
+ {
+ sound.melody = path;
+ ObservableObject::OnChanged();
+ }
}
Alarm::Type Alarm::GetType() const
void Alarm::SetType(Alarm::Type type)
{
- type_ = type;
+ if (type_ != type)
+ {
+ type_ = type;
+ ObservableObject::OnChanged();
+ }
}
WeekFlags Alarm::GetWeekFlags() const
return flags;
}
-void Alarm::SetWeekFlags(WeekFlags flags)
+void Alarm::SetWeekFlags(WeekFlags fl)
{
- if (this->flags == flags)
+ if (flags == fl)
return;
- this->flags = flags;
+ flags = fl;
- if (activated) {
+ if (IsActivated()) {
Deactivate();
Activate();
}
+ ObservableObject::OnChanged();
}
bool Alarm::IsActivated() const
void Alarm::SetVolume(double volume)
{
+ if (AreEqual(sound.volume, volume)) return;
if (volume < 0.0) volume = 0.0;
if (volume > 1.0) volume = 1.0;
sound.volume = volume;
+ ObservableObject::OnChanged();
}
void Ring::AlarmDefer()
{
- if (alarm_ && alarm_->IsSnoozeEnabled()) {
+ if (alarm_ && alarm_->CanSnooze()) {
AlarmSnooze();
return;
}
{
}
+void AlarmListPresenter::AddItem(AlarmList::Iterator it)
+{
+ int id = view_->ItemPrepend(
+ it->GetTime(),
+ it->GetName().c_str(),
+ it->GetWeekFlags(),
+ it->IsActivated(),
+ it->IsSnoozed());
+ alarms_.insert(std::map<int, AlarmList::Iterator>::value_type (id, it));
+ object_connections_.push_back(it->OnChanged.Connect(
+ std::bind(&AlarmListPresenter::OnAlarmEditedEvent, this, it)));
+}
+
void AlarmListPresenter::ShowAll()
{
view_->Clear();
+ object_connections_.clear();
- for (auto it = model_.Begin(); it != model_.End(); ++it) {
- int id = view_->ItemPrepend(
- it->GetTime(),
- it->GetName().c_str(),
- it->GetWeekFlags(),
- it->IsActivated());
- alarms_.insert(std::map<int, AlarmList::Iterator>::value_type (id, it));
- }
-
+ for (auto it = model_.Begin(); it != model_.End(); ++it)
+ AddItem(it);
UpdateBackgroundLabel();
}
auto &alarm_it = it->second;
- if (alarm_it->IsActivated()) {
- alarm_it->Deactivate();
+ if (alarm_it->IsSnoozed()) {
+ alarm_it->Dismiss();
} else {
- alarm_it->Activate();
+ if (alarm_it->IsActivated()) {
+ alarm_it->Deactivate();
+ } else {
+ alarm_it->Activate();
+ }
}
model_.Replace(alarm_it, *alarm_it);
void AlarmListPresenter::OnAlarmAddedEvent(AlarmList::Iterator it)
{
- int id = view_->ItemPrepend(
- it->GetTime(),
- it->GetName().c_str(),
- it->GetWeekFlags(),
- it->IsActivated());
- alarms_.insert(std::map<int, AlarmList::Iterator>::value_type (id, it));
+ AddItem(it);
UpdateBackgroundLabel();
}
if (iterator == it->second) {
auto &alarm_it = it->second;
view_->ItemUpdate(it->first, alarm_it->GetTime(), alarm_it->GetName().c_str(),
- alarm_it->GetWeekFlags(), alarm_it->IsActivated());
+ alarm_it->GetWeekFlags(), alarm_it->IsActivated(), alarm_it->IsSnoozed());
break;
}
}
std::vector<int> deleted = view_->GetSelectedItems();
for (auto idx: deleted) {
- model_.Remove(alarms_.find(idx)->second);
+ auto it = alarms_.find(idx);
+ if (it != alarms_.end())
+ {
+ auto alarm_it = it->second;
+ alarm_it->Deactivate();
+ model_.Remove(alarm_it);
+ }
+
}
view_->PopPage();
}
animator_.Start();
- view_->EnableSnooze(alarm ? alarm->IsSnoozeEnabled() : false);
+ view_->EnableSnooze(alarm ? alarm->CanSnooze() : false);
}
void RingPresenter::TimeoutHandle(utils::Event &e)
Time time;
/** Iternal EFL handle */
Elm_Object_Item *it;
+ /* Snooze flag */
+ bool snoozed;
};
Elm_Genlist_Item_Class AlarmListView::alarm_itc = {
void AlarmListView::ItemClicked(void *data, Evas_Object *obj, void *info)
{
ItemData *id = static_cast<ItemData*>(data);
-
- if (elm_check_state_get(obj))
- elm_object_item_signal_emit(id->it, "alarm,state,enabled", "clock");
- else
- elm_object_item_signal_emit(id->it, "alarm,state,disabled", "clock");
-
id->instance->OnItemToggled((uintptr_t)id->it);
}
}
}
-void AlarmListView::ItemRealized(void *data, Evas_Object *obj, void *info)
+static void
+tizen_check_color_set(Evas_Object *check, int r, int g, int b, int a)
{
- Elm_Object_Item *it = static_cast<Elm_Object_Item*>(info);
+ Evas_Object *edje = elm_layout_edje_get(check);
+ if (!edje) return;
+ edje_object_color_class_set(edje, "check/toggle/bg_on", r, g, b, a,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+}
- ItemData *id = static_cast<ItemData*>(elm_object_item_data_get(it));
+void AlarmListView::SetCheckboxStatus(Evas_Object *check, bool is_snoozed)
+{
+ if (is_snoozed)
+ tizen_check_color_set(check, 255, 180, 0, 255);
+ else
+ tizen_check_color_set(check, 89, 176, 58, 255);
+}
- if (id->active)
- elm_object_item_signal_emit(id->it, "alarm,state,enabled", "clock");
+void AlarmListView::SetItemCheckboxStatus(Elm_Object_Item *item, bool is_snoozed)
+{
+ Evas_Object *check = elm_object_item_part_content_get(item, "onoff");
+ if (!check)
+ return;
+
+ SetCheckboxStatus(check, is_snoozed);
+}
+
+void AlarmListView::SetItemActiveStatus(Elm_Object_Item *it, bool active)
+{
+ if (active)
+ elm_object_item_signal_emit(it, "alarm,state,enabled", "clock");
else
- elm_object_item_signal_emit(id->it, "alarm,state,disabled", "clock");
+ elm_object_item_signal_emit(it, "alarm,state,disabled", "clock");
+}
+
+void AlarmListView::ItemRealized(void *data, Evas_Object *obj, void *info)
+{
+ Elm_Object_Item *it = static_cast<Elm_Object_Item*>(info);
+ ItemData *id = static_cast<ItemData*>(elm_object_item_data_get(it));
+ SetItemCheckboxStatus(it, id->snoozed);
+ SetItemActiveStatus(it, id->active);
SetItemRepeatIconVisibility(it, (id->flags.OnAny(WeekDay::ALL_WEEK)));
}
ItemData *id = static_cast<ItemData*>(data);
if (!strcmp(part, "onoff")) {
- Evas_Object *toggle = elm_check_add(obj);
- elm_object_style_set(toggle, "on&off");
- elm_check_state_set(toggle, id->active ? EINA_TRUE : EINA_FALSE);
- evas_object_propagate_events_set(toggle, EINA_FALSE);
- evas_object_smart_callback_add(toggle, "changed", AlarmListView::ItemClicked, id);
- evas_object_show(toggle);
- return toggle;
+ Evas_Object *check = elm_check_add(obj);
+ elm_object_style_set(check, "toggle");
+ elm_check_state_set(check, id->active ? EINA_TRUE : EINA_FALSE);
+ evas_object_propagate_events_set(check, EINA_FALSE);
+ evas_object_smart_callback_add(check, "changed", AlarmListView::ItemClicked, id);
+ SetCheckboxStatus(check, id->snoozed);
+ evas_object_show(check);
+ return check;
}
return nullptr;
}
elm_genlist_clear(genlist_);
}
-int AlarmListView::ItemPrepend(Time time, const char *name, const WeekFlags flags, bool active)
+AlarmListView::ItemId AlarmListView::ItemPrepend(Time time, const char *name, const WeekFlags flags, bool active, bool is_snoozed)
{
ItemData *data = new ItemData;
data->flags = flags;
data->active = active;
data->time = time;
+ data->snoozed = is_snoozed;
data->it = elm_genlist_item_prepend(genlist_,
&alarm_itc,
ELM_GENLIST_ITEM_NONE,
AlarmListView::ItemSelected, data);
- return (uintptr_t)data->it;
+ return (ItemId)data->it;
}
-void AlarmListView::ItemUpdate(int idx, Time time, const char *name,
- const WeekFlags flags, bool active)
+void AlarmListView::ItemUpdate(ItemId id, Time time, const char *name,
+ const WeekFlags flags, bool active, bool is_snoozed)
{
- Elm_Object_Item *it = (Elm_Object_Item*)idx;
+ Elm_Object_Item *it = (Elm_Object_Item*)id;
if (!it) return;
ItemData *data = static_cast<ItemData*>(elm_object_item_data_get(it));
data->flags = flags;
data->active = active;
data->time = time;
+ data->snoozed = is_snoozed;
- SetItemRepeatIconVisibility(it, (data->flags.OnAny(WeekDay::ALL_WEEK)));
elm_genlist_item_update(it);
}
-void AlarmListView::RemoveItem(int idx)
+void AlarmListView::RemoveItem(ItemId id)
{
- Elm_Object_Item *it = (Elm_Object_Item*)idx;
+ Elm_Object_Item *it = (Elm_Object_Item*)id;
if (it) elm_object_item_del(it);
}