aboutsummaryrefslogtreecommitdiff
path: root/src/notifications.cpp
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2014-08-08 20:52:31 +0000
committerCI bot <ps-jenkins@lists.canonical.com>2014-08-08 20:52:31 +0000
commit0777142ba348c212f57af039c6f7b2ae15d5058f (patch)
treec4998c28ec72c215ccb8bff40ef5851e826358ac /src/notifications.cpp
parent23b0a5d6d97b8e36443a2a719b879f99975ef972 (diff)
parent9cc6380c1cb0c4e96a893a1b9d2720d2ed3181bd (diff)
downloadayatana-indicator-datetime-0777142ba348c212f57af039c6f7b2ae15d5058f.tar.gz
ayatana-indicator-datetime-0777142ba348c212f57af039c6f7b2ae15d5058f.tar.bz2
ayatana-indicator-datetime-0777142ba348c212f57af039c6f7b2ae15d5058f.zip
Add haptic feedback support for alarms. Fixes: 1350017
Approved by: Antti Kaijanmäki, PS Jenkins bot, Nekhelesh Ramananthan
Diffstat (limited to 'src/notifications.cpp')
-rw-r--r--src/notifications.cpp370
1 files changed, 370 insertions, 0 deletions
diff --git a/src/notifications.cpp b/src/notifications.cpp
new file mode 100644
index 0000000..18f15d9
--- /dev/null
+++ b/src/notifications.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2014 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <notifications/notifications.h>
+
+#include <libnotify/notify.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace unity {
+namespace indicator {
+namespace notifications {
+
+static G_DEFINE_QUARK(NotificationKey, notification_key)
+
+static G_DEFINE_QUARK(NotificationAction, notification_action)
+
+/***
+****
+***/
+
+class Builder::Impl
+{
+public:
+ std::string m_title;
+ std::string m_body;
+ std::string m_icon_name;
+ std::chrono::seconds m_duration;
+ std::set<std::string> m_string_hints;
+ std::vector<std::pair<std::string,std::string>> m_actions;
+ std::function<void(const std::string&)> m_closed_callback;
+};
+
+Builder::Builder():
+ impl(new Impl())
+{
+}
+
+Builder::~Builder()
+{
+}
+
+void
+Builder::set_title (const std::string& title)
+{
+ impl->m_title = title;
+}
+
+void
+Builder::set_body (const std::string& body)
+{
+ impl->m_body = body;
+}
+
+void
+Builder::set_icon_name (const std::string& icon_name)
+{
+ impl->m_icon_name = icon_name;
+}
+
+void
+Builder::set_timeout (const std::chrono::seconds& duration)
+{
+ impl->m_duration = duration;
+}
+
+void
+Builder::add_hint (const std::string& name)
+{
+ impl->m_string_hints.insert (name);
+}
+
+void
+Builder::add_action (const std::string& action, const std::string& label)
+{
+ impl->m_actions.push_back(std::pair<std::string,std::string>(action,label));
+}
+
+void
+Builder::set_closed_callback (std::function<void (const std::string&)> cb)
+{
+ impl->m_closed_callback.swap (cb);
+}
+
+/***
+****
+***/
+
+class Engine::Impl
+{
+ struct notification_data
+ {
+ std::shared_ptr<NotifyNotification> nn;
+ std::function<void(const std::string&)> closed_callback;
+ };
+
+public:
+
+ Impl(const std::string& app_name):
+ m_app_name(app_name)
+ {
+ if (!notify_init(app_name.c_str()))
+ g_critical("Unable to initialize libnotify!");
+ }
+
+ ~Impl()
+ {
+ close_all ();
+
+ notify_uninit ();
+ }
+
+ const std::string& app_name() const
+ {
+ return m_app_name;
+ }
+
+ bool supports_actions() const
+ {
+ return server_caps().count("actions") != 0;
+ }
+
+ void close_all ()
+ {
+ // call close() on all our keys
+
+ std::set<int> keys;
+ for (const auto& it : m_notifications)
+ keys.insert (it.first);
+
+ for (const int key : keys)
+ close (key);
+ }
+
+ void close (int key)
+ {
+ auto it = m_notifications.find(key);
+ if (it != m_notifications.end())
+ {
+ // tell the server to close the notification
+ GError * error = nullptr;
+ if (!notify_notification_close (it->second.nn.get(), &error))
+ {
+ g_warning ("Unable to close notification %d: %s", key, error->message);
+ g_error_free (error);
+ }
+
+ // call the user callback and remove it from our bookkeeping
+ remove_closed_notification (key);
+ }
+ }
+
+ int show (const Builder& builder)
+ {
+ int ret = -1;
+ const auto& info = *builder.impl;
+
+ std::shared_ptr<NotifyNotification> nn (
+ notify_notification_new(info.m_title.c_str(),
+ info.m_body.c_str(),
+ info.m_icon_name.c_str()),
+ [this](NotifyNotification * n) {
+ g_signal_handlers_disconnect_by_data(n, this);
+ g_object_unref (G_OBJECT(n));
+ }
+ );
+
+ if (info.m_duration.count() != 0)
+ {
+ const auto& d= info.m_duration;
+ auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(d);
+
+ notify_notification_set_hint (nn.get(),
+ HINT_TIMEOUT,
+ g_variant_new_int32(ms.count()));
+ }
+
+ for (const auto& hint : info.m_string_hints)
+ {
+ notify_notification_set_hint (nn.get(),
+ hint.c_str(),
+ g_variant_new_boolean(true));
+ }
+
+ for (const auto& action : info.m_actions)
+ {
+ notify_notification_add_action (nn.get(),
+ action.first.c_str(),
+ action.second.c_str(),
+ on_notification_clicked,
+ nullptr,
+ nullptr);
+ }
+
+ static int next_key = 1;
+ const int key = next_key++;
+ g_object_set_qdata (G_OBJECT(nn.get()),
+ notification_key_quark(),
+ GINT_TO_POINTER(key));
+
+ m_notifications[key] = { nn, info.m_closed_callback };
+ g_signal_connect (nn.get(), "closed",
+ G_CALLBACK(on_notification_closed), this);
+
+ GError * error = nullptr;
+ if (notify_notification_show(nn.get(), &error))
+ {
+ ret = key;
+ }
+ else
+ {
+ g_critical ("Unable to show notification for '%s': %s",
+ info.m_title.c_str(),
+ error->message);
+ g_error_free (error);
+ m_notifications.erase(key);
+ }
+
+ return ret;
+ }
+
+private:
+
+ const std::set<std::string>& server_caps() const
+ {
+ if (G_UNLIKELY(m_lazy_caps.empty()))
+ {
+ auto caps_gl = notify_get_server_caps();
+ std::string caps_str;
+ for(auto l=caps_gl; l!=nullptr; l=l->next)
+ {
+ m_lazy_caps.insert((const char*)l->data);
+
+ caps_str += (const char*) l->data;;
+ if (l->next != nullptr)
+ caps_str += ", ";
+ }
+
+ g_debug("%s notify_get_server() returned [%s]", G_STRFUNC, caps_str.c_str());
+ g_list_free_full(caps_gl, g_free);
+ }
+
+ return m_lazy_caps;
+ }
+
+ static void on_notification_clicked (NotifyNotification * nn,
+ char * action,
+ gpointer)
+ {
+ g_object_set_qdata_full (G_OBJECT(nn),
+ notification_action_quark(),
+ g_strdup(action),
+ g_free);
+ }
+
+ static void on_notification_closed (NotifyNotification * nn, gpointer gself)
+ {
+ const GQuark q = notification_key_quark();
+ const gpointer gkey = g_object_get_qdata(G_OBJECT(nn), q);
+ static_cast<Impl*>(gself)->remove_closed_notification(GPOINTER_TO_INT(gkey));
+ }
+
+ void remove_closed_notification (int key)
+ {
+ auto it = m_notifications.find(key);
+ g_return_if_fail (it != m_notifications.end());
+
+ const auto& ndata = it->second;
+ auto nn = ndata.nn.get();
+ if (ndata.closed_callback)
+ {
+ std::string action;
+
+ const GQuark q = notification_action_quark();
+ const gpointer p = g_object_get_qdata(G_OBJECT(nn), q);
+ if (p != nullptr)
+ action = static_cast<const char*>(p);
+
+ ndata.closed_callback (action);
+ }
+
+ m_notifications.erase(it);
+ }
+
+ /***
+ ****
+ ***/
+
+ const std::string m_app_name;
+
+ // key-to-data
+ std::map<int,notification_data> m_notifications;
+
+ // server capabilities.
+ // as the name indicates, don't use this directly: use server_caps() instead
+ mutable std::set<std::string> m_lazy_caps;
+
+ static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"};
+};
+
+/***
+****
+***/
+
+Engine::Engine(const std::string& app_name):
+ impl(new Impl(app_name))
+{
+}
+
+Engine::~Engine()
+{
+}
+
+bool
+Engine::supports_actions() const
+{
+ return impl->supports_actions();
+}
+
+int
+Engine::show(const Builder& builder)
+{
+ return impl->show(builder);
+}
+
+void
+Engine::close_all()
+{
+ impl->close_all();
+}
+
+void
+Engine::close(int key)
+{
+ impl->close(key);
+}
+
+const std::string&
+Engine::app_name() const
+{
+ return impl->app_name();
+}
+
+/***
+****
+***/
+
+} // namespace notifications
+} // namespace indicator
+} // namespace unity
+