diff options
-rw-r--r-- | MERGE-REVIEW | 53 | ||||
-rw-r--r-- | data/CMakeLists.txt | 10 | ||||
-rw-r--r-- | data/com.canonical.indicator.datetime.AlarmProperties.xml | 31 | ||||
-rw-r--r-- | data/com.canonical.indicator.datetime.gschema.xml.in (renamed from data/com.canonical.indicator.datetime.gschema.xml) | 116 | ||||
-rw-r--r-- | include/datetime/exporter.h | 27 | ||||
-rw-r--r-- | include/datetime/settings-shared.h | 10 | ||||
-rw-r--r-- | include/datetime/settings.h | 4 | ||||
-rw-r--r-- | src/CMakeLists.txt | 31 | ||||
-rw-r--r-- | src/engine-eds.cpp | 6 | ||||
-rw-r--r-- | src/exporter.cpp | 249 | ||||
-rw-r--r-- | src/main.cpp | 4 | ||||
-rw-r--r-- | src/settings-live.cpp | 12 | ||||
-rw-r--r-- | src/snap.cpp | 37 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 6 | ||||
-rw-r--r-- | tests/manual | 22 | ||||
-rw-r--r-- | tests/manual-test-snap.cpp | 22 | ||||
-rw-r--r-- | tests/test-exporter.cpp | 95 | ||||
-rw-r--r-- | tests/test-menus.cpp | 2 | ||||
-rw-r--r-- | tests/test-settings.cpp | 46 |
19 files changed, 512 insertions, 271 deletions
diff --git a/MERGE-REVIEW b/MERGE-REVIEW index 1a5815c..88f25f6 100644 --- a/MERGE-REVIEW +++ b/MERGE-REVIEW @@ -1,44 +1,35 @@ +* '''Checklist for component''': indicator-datetime + * '''Component Test Plan''': https://wiki.ubuntu.com/Process/Merges/TestPlan/indicator-datetime + * '''Trunk URL''': lp:indicator-datetime + * '''Ubuntu Package URL (LP)''': http://launchpad.net/ubuntu/+source/indicator-datetime This documents the expections that the project has on what both submitters and reviewers should ensure that they've done for a merge into the project. -== Submitter Responsibilities == +== MP Submission Checklist Template == - * Ensure the project compiles and the test suite executes without error - * Ensure that non-obvious code has comments explaining it - * If the change affects specific features, please reference the appropriate - tags in the merge description so reviewers can test appropriately: - [phone profile], [desktop profile], [alarms] +'''Note: Please ensure you include the following form filled out and submitted along side your code to the MP ticket.''' -== Reviewer Responsibilities == + * Are there any related MPs required for this MP to build/function as expected? Please list. + * Is your branch in sync with latest trunk? (e.g. bzr pull lp:trunk -> no changes) + * Did the code build without warnings? + * Did the tests run successfully? + * Did you perform an exploratory manual test run of your code change and any related functionality? + * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP? + * Has your component test plan been executed successfully on emulator, N4? + * Please list which manual tests are germane for the reviewer in this MR. - * Did the Jenkins build compile? Pass? Run unit tests successfully? - * Are there appropriate tests to cover any new functionality? - * Do the tag-specific tests pass? +== MP Review Checklist Template == -== Phone Profile Tests == +'''Note: Please ensure you include the following form filled out and submitted along side your code to the MP ticket.''' - * Run tests indicator-datetime/unity8* + * Have you checked that the submitter has accurately filled out the submitter checklist and has taken no shortcuts? + * Did you run the manual tests listed by the submitter? + * Did you do exploratory testing related to the component you own with the MP changeset included? + * If new features have been added, are the manual tests sufficient to cover them? -== Desktop Profile Tests == +== MP Landing Checklist Template == - * Run tests indicator-datetime/unity7* + * Ensure that the checklists have been properly filled out by submitter and all reviewers -== Alarm Tests == - - * Hardware wakeups for new alarms: - 1. Create and save an upcoming alarm in ubuntu-clock-app. - 2. Unplug the phone from any USB connection and put it to sleep. - 3. Confirm that the alarm sounds on time even if the phone is asleep. - (Note: if in doubt about sleep you can see in the syslog whether the - device actually suspended or whether the suspend was aborted) - - * Hardware wakeups for edited alarms: - 1. Edit an alarm that's already passed. (see previous test) - 2. Unplug the phone from any USB connection and put it to sleep. - 3. Confirm that the alarm sounds on time even if the phone is asleep. - (Note: if in doubt about sleep you can see in the syslog whether the - device actually suspended or whether the suspend was aborted.) - - diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 5d9e545..d66c865 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -4,9 +4,15 @@ include (UseGSettings) set (SCHEMA_NAME "com.canonical.indicator.datetime.gschema.xml") -set (SCHEMA_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME}") -add_schema (${SCHEMA_FILE}) +set (SCHEMA_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SCHEMA_NAME}") +set (SCHEMA_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME}.in") + +# generate the .xml file using intltool +set (ENV{LC_ALL} "C") +execute_process (COMMAND intltool-merge -quiet --xml-style --utf8 --no-translations "${SCHEMA_FILE_IN}" "${SCHEMA_FILE}") +# let UseGSettings do the rest +add_schema (${SCHEMA_FILE}) ## ## Upstart Job File diff --git a/data/com.canonical.indicator.datetime.AlarmProperties.xml b/data/com.canonical.indicator.datetime.AlarmProperties.xml new file mode 100644 index 0000000..d25fa82 --- /dev/null +++ b/data/com.canonical.indicator.datetime.AlarmProperties.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="com.canonical.indicator.datetime.AlarmProperties"> + + <property name="DefaultSound" type="s" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para>The default alarm sound's filename.</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="DefaultVolume" type="i" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para>How loudly to play alarm sounds. [Range: 1-100]</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Duration" type="i" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para>How long an alarm's sound should be looped.</doc:para> + </doc:description> + </doc:doc> + </property> + + </interface> +</node> diff --git a/data/com.canonical.indicator.datetime.gschema.xml b/data/com.canonical.indicator.datetime.gschema.xml.in index 3e0082d..62b42c1 100644 --- a/data/com.canonical.indicator.datetime.gschema.xml +++ b/data/com.canonical.indicator.datetime.gschema.xml.in @@ -5,152 +5,146 @@ <value nick="24-hour" value="2" /> <value nick="custom" value="3" /> </enum> - <enum id="alarm-volume-enum"> - <value nick="very-quiet" value="0" /> - <value nick="quiet" value="1" /> - <value nick="normal" value="2" /> - <value nick="loud" value="3" /> - <value nick="very-loud" value="4" /> - </enum> <schema id="com.canonical.indicator.datetime" path="/com/canonical/indicator/datetime/" gettext-domain="indicator-datetime"> <key name="show-clock" type="b"> <default>true</default> - <summary>Show the clock in the panel</summary> - <description> + <_summary>Show the clock in the panel</_summary> + <_description> Controls whether the clock indicator appears in the panel or not. - </description> + </_description> </key> <key name="time-format" enum="time-enum"> <default>'locale-default'</default> - <summary>What the time format should be</summary> - <description> + <_summary>What the time format should be</_summary> + <_description> Controls the time format that is displayed in the indicator. For almost all users this should be the default for their locale. If you think the setting is wrong for your locale please join or talk to the translation team for your language. If you just want something different you can adjust this to be either 12 or 24 time. Or, you can use a custom format string and set the custom-time-format setting. - </description> + </_description> </key> <key name="custom-time-format" type="s"> <default>"%l:%M %p"</default> - <summary>The format string passed to strftime</summary> - <description> + <_summary>The format string passed to strftime</_summary> + <_description> The format of the time and/or date that is visible on the panel when using the indicator. For most users this will be a set of predefined values as determined by the configuration utility, but advanced users can change it to anything strftime can accept. Look at the man page on strftime for more information. - </description> + </_description> </key> <key name="show-seconds" type="b"> <default>false</default> - <summary>Show the number of seconds in the indicator</summary> - <description> + <_summary>Show the number of seconds in the indicator</_summary> + <_description> Makes the datetime indicator show the number of seconds in the indicator. It's important to note that this will cause additional battery drain as the time will update 60 times as often, so it is not recommended. Also, this setting will be ignored if the time-format value is set to custom. - </description> + </_description> </key> <key name="show-day" type="b"> <default>false</default> - <summary>Show the day of the week in the indicator</summary> - <description> + <_summary>Show the day of the week in the indicator</_summary> + <_description> Puts the day of the week on the panel along with the time and/or date depending on settings. This setting will be ignored if the time-format value is set to custom. - </description> + </_description> </key> <key name="show-date" type="b"> <default>false</default> - <summary>Show the month and date in the indicator</summary> - <description> + <_summary>Show the month and date in the indicator</_summary> + <_description> Puts the month and the date in the panel along with the time and/or day of the week depending on settings. This setting will be ignored if the time-format value is set to custom. - </description> + </_description> </key> <key name="show-year" type="b"> <default>false</default> - <summary>Show the year in the indicator</summary> - <description> + <_summary>Show the year in the indicator</_summary> + <_description> Puts the year in the panel along with the month and the date. This setting will be ignored if either the time-format value is set to custom or if show-date is set to false. - </description> + </_description> </key> <key name="show-calendar" type="b"> <default>true</default> - <summary>Show the monthly calendar in the indicator</summary> - <description> + <_summary>Show the monthly calendar in the indicator</_summary> + <_description> Puts the monthly calendar in indicator-datetime's menu. - </description> + </_description> </key> <key name="show-week-numbers" type="b"> <default>false</default> - <summary>Show week numbers in calendar</summary> - <description> + <_summary>Show week numbers in calendar</_summary> + <_description> Shows the week numbers in the monthly calendar in indicator-datetime's menu. - </description> + </_description> </key> <key name="show-events" type="b"> <default>true</default> - <summary>Show events in the indicator</summary> - <description> + <_summary>Show events in the indicator</_summary> + <_description> Shows events from Evolution in indicator-datetime's menu. - </description> + </_description> </key> <key name="show-auto-detected-location" type="b"> <default>false</default> - <summary>Show the auto-detected location in the indicator</summary> - <description> + <_summary>Show the auto-detected location in the indicator</_summary> + <_description> Shows your current location (determined from geoclue and /etc/timezone) in indicator-datetime's menu. - </description> + </_description> </key> <key name="show-locations" type="b"> <default>false</default> - <summary>Show locations in the indicator</summary> - <description> + <_summary>Show locations in the indicator</_summary> + <_description> Shows custom defined locations in indicator-datetime's menu. - </description> + </_description> </key> <key name="locations" type="as"> <default>['UTC']</default> - <summary>A List of locations</summary> - <description> + <_summary>A List of locations</_summary> + <_description> Adds the list of locations the user has configured to display in the indicator-datetime menu. - </description> + </_description> </key> <key name="timezone-name" type="s"> <default>''</default> - <summary>The name of the current timezone</summary> - <description> + <_summary>The name of the current timezone</_summary> + <_description> Some timezones can be known by many different cities or names. This setting describes how the current zone prefers to be named. Format is "TIMEZONE NAME" (e.g. "America/New_York Boston" to name the New_York zone Boston). - </description> + </_description> </key> <key name="alarm-default-sound" type="s"> <default>'/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg'</default> - <summary>The alarm's default sound file.</summary> - <description> + <_summary>The alarm's default sound file.</_summary> + <_description> If an alarm doesn't specify its own sound file, this file will be used as the fallback sound. - </description> + </_description> </key> - <key name="alarm-default-volume" enum="alarm-volume-enum"> - <default>'normal'</default> - <summary>The alarm's default volume level.</summary> - <description> + <key name="alarm-default-volume" type="u"> + <range min="1" max="100"/> + <default>50</default> + <_summary>The alarm's default volume level.</_summary> + <_description> The volume at which alarms will be played. - </description> + </_description> </key> - <key name="alarm-duration-minutes" type="i"> + <key name="alarm-duration-minutes" type="u"> <range min="1" max="60"/> <default>30</default> - <summary>The alarm's duration.</summary> - <description> + <_summary>The alarm's duration.</_summary> + <_description> How long the alarm's sound will be looped if its snap decision is not dismissed by the user. - </description> + </_description> </key> </schema> </schemalist> diff --git a/include/datetime/exporter.h b/include/datetime/exporter.h index c228cc1..dd57263 100644 --- a/include/datetime/exporter.h +++ b/include/datetime/exporter.h @@ -22,11 +22,10 @@ #include <datetime/actions.h> #include <datetime/menu.h> +#include <datetime/settings.h> #include <core/signal.h> -#include <gio/gio.h> // GActionGroup - #include <memory> // std::shared_ptr #include <vector> @@ -40,31 +39,21 @@ namespace datetime { class Exporter { public: - Exporter() =default; + Exporter(const std::shared_ptr<Settings>&); ~Exporter(); - core::Signal<> name_lost; + core::Signal<>& name_lost(); void publish(const std::shared_ptr<Actions>& actions, const std::vector<std::shared_ptr<Menu>>& menus); private: - static void on_bus_acquired(GDBusConnection*, const gchar *name, gpointer gthis); - void on_bus_acquired(GDBusConnection*, const gchar *name); - - static void on_name_lost(GDBusConnection*, const gchar *name, gpointer gthis); - void on_name_lost(GDBusConnection*, const gchar *name); - - std::set<guint> m_exported_menu_ids; - guint m_own_id = 0; - guint m_exported_actions_id = 0; - GDBusConnection * m_dbus_connection = nullptr; - std::shared_ptr<Actions> m_actions; - std::vector<std::shared_ptr<Menu>> m_menus; + class Impl; + std::unique_ptr<Impl> p; - // we've got raw pointers and gsignal tags in here, so disable copying - Exporter(const Exporter&) =delete; - Exporter& operator=(const Exporter&) =delete; + // disable copying + Exporter(const Exporter&) =delete; + Exporter& operator=(const Exporter&) =delete; }; } // namespace datetime diff --git a/include/datetime/settings-shared.h b/include/datetime/settings-shared.h index bfddd88..23d2e1c 100644 --- a/include/datetime/settings-shared.h +++ b/include/datetime/settings-shared.h @@ -30,16 +30,6 @@ typedef enum } TimeFormatMode; -typedef enum -{ - ALARM_VOLUME_VERY_QUIET, - ALARM_VOLUME_QUIET, - ALARM_VOLUME_NORMAL, - ALARM_VOLUME_LOUD, - ALARM_VOLUME_VERY_LOUD -} -AlarmVolume; - #define SETTINGS_INTERFACE "com.canonical.indicator.datetime" #define SETTINGS_SHOW_CLOCK_S "show-clock" #define SETTINGS_TIME_FORMAT_S "time-format" diff --git a/include/datetime/settings.h b/include/datetime/settings.h index a941f05..e5f885e 100644 --- a/include/datetime/settings.h +++ b/include/datetime/settings.h @@ -57,8 +57,8 @@ public: core::Property<TimeFormatMode> time_format_mode; core::Property<std::string> timezone_name; core::Property<std::string> alarm_sound; - core::Property<AlarmVolume> alarm_volume; - core::Property<int> alarm_duration; + core::Property<unsigned int> alarm_volume; + core::Property<unsigned int> alarm_duration; }; } // namespace datetime diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ffa1523..af09c71 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,13 +1,13 @@ set (SERVICE_LIB "indicatordatetimeservice") set (SERVICE_EXEC "indicator-datetime-service") -SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") - add_definitions (-DTIMEZONE_FILE="/etc/timezone" -DG_LOG_DOMAIN="Indicator-Datetime") -set (SERVICE_SOURCES +# handwritten sources +set (SERVICE_C_SOURCES + utils.c) +set (SERVICE_CXX_SOURCES actions.cpp actions-live.cpp alarm-queue-simple.cpp @@ -32,16 +32,33 @@ set (SERVICE_SOURCES timezones-live.cpp utils.c wakeup-timer-mainloop.cpp) - if (HAVE_UBUNTU_HW_ALARM_H) - set (SERVICE_SOURCES ${SERVICE_SOURCES} wakeup-timer-uha.cpp) + set (SERVICE_CXX_SOURCES ${SERVICE_CXX_SOURCES} wakeup-timer-uha.cpp) endif () -add_library (${SERVICE_LIB} STATIC ${SERVICE_SOURCES}) +# generated sources +include (GdbusCodegen) +set(SERVICE_GENERATED_SOURCES) +add_gdbus_codegen(SERVICE_GENERATED_SOURCES dbus-alarm-properties + com.canonical.indicator + ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.datetime.AlarmProperties.xml) + +# add warnings/coverage info on handwritten files +# but not the autogenerated ones... +set_source_files_properties(${SERVICE_CXX_SOURCES} + PROPERTIES COMPILE_FLAGS "${CXX_WARNING_ARGS} ${GCOV_FLAGS} -g -std=c++11") +set_source_files_properties(${SERVICE_C_SOURCES} + PROPERTIES COMPILE_FLAGS "${CXX_WARNING_ARGS} ${GCOV_FLAGS} -g -std=c99") + +# add the bin dir to our include path so our code can find the generated header files +include_directories (${CMAKE_CURRENT_BINARY_DIR}) + +add_library (${SERVICE_LIB} STATIC ${SERVICE_C_SOURCES} ${SERVICE_CXX_SOURCES} ${SERVICE_GENERATED_SOURCES}) include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) add_executable (${SERVICE_EXEC} main.cpp) +set_source_files_properties(${SERVICE_SOURCES} main.cpp PROPERTIES COMPILE_FLAGS "${CXX_WARNING_ARGS} -g -std=c++11") target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 80a47da..58be0c4 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -41,8 +41,7 @@ class EdsEngine::Impl { public: - Impl(EdsEngine& owner): - m_owner(owner), + Impl(): m_cancellable(g_cancellable_new()) { e_source_registry_new(m_cancellable, on_source_registry_ready, this); @@ -492,7 +491,6 @@ private: return G_SOURCE_CONTINUE; } - EdsEngine& m_owner; core::Signal<> m_changed; std::set<ESource*> m_sources; std::map<ESource*,ECalClient*> m_clients; @@ -508,7 +506,7 @@ private: ***/ EdsEngine::EdsEngine(): - p(new Impl(*this)) + p(new Impl()) { } diff --git a/src/exporter.cpp b/src/exporter.cpp index ccd6e5c..e2b60f2 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -20,6 +20,8 @@ #include <datetime/dbus-shared.h> #include <datetime/exporter.h> +#include "dbus-alarm-properties.h" + #include <glib/gi18n.h> #include <gio/gio.h> @@ -31,108 +33,223 @@ namespace datetime { **** ***/ -Exporter::~Exporter() +class Exporter::Impl { - if (m_dbus_connection != nullptr) +public: + + Impl(const std::shared_ptr<Settings>& settings): + m_settings(settings), + m_alarm_props(datetime_alarm_properties_skeleton_new()) + { + alarm_properties_init(); + } + + ~Impl() { - for(auto& id : m_exported_menu_ids) - g_dbus_connection_unexport_menu_model(m_dbus_connection, id); + if (m_bus != nullptr) + { + for(auto& id : m_exported_menu_ids) + g_dbus_connection_unexport_menu_model(m_bus, id); - if (m_exported_actions_id) - g_dbus_connection_unexport_action_group(m_dbus_connection, m_exported_actions_id); + if (m_exported_actions_id) + g_dbus_connection_unexport_action_group(m_bus, m_exported_actions_id); + } + + g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(m_alarm_props)); + g_clear_object(&m_alarm_props); + + if (m_own_id) + g_bus_unown_name(m_own_id); + + g_clear_object(&m_bus); } - if (m_own_id) - g_bus_unown_name(m_own_id); + core::Signal<> name_lost; - g_clear_object(&m_dbus_connection); -} + void publish(const std::shared_ptr<Actions>& actions, + const std::vector<std::shared_ptr<Menu>>& menus) + { + m_actions = actions; + m_menus = menus; + m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + nullptr, + on_name_lost, + this, + nullptr); + } -/*** -**** -***/ +private: -void -Exporter::on_bus_acquired(GDBusConnection* connection, const gchar* name, gpointer gthis) -{ - g_debug("bus acquired: %s", name); - static_cast<Exporter*>(gthis)->on_bus_acquired(connection, name); -} + /*** + **** + ***/ -void -Exporter::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) -{ - m_dbus_connection = static_cast<GDBusConnection*>(g_object_ref(G_OBJECT(connection))); - - // export the actions - GError * error = nullptr; - const auto id = g_dbus_connection_export_action_group(m_dbus_connection, - BUS_PATH, - m_actions->action_group(), - &error); - if (id) + static void + on_gobject_notify_string(GObject* o, GParamSpec* pspec, gpointer p) + { + gchar* val = nullptr; + g_object_get (o, pspec->name, &val, nullptr); + static_cast<core::Property<std::string>*>(p)->set(val); + g_free(val); + } + void bind_string_property(gpointer o, const char* propname, + core::Property<std::string>& p) + { + // initialize the GObject property from the Settings + g_object_set(o, propname, p.get().c_str(), nullptr); + + // when the GObject changes, update the Settings + const std::string notify_propname = std::string("notify::") + propname; + g_signal_connect(o, notify_propname.c_str(), + G_CALLBACK(on_gobject_notify_string), &p); + + // when the Settings changes, update the GObject + p.changed().connect([o, propname](const std::string& val){ + g_object_set(o, propname, val.c_str(), nullptr); + }); + } + + + static void + on_gobject_notify_uint(GObject* o, GParamSpec* pspec, gpointer p) + { + uint val = 0; + g_object_get (o, pspec->name, &val, nullptr); + static_cast<core::Property<unsigned int>*>(p)->set(val); + } + void bind_uint_property(gpointer o, + const char* propname, + core::Property<unsigned int>& p) { - m_exported_actions_id = id; + // initialize the GObject property from the Settings + g_object_set(o, propname, p.get(), nullptr); + + // when the GObject changes, update the Settings + const std::string notify_propname = std::string("notify::") + propname; + g_signal_connect(o, notify_propname.c_str(), + G_CALLBACK(on_gobject_notify_uint), &p); + + // when the Settings changes, update the GObject + p.changed().connect([o, propname](unsigned int val){ + g_object_set(o, propname, val, nullptr); + }); } - else + + + void alarm_properties_init() { - g_warning("cannot export action group: %s", error->message); - g_clear_error(&error); + bind_uint_property(m_alarm_props, "duration", m_settings->alarm_duration); + bind_uint_property(m_alarm_props, "default-volume", m_settings->alarm_volume); + bind_string_property(m_alarm_props, "default-sound", m_settings->alarm_sound); } - // export the menus - for(auto& menu : m_menus) + /*** + **** + ***/ + + static void on_bus_acquired(GDBusConnection* connection, + const gchar* name, + gpointer gthis) { - const auto path = std::string(BUS_PATH) + "/" + menu->name(); - const auto id = g_dbus_connection_export_menu_model(m_dbus_connection, path.c_str(), menu->menu_model(), &error); + g_debug("bus acquired: %s", name); + static_cast<Impl*>(gthis)->on_bus_acquired(connection, name); + } + + void on_bus_acquired(GDBusConnection* bus, const gchar* /*name*/) + { + m_bus = static_cast<GDBusConnection*>(g_object_ref(G_OBJECT(bus))); + + // export the alarm properties + GError * error = nullptr; + g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(m_alarm_props), + m_bus, + BUS_PATH"/AlarmProperties", + &error); + + // export the actions + const auto id = g_dbus_connection_export_action_group(m_bus, + BUS_PATH, + m_actions->action_group(), + &error); if (id) { - m_exported_menu_ids.insert(id); + m_exported_actions_id = id; } else { - if (error != nullptr) - g_warning("cannot export %s menu: %s", menu->name().c_str(), error->message); + g_warning("cannot export action group: %s", error->message); g_clear_error(&error); } + + // export the menus + for(auto& menu : m_menus) + { + const auto path = std::string(BUS_PATH) + "/" + menu->name(); + const auto id = g_dbus_connection_export_menu_model(m_bus, path.c_str(), menu->menu_model(), &error); + if (id) + { + m_exported_menu_ids.insert(id); + } + else + { + if (error != nullptr) + g_warning("cannot export %s menu: %s", menu->name().c_str(), error->message); + g_clear_error(&error); + } + } } -} + + /*** + **** + ***/ + + static void on_name_lost(GDBusConnection*, const gchar* name, gpointer gthis) + { + g_debug("name lost: %s", name); + static_cast<Impl*>(gthis)->name_lost(); + } + + /*** + **** + ***/ + + std::shared_ptr<Settings> m_settings; + std::set<guint> m_exported_menu_ids; + guint m_own_id = 0; + guint m_exported_actions_id = 0; + GDBusConnection* m_bus = nullptr; + std::shared_ptr<Actions> m_actions; + std::vector<std::shared_ptr<Menu>> m_menus; + DatetimeAlarmProperties* m_alarm_props = nullptr; +}; + /*** **** ***/ -void -Exporter::on_name_lost(GDBusConnection* connection, const gchar* name, gpointer gthis) +Exporter::Exporter(const std::shared_ptr<Settings>& settings): + p(new Impl(settings)) { - g_debug("name lost: %s", name); - static_cast<Exporter*>(gthis)->on_name_lost(connection, name); } -void -Exporter::on_name_lost(GDBusConnection* /*connection*/, const gchar* /*name*/) + +Exporter::~Exporter() { - name_lost(); } -/*** -**** -***/ +core::Signal<>& Exporter::name_lost() +{ + return p->name_lost; +} -void -Exporter::publish(const std::shared_ptr<Actions>& actions, - const std::vector<std::shared_ptr<Menu>>& menus) +void Exporter::publish(const std::shared_ptr<Actions>& actions, + const std::vector<std::shared_ptr<Menu>>& menus) { - m_actions = actions; - m_menus = menus; - m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, - BUS_NAME, - G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, - on_bus_acquired, - nullptr, - on_name_lost, - this, - nullptr); + p->publish(actions, menus); } /*** diff --git a/src/main.cpp b/src/main.cpp index 1940eb6..cc81cd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -163,8 +163,8 @@ main(int /*argc*/, char** /*argv*/) // export them & run until we lose the busname auto loop = g_main_loop_new(nullptr, false); - Exporter exporter; - exporter.name_lost.connect([loop](){ + Exporter exporter(state->settings); + exporter.name_lost().connect([loop](){ g_message("%s exiting; failed/lost bus ownership", GETTEXT_PACKAGE); g_main_loop_quit(loop); }); diff --git a/src/settings-live.cpp b/src/settings-live.cpp index e34ace1..71bbd96 100644 --- a/src/settings-live.cpp +++ b/src/settings-live.cpp @@ -123,12 +123,12 @@ LiveSettings::LiveSettings(): g_settings_set_string(m_settings, SETTINGS_ALARM_SOUND_S, value.c_str()); }); - alarm_volume.changed().connect([this](AlarmVolume value){ - g_settings_set_enum(m_settings, SETTINGS_ALARM_VOLUME_S, gint(value)); + alarm_volume.changed().connect([this](unsigned int value){ + g_settings_set_uint(m_settings, SETTINGS_ALARM_VOLUME_S, value); }); - alarm_duration.changed().connect([this](int value){ - g_settings_set_int(m_settings, SETTINGS_ALARM_DURATION_S, value); + alarm_duration.changed().connect([this](unsigned int value){ + g_settings_set_uint(m_settings, SETTINGS_ALARM_DURATION_S, value); }); } @@ -229,12 +229,12 @@ void LiveSettings::update_alarm_sound() void LiveSettings::update_alarm_volume() { - alarm_volume.set((AlarmVolume)g_settings_get_enum(m_settings, SETTINGS_ALARM_VOLUME_S)); + alarm_volume.set(g_settings_get_uint(m_settings, SETTINGS_ALARM_VOLUME_S)); } void LiveSettings::update_alarm_duration() { - alarm_duration.set(g_settings_get_int(m_settings, SETTINGS_ALARM_DURATION_S)); + alarm_duration.set(g_settings_get_uint(m_settings, SETTINGS_ALARM_DURATION_S)); } /*** diff --git a/src/snap.cpp b/src/snap.cpp index 25632e9..eab7001 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -54,13 +54,12 @@ public: Sound(const std::shared_ptr<Clock>& clock, const std::string& filename, - AlarmVolume volume, - int duration_minutes, + unsigned int volume, + unsigned int duration_minutes, bool loop): m_clock(clock), m_filename(filename), m_volume(volume), - m_duration_minutes(duration_minutes), m_loop(loop), m_canberra_id(get_next_canberra_id()), m_loop_end_time(clock->localtime().add_full(0, 0, 0, 0, duration_minutes, 0.0)) @@ -133,18 +132,17 @@ private: g_clear_pointer(&props, ca_proplist_destroy); } - static float get_gain_level(const AlarmVolume volume) + static float get_gain_level(unsigned int volume) { - /* These values aren't set in stone -- - arrived at from from manual tests on Nexus 4 */ - switch (volume) - { - case ALARM_VOLUME_VERY_QUIET: return -8; - case ALARM_VOLUME_QUIET: return -4; - case ALARM_VOLUME_LOUD: return 4; - case ALARM_VOLUME_VERY_LOUD: return 8; - default: return 0; - } + const unsigned int clamped_volume = CLAMP(volume, 1, 100); + + /* This range isn't set in stone -- + arrived at from manual tests on Nextus 4 */ + constexpr float gain_low = -10; + constexpr float gain_high = 10; + + constexpr float gain_range = gain_high - gain_low; + return gain_low + (gain_range * (clamped_volume / 100.0f)); } static void on_done_playing(ca_context*, uint32_t, int rv, void* gself) @@ -183,8 +181,7 @@ private: const std::shared_ptr<Clock> m_clock; const std::string m_filename; - const AlarmVolume m_volume; - const int m_duration_minutes; + const unsigned int m_volume; const bool m_loop; const int32_t m_canberra_id; const DateTime m_loop_end_time; @@ -197,8 +194,8 @@ class SoundBuilder public: void set_clock(const std::shared_ptr<Clock>& c) {m_clock = c;} void set_filename(const std::string& s) {m_filename = s;} - void set_volume(const AlarmVolume v) {m_volume = v;} - void set_duration_minutes(int i) {m_duration_minutes=i;} + void set_volume(const unsigned int v) {m_volume = v;} + void set_duration_minutes(int unsigned i) {m_duration_minutes=i;} void set_looping(bool b) {m_looping=b;} Sound* operator()() { @@ -212,8 +209,8 @@ public: private: std::shared_ptr<Clock> m_clock; std::string m_filename; - AlarmVolume m_volume = ALARM_VOLUME_NORMAL; - int m_duration_minutes = 30; + unsigned int m_volume = 50; + unsigned int m_duration_minutes = 30; bool m_looping = true; }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fe6d7eb..958e1cc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,15 +21,17 @@ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compil OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE OUTPUT_STRIP_TRAILING_WHITESPACE) add_custom_command (OUTPUT gschemas.compiled - DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.datetime.gschema.xml - COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR} + DEPENDS ${CMAKE_BINARY_DIR}/data/com.canonical.indicator.datetime.gschema.xml + COMMAND cp -f ${CMAKE_BINARY_DIR}/data/*gschema.xml ${SCHEMA_DIR} COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) # look for headers in our src dir, and also in the directories where we autogenerate files... include_directories (${CMAKE_SOURCE_DIR}/src) +include_directories (${CMAKE_BINARY_DIR}/src) include_directories (${CMAKE_CURRENT_BINARY_DIR}) include_directories (${DBUSTEST_INCLUDE_DIRS}) + add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") diff --git a/tests/manual b/tests/manual index 17b4778..ecbcebb 100644 --- a/tests/manual +++ b/tests/manual @@ -22,3 +22,25 @@ Test-case indicator-datetime/unity8-items-check <dd>The menu is populated with items</dd> </dl> +Test-case indicator-datetime/new-alarm-wakeup +<dl> + <dt>Create and save an upcoming alarm in ubuntu-clock-app</dt> + <dt>Unplug the phone from any USB connection and put it to sleep</dt> + <dd>Confirm that the alarm sounds on time even if the phone is asleep. + (Note: if in doubt about sleep you can see in the syslog whether the + device actually suspended or whether the suspend was aborted)</dd> +</dl> + +Test-case indicator-datetime/edited-alarm-wakeup +<dl> + <dt>Edit an alarm that's already passed. (see previous test)</dt> + <dt>Unplug the phone from any USB connection and put it to sleep</dt> + <dd>Confirm that the alarm sounds on time even if the phone is asleep. + (Note: if in doubt about sleep you can see in the syslog whether the + device actually suspended or whether the suspend was aborted)</dd> +</dl> + +<strong> + If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result. + If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result</strong>. + diff --git a/tests/manual-test-snap.cpp b/tests/manual-test-snap.cpp index 90dbe08..cc24a67 100644 --- a/tests/manual-test-snap.cpp +++ b/tests/manual-test-snap.cpp @@ -41,10 +41,29 @@ namespace g_main_loop_quit(static_cast<GMainLoop*>(gloop)); return G_SOURCE_REMOVE; }; + + int volume = 50; + + GOptionEntry entries[] = + { + { "volume", 'v', 0, G_OPTION_ARG_INT, &volume, "Volume level [1..100]", "volume" }, + { NULL } + }; } -int main() +int main(int argc, const char* argv[]) { + GError* error = nullptr; + GOptionContext* context = g_option_context_new(nullptr); + g_option_context_add_main_entries(context, entries, nullptr); + if (!g_option_context_parse(context, &argc, (gchar***)&argv, &error)) + { + g_print("option parsing failed: %s\n", error->message); + exit(1); + } + g_option_context_free(context); + volume = CLAMP(volume, 1, 100); + Appointment a; a.color = "green"; a.summary = "Alarm"; @@ -74,6 +93,7 @@ int main() g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); auto settings = std::make_shared<LiveSettings>(); + settings->alarm_volume.set(volume); auto timezones = std::make_shared<LiveTimezones>(settings, TIMEZONE_FILE); auto clock = std::make_shared<LiveClock>(timezones); Snap snap (clock, settings); diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index a255ef9..e947740 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -21,6 +21,8 @@ #include "state-mock.h" #include "glib-fixture.h" +#include "dbus-alarm-properties.h" + #include <datetime/actions.h> #include <datetime/dbus-shared.h> #include <datetime/exporter.h> @@ -76,13 +78,14 @@ TEST_F(ExporterFixture, Publish) { std::shared_ptr<State> state(new MockState); std::shared_ptr<Actions> actions(new MockActions(state)); + std::shared_ptr<Settings> settings(new Settings); std::vector<std::shared_ptr<Menu>> menus; MenuFactory menu_factory (actions, state); for(int i=0; i<Menu::NUM_PROFILES; i++) menus.push_back(menu_factory.buildMenu(Menu::Profile(i))); - Exporter exporter; + Exporter exporter(settings); exporter.publish(actions, menus); wait_msec(); @@ -122,7 +125,7 @@ TEST_F(ExporterFixture, Publish) // try closing the connection prematurely // to test Exporter's name-lost signal bool name_lost = false; - exporter.name_lost.connect([this,&name_lost](){ + exporter.name_lost().connect([this,&name_lost](){ name_lost = true; g_main_loop_quit(loop); }); @@ -135,3 +138,91 @@ TEST_F(ExporterFixture, Publish) g_clear_object(&exported); g_clear_object(&connection); } + +TEST_F(ExporterFixture, AlarmProperties) +{ + /*** + **** Set up the exporter + ***/ + + std::shared_ptr<State> state(new MockState); + std::shared_ptr<Actions> actions(new MockActions(state)); + std::shared_ptr<Settings> settings(new Settings); + std::vector<std::shared_ptr<Menu>> menus; + + MenuFactory menu_factory (actions, state); + for(int i=0; i<Menu::NUM_PROFILES; i++) + menus.push_back(menu_factory.buildMenu(Menu::Profile(i))); + + Exporter exporter(settings); + exporter.publish(actions, menus); + wait_msec(); + + /*** + **** Set up the proxy + ***/ + + auto on_proxy_ready = [](GObject*, GAsyncResult* res, gpointer gproxy){ + GError* error = nullptr; + *reinterpret_cast<DatetimeAlarmProperties**>(gproxy) = datetime_alarm_properties_proxy_new_for_bus_finish(res, &error); + EXPECT_TRUE(error == nullptr); + }; + + DatetimeAlarmProperties* proxy = nullptr; + datetime_alarm_properties_proxy_new_for_bus(G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + BUS_NAME, + BUS_PATH"/AlarmProperties", + nullptr, + on_proxy_ready, + &proxy); + wait_msec(100); + ASSERT_TRUE(proxy != nullptr); + + /*** + **** Try changing the Settings -- do the DBus properties change to match it? + ***/ + + auto expected_volume = 1; + int expected_duration = 60; + const char * expected_sound = "/tmp/foo.wav"; + settings->alarm_volume.set(expected_volume); + settings->alarm_duration.set(expected_duration); + settings->alarm_sound.set(expected_sound); + wait_msec(); + + static constexpr const char* const SOUND_PROP {"default-sound"}; + static constexpr const char* const VOLUME_PROP {"default-volume"}; + static constexpr const char* const DURATION_PROP {"duration"}; + + char* sound = nullptr; + int volume = -1; + int duration = -1; + g_object_get(proxy, SOUND_PROP, &sound, + VOLUME_PROP, &volume, + DURATION_PROP, &duration, + nullptr); + EXPECT_STREQ(expected_sound, sound); + EXPECT_EQ(expected_volume, volume); + EXPECT_EQ(expected_duration, duration); + + /*** + **** Try chaning the DBus properties -- do the Settings change to match it? + ***/ + + expected_volume = 100; + expected_duration = 30; + expected_sound = "/tmp/bar.wav"; + g_object_set(proxy, SOUND_PROP, expected_sound, + VOLUME_PROP, expected_volume, + DURATION_PROP, expected_duration, + nullptr); + wait_msec(); + + EXPECT_STREQ(expected_sound, settings->alarm_sound.get().c_str()); + EXPECT_EQ(expected_volume, settings->alarm_volume.get()); + EXPECT_EQ(expected_duration, settings->alarm_duration.get()); + + // cleanup + g_clear_object(&proxy); +} diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index b485037..f5f8df2 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -484,7 +484,7 @@ TEST_F(MenuFixture, HelloWorld) EXPECT_EQ(Menu::NUM_PROFILES, m_menus.size()); for (int i=0; i<Menu::NUM_PROFILES; i++) { - EXPECT_TRUE(m_menus[i] != false); + EXPECT_TRUE(m_menus[i].get() != nullptr); EXPECT_TRUE(m_menus[i]->menu_model() != nullptr); EXPECT_EQ(i, m_menus[i]->profile()); } diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp index 2b500b2..44a0252 100644 --- a/tests/test-settings.cpp +++ b/tests/test-settings.cpp @@ -101,26 +101,26 @@ protected: g_clear_pointer(&tmp, g_free); } - void TestIntProperty(core::Property<int>& property, const gchar* key) + void TestUIntProperty(core::Property<unsigned int>& property, const gchar* key) { - EXPECT_EQ(g_settings_get_int(m_gsettings, key), property.get()); + EXPECT_EQ(g_settings_get_uint(m_gsettings, key), property.get()); - int expected_values[] = { 1, 2, 3 }; + unsigned int expected_values[] = { 1, 2, 3 }; // modify GSettings and confirm that the new value is propagated - for(const int& expected_value : expected_values) + for(const auto& expected_value : expected_values) { - g_settings_set_int(m_gsettings, key, expected_value); + g_settings_set_uint(m_gsettings, key, expected_value); EXPECT_EQ(expected_value, property.get()); - EXPECT_EQ(expected_value, g_settings_get_int(m_gsettings, key)); + EXPECT_EQ(expected_value, g_settings_get_uint(m_gsettings, key)); } // modify the property and confirm that the new value is propagated - for(const int& expected_value : expected_values) + for(const auto& expected_value : expected_values) { property.set(expected_value); EXPECT_EQ(expected_value, property.get()); - EXPECT_EQ(expected_value, g_settings_get_int(m_gsettings, key)); + EXPECT_EQ(expected_value, g_settings_get_uint(m_gsettings, key)); } } }; @@ -148,9 +148,10 @@ TEST_F(SettingsFixture, BoolProperties) TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); } -TEST_F(SettingsFixture, IntProperties) +TEST_F(SettingsFixture, UIntProperties) { - TestIntProperty(m_settings->alarm_duration, SETTINGS_ALARM_DURATION_S); + TestUIntProperty(m_settings->alarm_duration, SETTINGS_ALARM_DURATION_S); + TestUIntProperty(m_settings->alarm_volume, SETTINGS_ALARM_VOLUME_S); } TEST_F(SettingsFixture, StringProperties) @@ -181,31 +182,6 @@ TEST_F(SettingsFixture, TimeFormatMode) } } -TEST_F(SettingsFixture, AlarmVolume) -{ - const auto key = SETTINGS_ALARM_VOLUME_S; - const AlarmVolume volumes[] = { ALARM_VOLUME_VERY_QUIET, - ALARM_VOLUME_QUIET, - ALARM_VOLUME_NORMAL, - ALARM_VOLUME_LOUD, - ALARM_VOLUME_VERY_LOUD }; - - for(const auto& val : volumes) - { - g_settings_set_enum(m_gsettings, key, val); - EXPECT_EQ(val, m_settings->alarm_volume.get()); - EXPECT_EQ(val, g_settings_get_enum(m_gsettings, key)); - } - - for(const auto& val : volumes) - { - m_settings->alarm_volume.set(val); - EXPECT_EQ(val, m_settings->alarm_volume.get()); - EXPECT_EQ(val, g_settings_get_enum(m_gsettings, key)); - } -} - - namespace { std::vector<std::string> strv_to_vector(const gchar** strv) |