aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--debian/control1
-rw-r--r--include/datetime/snap.h7
-rw-r--r--src/main.cpp15
-rw-r--r--src/snap.cpp146
-rw-r--r--tests/CMakeLists.txt4
-rw-r--r--tests/manual-test-snap.cpp63
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;
+}