diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | debian/control | 1 | ||||
-rw-r--r-- | include/datetime/snap.h | 7 | ||||
-rw-r--r-- | src/main.cpp | 15 | ||||
-rw-r--r-- | src/snap.cpp | 146 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 4 | ||||
-rw-r--r-- | tests/manual-test-snap.cpp | 63 |
7 files changed, 194 insertions, 43 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ab8cca4..3e6a810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ pkg_check_modules (SERVICE_DEPS REQUIRED libical>=0.48 libecal-1.2>=3.5 libedataserver-1.2>=3.5 + libcanberra>=0.12 libnotify>=0.7.6 url-dispatcher-1>=1 properties-cpp>=0.0.1) diff --git a/debian/control b/debian/control index 54d265d..76fc5a1 100644 --- a/debian/control +++ b/debian/control @@ -16,6 +16,7 @@ Build-Depends: cmake, libgtest-dev, libglib2.0-dev (>= 2.35.4), libnotify-dev (>= 0.7.6), + libcanberra-dev, libido3-0.1-dev (>= 0.2.90), libgeoclue-dev (>= 0.12.0), libecal1.2-dev (>= 3.5), diff --git a/include/datetime/snap.h b/include/datetime/snap.h index 584d895..a493772 100644 --- a/include/datetime/snap.h +++ b/include/datetime/snap.h @@ -23,6 +23,7 @@ #include <datetime/appointment.h> #include <memory> +#include <functional> namespace unity { namespace indicator { @@ -36,7 +37,11 @@ class Snap public: Snap(); virtual ~Snap(); - void operator()(const Appointment&); + + typedef std::function<void(const Appointment&)> appointment_func; + void operator()(const Appointment& appointment, + appointment_func show, + appointment_func dismiss); }; } // namespace datetime diff --git a/src/main.cpp b/src/main.cpp index 71a1ce5..7e09fda 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,8 +17,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ - - #include <datetime/actions-live.h> #include <datetime/clock.h> #include <datetime/clock-watcher.h> @@ -33,10 +31,11 @@ #include <glib/gi18n.h> // bindtextdomain() #include <gio/gio.h> -#include <libnotify/notify.h> + +#include <url-dispatcher.h> #include <locale.h> -#include <stdlib.h> // exit() +#include <cstdlib> // exit() using namespace unity::indicator::datetime; @@ -52,10 +51,6 @@ main(int /*argc*/, char** /*argv*/) bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain(GETTEXT_PACKAGE); - // init libnotify - if(!notify_init("indicator-datetime-service")) - g_critical("libnotify initialization failed"); - // build the state, actions, and menufactory std::shared_ptr<State> state(new State); std::shared_ptr<Settings> live_settings(new LiveSettings); @@ -73,7 +68,9 @@ main(int /*argc*/, char** /*argv*/) ClockWatcherImpl clock_watcher(state); Snap snap; clock_watcher.alarm_reached().connect([&snap](const Appointment& appt){ - snap(appt); + snap(appt, + [](const Appointment& a){url_dispatch_send(a.url.c_str(), nullptr, nullptr);}, + [](const Appointment&){}); }); // create the menus diff --git a/src/snap.cpp b/src/snap.cpp index a290f99..5f46dc7 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -22,13 +22,14 @@ #include <datetime/snap.h> #include <datetime/utils.h> // generate_full_format_string_at_time() -#include <url-dispatcher.h> - +#include <canberra.h> #include <libnotify/notify.h> #include <glib/gi18n.h> #include <glib.h> +#define ALARM_SOUND_FILENAME "/usr/share/sounds/ubuntu/stereo/phone-incoming-call.ogg" + namespace unity { namespace indicator { namespace datetime { @@ -40,77 +41,156 @@ namespace datetime { namespace { -void dispatch_alarm_url(const Appointment& appointment) +/** +*** libcanberra -- play sounds +**/ + +ca_context *c_context = nullptr; + +ca_context* get_ca_context() { - g_return_if_fail(!appointment.has_alarms); + if (G_UNLIKELY(c_context == nullptr)) + { + int rv; + if ((rv = ca_context_create(&c_context)) != CA_SUCCESS) + { + g_warning("Failed to create canberra context: %s\n", ca_strerror(rv)); + c_context = nullptr; + } + } - const auto fmt = appointment.begin.format("%F %T"); - g_debug("dispatching url \"%s\" for appointment \"%s\", which begins at %s", - appointment.url.c_str(), - appointment.summary.c_str(), - fmt.c_str()); + return c_context; +} + +void play_soundfile(const char* filename) +{ + auto context = get_ca_context(); + g_return_if_fail(context != nullptr); - url_dispatch_send(appointment.url.c_str(), nullptr, nullptr); + const auto rv = ca_context_play(context, 0, CA_PROP_MEDIA_FILENAME, filename, NULL); + if (rv != CA_SUCCESS) + g_warning("Failed to play file '%s': %s\n", filename, ca_strerror(rv)); } -void on_snap_decided(NotifyNotification * /*notification*/, - char * action, - gpointer gurl) +void play_alarm_sound() { - g_debug("%s: %s", G_STRFUNC, action); + play_soundfile(ALARM_SOUND_FILENAME); +} + +/** +*** libnotify -- snap decisions +**/ - if (!g_strcmp0(action, "show")) +void first_time_init() +{ + static bool inited = false; + + if (G_UNLIKELY(!inited)) { - const auto url = static_cast<const gchar*>(gurl); - g_debug("dispatching url '%s'", url); - url_dispatch_send(url, nullptr, nullptr); + inited = true; + + if(!notify_init("indicator-datetime-service")) + g_critical("libnotify initialization failed"); } } -} // unnamed namespace +struct SnapData +{ + Snap::appointment_func show; + Snap::appointment_func dismiss; + Appointment appointment; +}; -/*** -**** -***/ +void on_snap_show(NotifyNotification*, gchar* /*action*/, gpointer gdata) +{ + auto data = static_cast<SnapData*>(gdata); + data->show(data->appointment); +} -Snap::Snap() +void on_snap_dismiss(NotifyNotification*, gchar* /*action*/, gpointer gdata) { + auto data = static_cast<SnapData*>(gdata); + data->dismiss(data->appointment); } -Snap::~Snap() +void snap_data_destroy_notify(gpointer gdata) { + delete static_cast<SnapData*>(gdata); } -void Snap::operator()(const Appointment& appointment) +void show_snap_decision(SnapData* data) { - if (!appointment.has_alarms) - return; + const Appointment& appointment = data->appointment; - auto timestr = generate_full_format_string_at_time (appointment.begin.get(), nullptr, nullptr); + auto timestr = generate_full_format_string_at_time(appointment.begin.get(), nullptr, nullptr); auto title = g_strdup_printf(_("Alarm %s"), timestr); const auto body = appointment.summary; const gchar* icon_name = "alarm-clock"; - g_debug("creating a snap decision with title '%s', body '%s', icon '%s'", title, body.c_str(), icon_name); auto nn = notify_notification_new(title, body.c_str(), icon_name); notify_notification_set_hint_string(nn, "x-canonical-snap-decisions", "true"); notify_notification_set_hint_string(nn, "x-canonical-private-button-tint", "true"); - notify_notification_add_action(nn, "show", _("Show"), on_snap_decided, g_strdup(appointment.url.c_str()), g_free); - notify_notification_add_action(nn, "dismiss", _("Dismiss"), on_snap_decided, nullptr, nullptr); + notify_notification_add_action(nn, "show", _("Show"), on_snap_show, data, nullptr); + notify_notification_add_action(nn, "dismiss", _("Dismiss"), on_snap_dismiss, data, nullptr); + g_object_set_data_full(G_OBJECT(nn), "snap-data", data, snap_data_destroy_notify); GError * error = nullptr; notify_notification_show(nn, &error); if (error != NULL) { - g_warning("Unable to show alarm '%s' popup: %s", body.c_str(), error->message); + g_warning("Unable to show snap decision for '%s': %s", body.c_str(), error->message); g_error_free(error); - dispatch_alarm_url(appointment); + data->show(data->appointment); } g_free(title); g_free(timestr); } +/** +*** +**/ + +void notify(const Appointment& appointment, + Snap::appointment_func show, + Snap::appointment_func dismiss) +{ + auto data = new SnapData; + data->appointment = appointment; + data->show = show; + data->dismiss = dismiss; + + play_alarm_sound(); + show_snap_decision(data); +} + +} // unnamed namespace + + +/*** +**** +***/ + +Snap::Snap() +{ + first_time_init(); +} + +Snap::~Snap() +{ + g_clear_pointer(&c_context, ca_context_destroy); +} + +void Snap::operator()(const Appointment& appointment, + appointment_func show, + appointment_func dismiss) +{ + if (appointment.has_alarms) + notify(appointment, show, dismiss); + else + dismiss(appointment); +} + /*** **** ***/ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 06e40a7..7d590c9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,6 +53,10 @@ add_test_by_name(test-settings) add_test_by_name(test-timezone-file) add_test_by_name(test-utils) +set (TEST_NAME manual-test-snap) +add_executable (${TEST_NAME} ${TEST_NAME}.cpp) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) # disabling the timezone unit tests because they require # https://code.launchpad.net/~ted/dbus-test-runner/multi-interface-test/+merge/199724 diff --git a/tests/manual-test-snap.cpp b/tests/manual-test-snap.cpp new file mode 100644 index 0000000..51556cd --- /dev/null +++ b/tests/manual-test-snap.cpp @@ -0,0 +1,63 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <datetime/appointment.h> +#include <datetime/snap.h> + +#include <glib.h> + +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +int main() +{ + Appointment a; + a.color = "green"; + a.summary = "Alarm"; + a.url = "alarm:///hello-world"; + a.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; + a.is_event = false; + a.is_daily = false; + a.has_alarms = true; + auto begin = g_date_time_new_local(2014,12,25,0,0,0); + auto end = g_date_time_add_full(begin,0,0,1,0,0,-1); + a.begin = begin; + a.end = end; + g_date_time_unref(end); + g_date_time_unref(begin); + + auto loop = g_main_loop_new(nullptr, false); + auto show = [loop](const Appointment& appt){ + g_message("You clicked 'show' for appt url '%s'", appt.url.c_str()); + g_main_loop_quit(loop); + }; + auto dismiss = [loop](const Appointment&){ + g_message("You clicked 'dismiss'"); + g_main_loop_quit(loop); + }; + + Snap snap; + snap(a, show, dismiss); + g_main_loop_run(loop); + return 0; +} |