aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/main.c12
-rw-r--r--src/notifier.c336
-rw-r--r--src/notifier.h68
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/test-notify.cc59
6 files changed, 474 insertions, 4 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a39b945..4747b12 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,6 +10,7 @@ set(SERVICE_MANUAL_SOURCES
ib-brightness-uscreen-control.c
device-provider.c
device.c
+ notifier.c
service.c)
# generated sources
diff --git a/src/main.c b/src/main.c
index 7363284..70b7f2a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -25,6 +25,7 @@
#include "device.h"
#include "device-provider-upower.h"
+#include "notifier.h"
#include "service.h"
/***
@@ -41,9 +42,10 @@ on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop)
int
main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
{
- GMainLoop * loop;
- IndicatorPowerService * service;
IndicatorPowerDeviceProvider * device_provider;
+ IndicatorPowerNotifier * notifier;
+ IndicatorPowerService * service;
+ GMainLoop * loop;
/* boilerplate i18n */
setlocale (LC_ALL, "");
@@ -52,6 +54,7 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
/* run */
device_provider = indicator_power_device_provider_upower_new ();
+ notifier = indicator_power_notifier_new (device_provider);
service = indicator_power_service_new (device_provider);
loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (service, INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST,
@@ -59,8 +62,9 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
g_main_loop_run (loop);
/* cleanup */
- g_clear_object (&device_provider);
- g_clear_object (&service);
g_main_loop_unref (loop);
+ g_clear_object (&service);
+ g_clear_object (&notifier);
+ g_clear_object (&device_provider);
return 0;
}
diff --git a/src/notifier.c b/src/notifier.c
new file mode 100644
index 0000000..79ea4cc
--- /dev/null
+++ b/src/notifier.c
@@ -0,0 +1,336 @@
+/*
+ * 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 "device.h"
+#include "device-provider.h"
+#include "notifier.h"
+#include "service.h"
+
+#include <libnotify/notify.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+G_DEFINE_TYPE(IndicatorPowerNotifier,
+ indicator_power_notifier,
+ G_TYPE_OBJECT)
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE_PROVIDER,
+ LAST_PROP
+};
+
+static GParamSpec * properties[LAST_PROP];
+
+static int n_notifiers = 0;
+
+struct _IndicatorPowerNotifierPrivate
+{
+ IndicatorPowerDeviceProvider * device_provider;
+ IndicatorPowerDevice * primary_device;
+ gdouble battery_level;
+ time_t time_remaining;
+ NotifyNotification* notify_notification;
+};
+
+typedef IndicatorPowerNotifierPrivate priv_t;
+
+/***
+****
+***/
+
+static void
+emit_critical_signal(IndicatorPowerNotifier * self G_GNUC_UNUSED)
+{
+ g_message("FIXME %s %s", G_STRFUNC, G_STRLOC);
+}
+
+static void
+emit_hide_signal(IndicatorPowerNotifier * self G_GNUC_UNUSED)
+{
+ g_message("FIXME %s %s", G_STRFUNC, G_STRLOC);
+}
+
+static void
+emit_show_signal(IndicatorPowerNotifier * self G_GNUC_UNUSED)
+{
+ g_message("FIXME %s %s", G_STRFUNC, G_STRLOC);
+}
+
+static void
+notification_clear(IndicatorPowerNotifier * self)
+{
+ priv_t * p = self->priv;
+
+ if (p->notify_notification != NULL)
+ {
+ notify_notification_clear_actions(p->notify_notification);
+ g_signal_handlers_disconnect_by_data(p->notify_notification, self);
+ g_clear_object(&p->notify_notification);
+ emit_hide_signal(self);
+ }
+}
+
+static void
+on_notification_clicked(NotifyNotification * notify_notification G_GNUC_UNUSED,
+ char * action G_GNUC_UNUSED,
+ gpointer gself G_GNUC_UNUSED)
+{
+ /* no-op */
+}
+
+static void
+notification_show(IndicatorPowerNotifier * self,
+ IndicatorPowerDevice * device)
+
+{
+ priv_t * p = self->priv;
+ char * body;
+ NotifyNotification * nn;
+
+ // if there's already a notification, tear it down
+ if (p->notify_notification != NULL)
+ {
+ notification_clear (self);
+ }
+
+ // create the notification
+ body = g_strdup_printf(_("%d%% charge remaining"), (int)indicator_power_device_get_percentage(device));
+ p->notify_notification = nn = notify_notification_new(_("Battery Low"), body, NULL);
+ notify_notification_set_hint(nn, "x-canonical-snap-decisions", g_variant_new_boolean(TRUE));
+ notify_notification_set_hint(nn, "x-canonical-private-button-tint", g_variant_new_boolean(TRUE));
+ notify_notification_add_action(nn, "OK", _("OK"), on_notification_clicked, self, NULL);
+ g_signal_connect_swapped(nn, "closed", G_CALLBACK(notification_clear), self);
+
+ // show the notification
+ GError* error = NULL;
+ notify_notification_show(nn, &error);
+ if (error != NULL)
+ {
+ g_critical("Unable to show snap decision for '%s': %s", body, error->message);
+ g_error_free(error);
+ }
+ else
+ {
+ emit_show_signal(self);
+ }
+
+ g_free (body);
+}
+
+static void
+on_battery_level_changed(IndicatorPowerNotifier * self G_GNUC_UNUSED,
+ IndicatorPowerDevice * device,
+ gdouble old_value,
+ gdouble new_value)
+{
+ static const double critical_level = 2.0;
+ static const double very_low_level = 5.0;
+ static const double low_level = 48.0;
+
+ if (indicator_power_device_get_state(device) != UP_DEVICE_STATE_DISCHARGING)
+ return;
+
+g_message ("%s - %s - %f - %f", G_STRFUNC, indicator_power_device_get_object_path(device), old_value, new_value);
+
+ if ((old_value > critical_level) && (new_value <= critical_level))
+ {
+ emit_critical_signal(self);
+ }
+ else if ((old_value > very_low_level) && (new_value <= very_low_level))
+ {
+ notification_show(self, device);
+ }
+ else if ((old_value > low_level) && (new_value <= low_level))
+ {
+ notification_show(self, device);
+ }
+}
+
+static void
+on_devices_changed(IndicatorPowerNotifier * self)
+{
+ priv_t * p = self->priv;
+ GList * devices;
+
+ // find the primary device
+ devices = indicator_power_device_provider_get_devices(p->device_provider);
+ g_clear_object(&p->primary_device);
+ p->primary_device = indicator_power_service_choose_primary_device (devices);
+ g_list_free_full (devices, (GDestroyNotify)g_object_unref);
+
+ if (p->primary_device != NULL)
+ {
+ // test for battery level change
+ const gdouble old_level = (int)(p->battery_level*1000) ? p->battery_level : 100.0;
+ const gdouble new_level = indicator_power_device_get_percentage(p->primary_device);
+ if ((int)(old_level*1000) != (int)(new_level*1000))
+ on_battery_level_changed (self, p->primary_device, old_level, new_level);
+
+ p->battery_level = new_level;
+ p->time_remaining = indicator_power_device_get_time (p->primary_device);
+ }
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorPowerNotifier * self = INDICATOR_POWER_NOTIFIER (o);
+ priv_t * p = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_DEVICE_PROVIDER:
+ g_value_set_object (value, p->device_provider);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+static void
+my_set_property (GObject * o,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorPowerNotifier * self = INDICATOR_POWER_NOTIFIER (o);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE_PROVIDER:
+ indicator_power_notifier_set_device_provider (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorPowerNotifier * self = INDICATOR_POWER_NOTIFIER(o);
+
+ indicator_power_notifier_set_device_provider(self, NULL);
+ notification_clear(self);
+
+ G_OBJECT_CLASS (indicator_power_notifier_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o G_GNUC_UNUSED)
+{
+ if (!--n_notifiers)
+ notify_uninit();
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_power_notifier_init (IndicatorPowerNotifier * self)
+{
+ priv_t * p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_POWER_NOTIFIER,
+ IndicatorPowerNotifierPrivate);
+ self->priv = p;
+
+ //p->battery_levels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
+
+ if (!n_notifiers++ && !notify_init("indicator-power-service"))
+ g_critical("Unable to initialize libnotify! Notifications might not be shown.");
+}
+
+static void
+indicator_power_notifier_class_init (IndicatorPowerNotifierClass * klass)
+{
+ GObjectClass * object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+ object_class->get_property = my_get_property;
+ object_class->set_property = my_set_property;
+
+ g_type_class_add_private (klass, sizeof (IndicatorPowerNotifierPrivate));
+
+ properties[PROP_0] = NULL;
+
+ properties[PROP_DEVICE_PROVIDER] = g_param_spec_object (
+ "device-provider",
+ "Device Provider",
+ "Source for power devices",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+/***
+**** Public API
+***/
+
+IndicatorPowerNotifier *
+indicator_power_notifier_new (IndicatorPowerDeviceProvider * device_provider)
+{
+ GObject * o = g_object_new (INDICATOR_TYPE_POWER_NOTIFIER,
+ "device-provider", device_provider,
+ NULL);
+
+ return INDICATOR_POWER_NOTIFIER (o);
+}
+
+void
+indicator_power_notifier_set_device_provider(IndicatorPowerNotifier * self,
+ IndicatorPowerDeviceProvider * dp)
+{
+ priv_t * p;
+
+ g_return_if_fail(INDICATOR_IS_POWER_NOTIFIER(self));
+ g_return_if_fail(!dp || INDICATOR_IS_POWER_DEVICE_PROVIDER(dp));
+ p = self->priv;
+
+ if (p->device_provider != NULL)
+ {
+ g_signal_handlers_disconnect_by_data(p->device_provider, self);
+ g_clear_object(&p->device_provider);
+ g_clear_object(&p->primary_device);
+ }
+
+ if (dp != NULL)
+ {
+ p->device_provider = g_object_ref(dp);
+
+ g_signal_connect_swapped(p->device_provider, "devices-changed",
+ G_CALLBACK(on_devices_changed), self);
+
+ on_devices_changed(self);
+ }
+}
diff --git a/src/notifier.h b/src/notifier.h
new file mode 100644
index 0000000..e8dfaab
--- /dev/null
+++ b/src/notifier.h
@@ -0,0 +1,68 @@
+/*
+ * 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>
+ */
+
+#ifndef __INDICATOR_POWER_NOTIFIER_H__
+#define __INDICATOR_POWER_NOTIFIER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "device-provider.h"
+
+G_BEGIN_DECLS
+
+/* standard GObject macros */
+#define INDICATOR_POWER_NOTIFIER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_POWER_NOTIFIER, IndicatorPowerNotifier))
+#define INDICATOR_TYPE_POWER_NOTIFIER (indicator_power_notifier_get_type())
+#define INDICATOR_IS_POWER_NOTIFIER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_POWER_NOTIFIER))
+
+typedef struct _IndicatorPowerNotifier IndicatorPowerNotifier;
+typedef struct _IndicatorPowerNotifierClass IndicatorPowerNotifierClass;
+typedef struct _IndicatorPowerNotifierPrivate IndicatorPowerNotifierPrivate;
+
+/**
+ * The Indicator Power Notifier.
+ */
+struct _IndicatorPowerNotifier
+{
+ /*< private >*/
+ GObject parent;
+ IndicatorPowerNotifierPrivate * priv;
+};
+
+struct _IndicatorPowerNotifierClass
+{
+ GObjectClass parent_class;
+};
+
+/***
+****
+***/
+
+GType indicator_power_notifier_get_type (void);
+
+IndicatorPowerNotifier * indicator_power_notifier_new (IndicatorPowerDeviceProvider * provider);
+
+void indicator_power_notifier_set_device_provider (IndicatorPowerNotifier * self,
+ IndicatorPowerDeviceProvider * provider);
+
+
+G_END_DECLS
+
+#endif /* __INDICATOR_POWER_NOTIFIER_H__ */
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index c5ad09d..4489bdc 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -42,5 +42,7 @@ function(add_test_by_name name)
add_dependencies (${TEST_NAME} libindicatorpowerservice)
target_link_libraries (${TEST_NAME} indicatorpowerservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
endfunction()
+add_test_by_name(test-notify)
+add_test(NAME dear-reader-the-next-test-takes-80-seconds COMMAND true)
add_test_by_name(test-device)
diff --git a/tests/test-notify.cc b/tests/test-notify.cc
new file mode 100644
index 0000000..0b75177
--- /dev/null
+++ b/tests/test-notify.cc
@@ -0,0 +1,59 @@
+/*
+ * 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 "device.h"
+#include "service.h"
+
+#include <gtest/gtest.h>
+
+#include <gio/gio.h>
+
+class NotifyTest : public ::testing::Test
+{
+ private:
+
+ typedef ::testing::Test super;
+
+ protected:
+
+ virtual void SetUp()
+ {
+ super::SetUp();
+ }
+
+ virtual void TearDown()
+ {
+ super::TearDown();
+ }
+};
+
+/***
+****
+***/
+
+// mock device provider
+
+// send notifications of a device going down from 50% to 3% by 1% increments
+
+// popup should appear exactly twice: once at 10%, once at 5%
+
+TEST_F(NotifyTest, HelloWorld)
+{
+}
+