aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2021-08-28 10:17:41 +0200
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2021-08-28 10:17:41 +0200
commit046a91ec739efdf1504e8716a28cec2fd5782303 (patch)
tree5e2c48a3dcd5992e4c190609484706a69d3393f8
parent92ac2554299affa7980dd754dfa5745ce1571bdd (diff)
parentd5a53027e58b6153b86b6c4b832cfd393bcdefb0 (diff)
downloadayatana-indicator-power-046a91ec739efdf1504e8716a28cec2fd5782303.tar.gz
ayatana-indicator-power-046a91ec739efdf1504e8716a28cec2fd5782303.tar.bz2
ayatana-indicator-power-046a91ec739efdf1504e8716a28cec2fd5782303.zip
Merge branch 'tari01-pr/ubports-patches'
Attributes GH PR #33: https://github.com/AyatanaIndicators/ayatana-indicator-power/pull/33
-rw-r--r--.build.yml5
-rw-r--r--CMakeLists.txt21
-rw-r--r--data/CMakeLists.txt9
-rw-r--r--data/sounds/Low battery.oggbin0 -> 71140 bytes
-rw-r--r--debian/control1
-rw-r--r--src/CMakeLists.txt22
-rw-r--r--src/brightness.c28
-rw-r--r--src/com.lomiri.Repowerd.xml (renamed from src/com.canonical.powerd.xml)2
-rw-r--r--src/datafiles.c71
-rw-r--r--src/datafiles.h37
-rw-r--r--src/flashlight.c89
-rw-r--r--src/flashlight.h9
-rw-r--r--src/main.c16
-rw-r--r--src/notifier.c119
-rw-r--r--src/notifier.h2
-rw-r--r--src/service.c71
-rw-r--r--src/service.h7
-rw-r--r--tests/CMakeLists.txt9
-rw-r--r--tests/glib-fixture.h201
-rw-r--r--tests/test-device.cc4
-rw-r--r--tests/test-notify.cc71
21 files changed, 659 insertions, 135 deletions
diff --git a/.build.yml b/.build.yml
index 90c2e9a..14f553b 100644
--- a/.build.yml
+++ b/.build.yml
@@ -17,6 +17,7 @@ requires:
- glib2
- libnotify
- systemd
+ - gstreamer
debian:
# Useful URL: https://salsa.debian.org/debian-ayatana-team/ayatana-indicator-power
@@ -38,6 +39,7 @@ requires:
- dbus-test-runner
- libdbustest1-dev
- systemd
+ - libgstreamer1.0-dev
debian:stable:
- autopoint
@@ -57,6 +59,7 @@ requires:
- dbus-test-runner
- libdbustest1-dev
- systemd
+ - libgstreamer1.0-dev
ubuntu:
- autopoint
@@ -76,6 +79,7 @@ requires:
- dbus-test-runner
- libdbustest1-dev
- systemd
+ - libgstreamer1.0-dev
ubuntu:focal:
- autopoint
@@ -94,6 +98,7 @@ requires:
- dbus-test-runner
- libdbustest1-dev
- systemd
+ - libgstreamer1.0-dev
variables:
- 'CHECKERS="
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9cc29fe..72f4bde 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,13 +40,21 @@ set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_
##
set(GETTEXT_PACKAGE "ayatana-indicator-power")
-add_definitions (-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}"
- -DLOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}")
+add_definitions(
+ -DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}"
+ -DG_LOG_DOMAIN="${GETTEXT_PACKAGE}"
+ -DLOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}"
+ -DLOW_BATTERY_SOUND="Low battery.ogg"
+)
##
## Check for prerequisites
##
+set(SERVICE_LIB "ayatanaindicatorpowerservice")
+set(SERVICE_EXEC "ayatana-indicator-power-service")
+add_definitions(-DSERVICE_EXEC="${SERVICE_EXEC}")
+
find_package (PkgConfig REQUIRED)
include (CheckIncludeFile)
include (FindPkgConfig)
@@ -67,6 +75,13 @@ pkg_check_modules(
)
include_directories(${URLDISPATCHER_INCLUDE_DIRS})
+if(EXISTS "/usr/share/accountsservice/interfaces/com.ubuntu.touch.AccountsService.Sound.xml")
+ set (HAVE_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS ON)
+ add_definitions ( -DHAS_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS )
+else()
+ set (HAVE_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS OFF)
+endif()
+
##
## custom targets
##
@@ -110,7 +125,7 @@ if (ENABLE_TESTS)
if (ENABLE_COVERAGE)
find_package(CoverageReport)
ENABLE_COVERAGE_REPORT(
- TARGETS ayatanaindicatorpowerservice ayatana-indicator-power-service
+ TARGETS ${SERVICE_LIB} ${SERVICE_EXEC}
TESTS ${COVERAGE_TEST_TARGETS}
FILTER /usr/include ${CMAKE_BINARY_DIR}/*
)
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
index e15a81e..2b8ab36 100644
--- a/data/CMakeLists.txt
+++ b/data/CMakeLists.txt
@@ -68,3 +68,12 @@ set (AYATANA_INDICATOR_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${AYATANA_INDICATOR_NAM
install (FILES "${AYATANA_INDICATOR_FILE}"
DESTINATION "${AYATANA_INDICATOR_DIR}")
+
+##
+## Sounds
+##
+
+# where to install
+set (DATA_HOME "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}")
+message (STATUS "${DATA_HOME} is the sounds/ install dir")
+install (DIRECTORY sounds DESTINATION ${DATA_HOME})
diff --git a/data/sounds/Low battery.ogg b/data/sounds/Low battery.ogg
new file mode 100644
index 0000000..f6f9fb6
--- /dev/null
+++ b/data/sounds/Low battery.ogg
Binary files differ
diff --git a/debian/control b/debian/control
index a70647f..534e197 100644
--- a/debian/control
+++ b/debian/control
@@ -10,6 +10,7 @@ Build-Depends: cmake,
libnotify-dev (>= 0.7.6),
libglib2.0-dev (>= 2.36),
liblomiri-url-dispatcher-dev | hello,
+ accountsservice-ubuntu-schemas | hello,
# for packaging
debhelper (>= 9),
dpkg-dev (>= 1.16.1.1),
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fb8ffef..d6e3c7b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,8 +1,3 @@
-set (SERVICE_LIB "ayatanaindicatorpowerservice")
-set (SERVICE_EXEC "ayatana-indicator-power-service")
-
-add_definitions(-DG_LOG_DOMAIN="ayatana-indicator-power")
-
if(URLDISPATCHER_FOUND)
add_definitions( -DHAS_URLDISPATCHER )
endif()
@@ -10,6 +5,7 @@ endif()
# handwritten sources
set(SERVICE_MANUAL_SOURCES
brightness.c
+ datafiles.c
device-provider-mock.c
device-provider-upower.c
device-provider.c
@@ -23,10 +19,10 @@ set(SERVICE_MANUAL_SOURCES
# generated sources
include(GdbusCodegen)
set(SERVICE_GENERATED_SOURCES)
-add_gdbus_codegen_with_namespace(SERVICE_GENERATED_SOURCES dbus-powerd
- com.canonical
+add_gdbus_codegen_with_namespace(SERVICE_GENERATED_SOURCES dbus-repowerd
+ com.lomiri
Dbus
- ${CMAKE_SOURCE_DIR}/src/com.canonical.powerd.xml)
+ ${CMAKE_SOURCE_DIR}/src/com.lomiri.Repowerd.xml)
add_gdbus_codegen_with_namespace(SERVICE_GENERATED_SOURCES dbus-battery
org.ayatana.indicator.power
Dbus
@@ -35,6 +31,14 @@ add_gdbus_codegen_with_namespace(SERVICE_GENERATED_SOURCES dbus-testing
org.ayatana.indicator.power
Dbus
${CMAKE_SOURCE_DIR}/data/org.ayatana.indicator.power.Testing.xml)
+
+if (HAVE_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS)
+ add_gdbus_codegen_with_namespace(SERVICE_GENERATED_SOURCES dbus-accounts-sound
+ com.ubuntu.touch
+ Dbus
+ /usr/share/accountsservice/interfaces/com.ubuntu.touch.AccountsService.Sound.xml)
+endif()
+
# add the bin dir to our include path so the code can find the generated header files
include_directories(${CMAKE_CURRENT_BINARY_DIR})
@@ -44,7 +48,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-bad-function-cast") # g_clear_object()
set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-switch-enum")
set_source_files_properties(${SERVICE_MANUAL_SOURCES}
- PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -g -std=c99")
+ PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -std=c99")
# the service library for tests to link against (basically, everything except main())
add_library(${SERVICE_LIB} STATIC ${SERVICE_MANUAL_SOURCES} ${SERVICE_GENERATED_SOURCES})
diff --git a/src/brightness.c b/src/brightness.c
index eb48515..4b00568 100644
--- a/src/brightness.c
+++ b/src/brightness.c
@@ -18,7 +18,7 @@
*/
#include "brightness.h"
-#include "dbus-powerd.h"
+#include "dbus-repowerd.h"
#include <gio/gio.h>
@@ -46,7 +46,7 @@ typedef struct
GSettings * settings;
- DbusPowerd * powerd_proxy;
+ DbusRepowerd * powerd_proxy;
char * powerd_name_owner;
double percentage;
@@ -198,7 +198,7 @@ percentage_to_brightness(IndicatorPowerBrightness * self, double percentage)
}
/**
- * DBus Chatter: com.canonical.powerd
+ * DBus Chatter: com.lomiri.Repowerd
*
* This is used to get default value, and upper and lower bounds,
* of the brightness setting
@@ -217,7 +217,7 @@ on_powerd_brightness_params_ready(GObject * oproxy,
v = NULL;
error = NULL;
- if (dbus_powerd_call_get_brightness_params_finish(DBUS_POWERD(oproxy), &v, res, &error))
+ if (dbus_repowerd_call_get_brightness_params_finish(DBUS_REPOWERD(oproxy), &v, res, &error))
{
IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(gself);
priv_t * p = get_priv(self);
@@ -235,12 +235,12 @@ on_powerd_brightness_params_ready(GObject * oproxy,
p->powerd_max,
p->powerd_default_value,
(int)p->powerd_ab_supported);
-
+
if (old_ab_supported != p->powerd_ab_supported)
g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AUTO_SUPPORTED]);
if (p->settings != NULL)
- {
+ {
if (g_settings_get_boolean(p->settings, KEY_NEED_DEFAULT))
{
/* user's first session, so init the schema's default
@@ -282,7 +282,7 @@ on_powerd_name_owner_changed(GDBusProxy * powerd_proxy,
if (owner != NULL)
{
- dbus_powerd_call_get_brightness_params(DBUS_POWERD(powerd_proxy),
+ dbus_repowerd_call_get_brightness_params(DBUS_REPOWERD(powerd_proxy),
p->cancellable,
on_powerd_brightness_params_ready,
gself);
@@ -294,11 +294,11 @@ on_powerd_name_owner_changed(GDBusProxy * powerd_proxy,
}
static void
-on_powerd_brightness_changed(DbusPowerd * powerd_proxy,
+on_powerd_brightness_changed(DbusRepowerd * powerd_proxy,
GParamSpec * pspec G_GNUC_UNUSED,
gpointer gself)
{
- set_brightness_local(gself, dbus_powerd_get_brightness(powerd_proxy));
+ set_brightness_local(gself, dbus_repowerd_get_brightness(powerd_proxy));
}
static void
@@ -307,10 +307,10 @@ on_powerd_proxy_ready(GObject * source_object G_GNUC_UNUSED,
gpointer gself)
{
GError * error;
- DbusPowerd * powerd_proxy;
+ DbusRepowerd * powerd_proxy;
error = NULL;
- powerd_proxy = dbus_powerd_proxy_new_for_bus_finish(res, &error);
+ powerd_proxy = dbus_repowerd_proxy_new_for_bus_finish(res, &error);
if (powerd_proxy != NULL)
{
@@ -461,10 +461,10 @@ indicator_power_brightness_init(IndicatorPowerBrightness * self)
g_settings_schema_unref(schema);
}
- dbus_powerd_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ dbus_repowerd_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
- "com.canonical.powerd",
- "/com/canonical/powerd",
+ "com.lomiri.Repowerd",
+ "/com/lomiri/Repowerd",
p->cancellable,
on_powerd_proxy_ready,
self);
diff --git a/src/com.canonical.powerd.xml b/src/com.lomiri.Repowerd.xml
index b5b4ac4..377eeab 100644
--- a/src/com.canonical.powerd.xml
+++ b/src/com.lomiri.Repowerd.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/">
- <interface name="com.canonical.powerd">
+ <interface name="com.lomiri.Repowerd">
<!-- Properties -->
<property name="brightness" type="i" access="readwrite">
diff --git a/src/datafiles.c b/src/datafiles.c
new file mode 100644
index 0000000..6d9962c
--- /dev/null
+++ b/src/datafiles.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 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 "datafiles.h"
+
+static const gchar*
+get_directory_prefix_for_type (DatafileType type)
+{
+ switch (type)
+ {
+ case DATAFILE_TYPE_SOUND:
+ return "sounds";
+
+ default:
+ g_critical("unknown type");
+ return "";
+ }
+}
+
+static gchar*
+test_directory_for_file(const char* dir, DatafileType type, const char* basename)
+{
+ gchar* filename = g_build_filename(dir,
+ GETTEXT_PACKAGE,
+ get_directory_prefix_for_type(type),
+ basename,
+ NULL);
+
+ g_debug("looking for \"%s\" at \"%s\"", basename, filename);
+ if (g_file_test(filename, G_FILE_TEST_EXISTS))
+ return filename;
+
+ g_free(filename);
+ return NULL;
+}
+
+gchar*
+datafile_find(DatafileType type, const char * basename)
+{
+ gchar * filename;
+ const gchar * user_data_dir;
+ const gchar * const * system_data_dirs;
+ gsize i;
+
+ user_data_dir = g_get_user_data_dir();
+ if ((filename = test_directory_for_file(user_data_dir, type, basename)))
+ return filename;
+
+ system_data_dirs = g_get_system_data_dirs();
+ for (i=0; system_data_dirs && system_data_dirs[i]; ++i)
+ if ((filename = test_directory_for_file(system_data_dirs[i], type, basename)))
+ return filename;
+
+ return NULL;
+}
diff --git a/src/datafiles.h b/src/datafiles.h
new file mode 100644
index 0000000..95c5bc9
--- /dev/null
+++ b/src/datafiles.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 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_DATAFILES_H__
+#define __INDICATOR_POWER_DATAFILES_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ DATAFILE_TYPE_SOUND
+}
+DatafileType;
+
+gchar* datafile_find(DatafileType type, const char * basename);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_POWER_DATAFILES_H__ */
diff --git a/src/flashlight.c b/src/flashlight.c
index 55a108a..064bd31 100644
--- a/src/flashlight.c
+++ b/src/flashlight.c
@@ -26,17 +26,29 @@
#define QCOM_ENABLE "255"
#define QCOM_DISABLE "0"
+#define SIMPLE_ENABLE "1"
+#define SIMPLE_DISABLE "0"
-const size_t qcom_sysfs_size = 5;
+const size_t qcom_sysfs_size = 7;
const char* const qcom_sysfs[] = {"/sys/class/leds/torch-light/brightness",
"/sys/class/leds/led:flash_torch/brightness",
"/sys/class/leds/flashlight/brightness",
"/sys/class/leds/torch-light0/brightness",
- "/sys/class/leds/torch-light1/brightness"};
+ "/sys/class/leds/torch-light1/brightness",
+ "/sys/class/leds/led:torch_0/brightness",
+ "/sys/class/leds/led:torch_1/brightness"};
+const size_t qcom_switch_size = 2;
+const char* const qcom_switch[] = {"/sys/class/leds/led:switch/brightness",
+ "/sys/class/leds/led:switch_0/brightness"};
-const char* qcom_torch_enable = "/sys/class/leds/led:switch/brightness";
+const size_t simple_sysfs_size = 2;
+const char* const simple_sysfs[] = {"/sys/class/flashlight_core/flashlight/flashlight_torch",
+ "/sys/class/leds/white:flash/brightness"};
char* flash_sysfs_path = NULL;
+char* qcom_switch_path = NULL;
+
+enum TorchType torch_type = SIMPLE;
gboolean activated = 0;
int
@@ -45,6 +57,18 @@ set_sysfs_path()
for (size_t i = 0; i < qcom_sysfs_size; i++) {
if (access(qcom_sysfs[i], F_OK ) != -1){
flash_sysfs_path = (char*)qcom_sysfs[i];
+ torch_type = QCOM;
+ /* Qualcomm torch; determine switch file (if one is needed) */
+ for (size_t i = 0; i < qcom_switch_size; i++) {
+ if (access(qcom_switch[i], F_OK ) != -1)
+ qcom_switch_path = (char*)qcom_switch[i];
+ }
+ return 1;
+ }
+ }
+ for (size_t i = 0; i < simple_sysfs_size; i++) {
+ if (access(simple_sysfs[i], F_OK ) != -1){
+ flash_sysfs_path = (char*)simple_sysfs[i];
return 1;
}
}
@@ -57,27 +81,17 @@ flashlight_activated()
return activated;
}
-void
-toggle_flashlight_action(GAction *action,
- GVariant *parameter G_GNUC_UNUSED,
- gpointer data G_GNUC_UNUSED)
+int
+toggle_flashlight_action_qcom()
{
- GVariant *state;
FILE *fd1 = NULL, *fd2 = NULL;
-
int needs_enable;
- if (!set_sysfs_path())
- return;
-
- state = g_action_get_state(action);
- activated = g_variant_get_boolean(state);
- g_variant_unref(state);
fd1 = fopen(flash_sysfs_path, "w");
if (fd1 != NULL) {
- needs_enable = access(qcom_torch_enable, F_OK ) != -1;
+ needs_enable = access(qcom_switch_path, F_OK ) != -1;
if (needs_enable)
- fd2 = fopen(qcom_torch_enable, "w");
+ fd2 = fopen(qcom_switch_path, "w");
if (activated)
if (needs_enable && fd2 != NULL)
fprintf(fd2, "0");
@@ -91,8 +105,47 @@ toggle_flashlight_action(GAction *action,
fclose(fd1);
if (fd2 !=NULL)
fclose(fd2);
- g_action_change_state(action, g_variant_new_boolean(!activated));
+ return 1;
+ }
+ return 0;
+}
+
+int
+toggle_flashlight_action_simple()
+{
+ FILE *fd = NULL;
+
+ fd = fopen(flash_sysfs_path, "w");
+ if (fd != NULL) {
+ fprintf(fd, activated ? SIMPLE_DISABLE : SIMPLE_ENABLE);
+ fclose(fd);
+ return 1;
}
+ return 0;
+}
+
+void
+toggle_flashlight_action(GAction *action,
+ GVariant *parameter G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED)
+{
+ GVariant *state;
+ int toggled;
+
+ if (!set_sysfs_path())
+ return;
+
+ state = g_action_get_state(action);
+ activated = g_variant_get_boolean(state);
+ g_variant_unref(state);
+
+ if (torch_type == QCOM)
+ toggled = toggle_flashlight_action_qcom();
+ else
+ toggled = toggle_flashlight_action_simple();
+
+ if (toggled)
+ g_action_change_state(action, g_variant_new_boolean(!activated));
}
int
diff --git a/src/flashlight.h b/src/flashlight.h
index 51ed5e7..19f738e 100644
--- a/src/flashlight.h
+++ b/src/flashlight.h
@@ -24,6 +24,12 @@
G_BEGIN_DECLS
+int
+toggle_flashlight_action_qcom();
+
+int
+toggle_flashlight_action_simple();
+
void
toggle_flashlight_action(GAction *action,
GVariant *parameter G_GNUC_UNUSED,
@@ -35,6 +41,9 @@ flashlight_supported();
gboolean
flashlight_activated();
+enum
+TorchType { SIMPLE = 1, QCOM };
+
G_END_DECLS
#endif /* INDICATOR_POWER_FLASHLIGHT__H */
diff --git a/src/main.c b/src/main.c
index 03e100d..ce54ae8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,8 +1,5 @@
/*
- * Copyright 2013 Canonical Ltd.
- *
- * Authors:
- * Charles Kerr <charles.kerr@canonical.com>
+ * Copyright 2013-2016 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
@@ -15,6 +12,9 @@
*
* 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 <locale.h>
@@ -23,6 +23,7 @@
#include <glib/gi18n.h>
#include "device.h"
+#include "notifier.h"
#include "service.h"
#include "testing.h"
@@ -40,6 +41,7 @@ on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop)
int
main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
{
+ IndicatorPowerNotifier * notifier;
IndicatorPowerService * service;
IndicatorPowerTesting * testing;
GMainLoop * loop;
@@ -50,7 +52,8 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
textdomain (GETTEXT_PACKAGE);
/* run */
- service = indicator_power_service_new (NULL);
+ notifier = indicator_power_notifier_new();
+ service = indicator_power_service_new(NULL, notifier);
testing = indicator_power_testing_new (service);
loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (service, INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST,
@@ -59,7 +62,8 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
/* cleanup */
g_main_loop_unref (loop);
- g_clear_object (&service);
g_clear_object (&testing);
+ g_clear_object (&service);
+ g_clear_object (&notifier);
return 0;
}
diff --git a/src/notifier.c b/src/notifier.c
index 123ee6f..d982ac2 100644
--- a/src/notifier.c
+++ b/src/notifier.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Canonical Ltd.
+ * Copyright 2014-2016 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
@@ -17,11 +17,21 @@
* Charles Kerr <charles.kerr@canonical.com>
*/
+#include "datafiles.h"
+
+#ifdef HAS_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS
+#include "dbus-accounts-sound.h"
+#endif
+
#include "dbus-battery.h"
#include "dbus-shared.h"
#include "notifier.h"
#include "utils.h"
+#ifdef HAS_URLDISPATCHER
+#include <lomiri-url-dispatcher.h>
+#endif
+
#include <libnotify/notify.h>
#include <glib/gi18n.h>
@@ -76,6 +86,12 @@ typedef struct
gboolean caps_queried;
gboolean actions_supported;
+
+ GCancellable * cancellable;
+ #ifdef HAS_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS
+ DbusAccountsServiceSound * accounts_service_sound_proxy;
+ gboolean accounts_service_sound_proxy_pending;
+ #endif
}
IndicatorPowerNotifierPrivate;
@@ -130,6 +146,58 @@ get_battery_power_level (IndicatorPowerDevice * battery)
}
/***
+**** Sounds
+***/
+
+#ifdef HAS_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS
+static void
+on_sound_proxy_ready (GObject * source_object G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * error;
+ error = NULL;
+
+ DbusAccountsServiceSound * proxy;
+ proxy = dbus_accounts_service_sound_proxy_new_for_bus_finish (res, &error);
+
+ if (error != NULL)
+ {
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ get_priv(gself)->accounts_service_sound_proxy_pending = FALSE;
+ g_debug("%s Couldn't find accounts service sound proxy: %s", G_STRLOC, error->message);
+ }
+
+ g_clear_error(&error);
+ }
+ else
+ {
+ IndicatorPowerNotifier * const self = INDICATOR_POWER_NOTIFIER(gself);
+ priv_t * const p = get_priv (self);
+ g_clear_object (&p->accounts_service_sound_proxy);
+ p->accounts_service_sound_proxy = proxy;
+ p->accounts_service_sound_proxy_pending = FALSE;
+ }
+}
+
+
+static gboolean
+silent_mode (IndicatorPowerNotifier * self)
+{
+ priv_t * const p = get_priv (self);
+
+ /* if we don't have a proxy yet, assume we're in silent mode
+ as a "do no harm" level of response */
+ if (p->accounts_service_sound_proxy_pending)
+ return TRUE;
+
+ return (p->accounts_service_sound_proxy != NULL)
+ && dbus_accounts_service_sound_get_silent_mode(p->accounts_service_sound_proxy);
+}
+#endif
+
+/***
**** Notifications
***/
@@ -243,6 +311,23 @@ notification_show(IndicatorPowerNotifier * self)
if (are_actions_supported(self))
{
+ #ifdef HAS_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS
+ if (!silent_mode(self))
+ #endif
+ {
+ gchar* filename = datafile_find(DATAFILE_TYPE_SOUND, LOW_BATTERY_SOUND);
+ if (filename != NULL)
+ {
+ gchar * uri = g_filename_to_uri(filename, NULL, NULL);
+ notify_notification_set_hint(nn, "sound-file", g_variant_new_take_string(uri));
+ g_clear_pointer(&filename, g_free);
+ }
+ else
+ {
+ g_warning("Unable to find '%s' in XDG data dirs", LOW_BATTERY_SOUND);
+ }
+ }
+
notify_notification_set_hint(nn, "x-canonical-snap-decisions", g_variant_new_string("true"));
notify_notification_set_hint(nn, "x-canonical-non-shaped-icon", g_variant_new_string("true"));
notify_notification_set_hint(nn, "x-canonical-private-affirmative-tint", g_variant_new_string("true"));
@@ -359,18 +444,28 @@ my_dispose (GObject * o)
IndicatorPowerNotifier * const self = INDICATOR_POWER_NOTIFIER(o);
priv_t * const p = get_priv (self);
+ if (p->cancellable != NULL)
+ {
+ g_cancellable_cancel(p->cancellable);
+ g_clear_object(&p->cancellable);
+ }
+
indicator_power_notifier_set_bus (self, NULL);
notification_clear (self);
indicator_power_notifier_set_battery (self, NULL);
g_clear_object (&p->dbus_battery);
+ #ifdef HAS_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS
+ g_clear_object (&p->accounts_service_sound_proxy);
+ #endif
+
G_OBJECT_CLASS (indicator_power_notifier_parent_class)->dispose (o);
}
static void
my_finalize (GObject * o G_GNUC_UNUSED)
{
- /* FIXME: This is an awkward place to put this.
+ /* FIXME: This is an awkward place to put this.
Ordinarily something like this would go in main(), but we need libnotify
to clean itself up before shutting down the bus in the unit tests as well. */
if (!--instance_count)
@@ -381,7 +476,6 @@ my_finalize (GObject * o G_GNUC_UNUSED)
**** Instantiation
***/
-
static void
indicator_power_notifier_init (IndicatorPowerNotifier * self)
{
@@ -393,8 +487,24 @@ indicator_power_notifier_init (IndicatorPowerNotifier * self)
p->power_level = POWER_LEVEL_OK;
- if (!instance_count++ && !notify_init("ayatana-indicator-power-service"))
+ p->cancellable = g_cancellable_new();
+
+ if (!instance_count++ && !notify_init(SERVICE_EXEC))
g_critical("Unable to initialize libnotify! Notifications might not be shown.");
+
+ #ifdef HAS_UT_ACCTSERVICE_SYSTEMSOUND_SETTINGS
+ p->accounts_service_sound_proxy_pending = TRUE;
+ gchar* object_path = g_strdup_printf("/org/freedesktop/Accounts/User%lu", (gulong)getuid());
+ dbus_accounts_service_sound_proxy_new_for_bus(
+ G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+ "org.freedesktop.Accounts",
+ object_path,
+ p->cancellable,
+ on_sound_proxy_ready,
+ self);
+ g_clear_pointer(&object_path, g_free);
+ #endif
}
static void
@@ -425,7 +535,6 @@ IndicatorPowerNotifier *
indicator_power_notifier_new (void)
{
GObject * o = g_object_new (INDICATOR_TYPE_POWER_NOTIFIER, NULL);
-
return INDICATOR_POWER_NOTIFIER (o);
}
diff --git a/src/notifier.h b/src/notifier.h
index 18e25d7..1b04943 100644
--- a/src/notifier.h
+++ b/src/notifier.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Canonical Ltd.
+ * Copyright 2014-2016 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
diff --git a/src/service.c b/src/service.c
index c93c4b6..9b99a05 100644
--- a/src/service.c
+++ b/src/service.c
@@ -1,12 +1,7 @@
/*
- * Copyright 2013 Canonical Ltd.
+ * Copyright 2013-2016 Canonical Ltd.
* Copytight 2021 AyatanaIndicators
*
- * Authors:
- * Charles Kerr <charles.kerr@canonical.com>
- * Ted Gould <ted@canonical.com>
- * Robert Tari <robert@tari.in>
- *
* 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.
@@ -18,6 +13,11 @@
*
* 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>
+ * Ted Gould <ted@canonical.com>
+ * Robert Tari <robert@tari.in>
*/
#include <glib/gi18n.h>
@@ -52,6 +52,7 @@ enum
PROP_0,
PROP_BUS,
PROP_DEVICE_PROVIDER,
+ PROP_NOTIFIER,
LAST_PROP
};
@@ -199,6 +200,14 @@ device_compare_func (gconstpointer ga, gconstpointer gb)
}
state = UP_DEVICE_STATE_DISCHARGING;
+
+ /* discharging items with more than 10% remaining always lose */
+ if (!ret && (((a_state == state) && !a_time && (a_percentage > 10))))
+ ret = 1;
+
+ if (!ret && (((b_state == state) && !b_time && (b_percentage > 10))))
+ ret = -1;
+
if (!ret && (((a_state == state) && a_time) ||
((b_state == state) && b_time)))
{
@@ -591,7 +600,7 @@ create_brightness_menu_item(void)
GMenuItem * item;
item = g_menu_item_new(NULL, "indicator.brightness");
- g_menu_item_set_attribute(item, "x-ayatana-type", "s", "org.ayatana.unity.slider");
+ g_menu_item_set_attribute(item, "x-ayatana-type", "s", "org.ayatana.indicator.slider");
g_menu_item_set_attribute(item, "min-value", "d", 0.0);
g_menu_item_set_attribute(item, "max-value", "d", 1.0);
@@ -968,7 +977,8 @@ on_bus_acquired (GDBusConnection * connection,
g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_BUS]);
/* export the battery properties */
- indicator_power_notifier_set_bus (p->notifier, connection);
+ if (p->notifier != NULL)
+ indicator_power_notifier_set_bus (p->notifier, connection);
/* export the actions */
if ((id = g_dbus_connection_export_action_group (connection,
@@ -1110,6 +1120,10 @@ my_get_property (GObject * o,
g_value_set_object (value, p->device_provider);
break;
+ case PROP_NOTIFIER:
+ g_value_set_object (value, p->notifier);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
}
@@ -1129,6 +1143,10 @@ my_set_property (GObject * o,
indicator_power_service_set_device_provider (self, g_value_get_object (value));
break;
+ case PROP_NOTIFIER:
+ indicator_power_service_set_notifier (self, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
}
@@ -1171,6 +1189,7 @@ my_dispose (GObject * o)
g_clear_object (&p->conn);
indicator_power_service_set_device_provider (self, NULL);
+ indicator_power_service_set_notifier (self, NULL);
G_OBJECT_CLASS (indicator_power_service_parent_class)->dispose (o);
}
@@ -1192,8 +1211,6 @@ indicator_power_service_init (IndicatorPowerService * self)
p->settings = g_settings_new ("org.ayatana.indicator.power");
- p->notifier = indicator_power_notifier_new ();
-
p->brightness = indicator_power_brightness_new();
g_signal_connect_swapped(p->brightness, "notify::percentage",
G_CALLBACK(update_brightness_action_state), self);
@@ -1253,6 +1270,13 @@ indicator_power_service_class_init (IndicatorPowerServiceClass * klass)
G_TYPE_OBJECT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_NOTIFIER] = g_param_spec_object (
+ "notifier",
+ "Notifier",
+ "Notifies user of important battery changes",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties (object_class, LAST_PROP, properties);
}
@@ -1261,10 +1285,12 @@ indicator_power_service_class_init (IndicatorPowerServiceClass * klass)
***/
IndicatorPowerService *
-indicator_power_service_new (IndicatorPowerDeviceProvider * device_provider)
+indicator_power_service_new (IndicatorPowerDeviceProvider * device_provider,
+ IndicatorPowerNotifier * notifier)
{
GObject * o = g_object_new (INDICATOR_TYPE_POWER_SERVICE,
"device-provider", device_provider,
+ "notifier", notifier,
NULL);
return INDICATOR_POWER_SERVICE (o);
@@ -1303,6 +1329,29 @@ indicator_power_service_set_device_provider (IndicatorPowerService * self,
}
}
+void
+indicator_power_service_set_notifier (IndicatorPowerService * self,
+ IndicatorPowerNotifier * notifier)
+{
+ priv_t * p;
+
+ g_return_if_fail (INDICATOR_IS_POWER_SERVICE (self));
+ g_return_if_fail (!notifier || INDICATOR_IS_POWER_NOTIFIER (notifier));
+ p = self->priv;
+
+ if (p->notifier == notifier)
+ return;
+
+ g_clear_object (&p->notifier);
+
+ if (notifier != NULL)
+ {
+ p->notifier = g_object_ref (notifier);
+ indicator_power_notifier_set_bus (p->notifier, p->conn);
+ }
+}
+
+
/* If a device has multiple batteries and uses only one of them at a time,
they should be presented as separate items inside the battery menu,
but everywhere else they should be aggregated (bug 880881).
diff --git a/src/service.h b/src/service.h
index 76ed10f..00ed3e6 100644
--- a/src/service.h
+++ b/src/service.h
@@ -24,6 +24,7 @@
#include <glib-object.h>
#include "device-provider.h"
+#include "notifier.h"
G_BEGIN_DECLS
@@ -64,11 +65,15 @@ struct _IndicatorPowerServiceClass
GType indicator_power_service_get_type (void);
-IndicatorPowerService * indicator_power_service_new (IndicatorPowerDeviceProvider * provider);
+IndicatorPowerService * indicator_power_service_new (IndicatorPowerDeviceProvider * provider,
+ IndicatorPowerNotifier * notifier);
void indicator_power_service_set_device_provider (IndicatorPowerService * self,
IndicatorPowerDeviceProvider * provider);
+void indicator_power_service_set_notifier (IndicatorPowerService * self,
+ IndicatorPowerNotifier * notifier);
+
IndicatorPowerDevice * indicator_power_service_choose_primary_device (GList * devices);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 1dc8a8f..9c0d222 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -14,6 +14,11 @@ set_directory_properties (PROPERTIES
ADDITIONAL_MAKE_CLEAN_FILES gschemas.compiled)
set_source_files_properties (gschemas.compiled GENERATED)
+# make a XDG_DATA_HOME for sounds/
+set(XDG_DATA_HOME "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}")
+add_definitions(-DXDG_DATA_HOME="${XDG_DATA_HOME}")
+file(COPY "${CMAKE_SOURCE_DIR}/data/sounds" DESTINATION "${XDG_DATA_HOME}/${CMAKE_PROJECT_NAME}")
+
# GSettings:
# compile the ayatana-indicator-power schema into a gschemas.compiled file in this directory,
# and help the tests to find that file by setting -DSCHEMA_DIR
@@ -45,8 +50,8 @@ function(add_test_by_name name)
add_executable (${TEST_NAME} ${TEST_NAME}.cc)
target_link_options(${TEST_NAME} PRIVATE -no-pie)
add_test (${TEST_NAME} ${TEST_NAME})
- add_dependencies (${TEST_NAME} ayatanaindicatorpowerservice gschemas-compiled)
- target_link_libraries (${TEST_NAME} ayatanaindicatorpowerservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS} ${URLDISPATCHER_LIBRARIES} ${GMOCK_LIBRARIES})
+ add_dependencies (${TEST_NAME} ${SERVICE_LIB} gschemas-compiled)
+ target_link_libraries (${TEST_NAME} ${SERVICE_LIB} ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${URLDISPATCHER_LIBRARIES} ${GMOCK_LIBRARIES})
endfunction()
add_test_by_name(test-notify)
add_test(NAME dear-reader-the-next-test-takes-80-seconds COMMAND true)
diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h
index 09c37a3..2350d49 100644
--- a/tests/glib-fixture.h
+++ b/tests/glib-fixture.h
@@ -1,5 +1,8 @@
/*
- * Copyright 2014 Canonical Ltd.
+ * Copyright 2013-2016 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
@@ -12,12 +15,14 @@
*
* 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>
*/
+#pragma once
+
+#include <chrono>
+#include <functional> // std::function
#include <map>
+#include <memory> // std::shared_ptr
#include <glib.h>
#include <glib/gstdio.h>
@@ -29,79 +34,48 @@
class GlibFixture : public ::testing::Test
{
- private:
+ public:
- GLogFunc realLogHandler;
-
- std::map<GLogLevelFlags,size_t> expected_log;
- std::map<GLogLevelFlags,std::vector<std::string>> log;
-
- void test_log_counts()
- {
- const GLogLevelFlags levels_to_test[] = { G_LOG_LEVEL_ERROR,
- G_LOG_LEVEL_CRITICAL,
- G_LOG_LEVEL_MESSAGE,
- G_LOG_LEVEL_WARNING };
-
- for(const auto& level : levels_to_test)
- {
- const auto& v = log[level];
- const auto n = v.size();
-
- EXPECT_EQ(expected_log[level], n);
-
- if (expected_log[level] != n)
- for (size_t i=0; i<n; ++i)
- g_print("%lu %s\n", (n+1), v[i].c_str());
- }
-
- expected_log.clear();
- log.clear();
- }
-
- static void default_log_handler(const gchar * log_domain,
- GLogLevelFlags log_level,
- const gchar * message,
- gpointer self)
- {
- auto tmp = g_strdup_printf ("%s:%d \"%s\"", log_domain, int(log_level), message);
- static_cast<GlibFixture*>(self)->log[log_level].push_back(tmp);
- g_free(tmp);
- }
+ virtual ~GlibFixture() =default;
protected:
- void increment_expected_errors(GLogLevelFlags level, size_t n=1)
- {
- expected_log[level] += n;
- }
-
- virtual void SetUp()
+ virtual void SetUp() override
{
setlocale(LC_ALL, "C.UTF-8");
loop = g_main_loop_new(nullptr, false);
- g_log_set_default_handler(default_log_handler, this);
+ // only use local, temporary settings
+ g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true));
+ g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true));
+ g_debug("SCHEMA_DIR is %s", SCHEMA_DIR);
+
+ // fail on unexpected messages from this domain
+ g_log_set_fatal_mask(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING);
g_unsetenv("DISPLAY");
+
}
- virtual void TearDown()
+ virtual void TearDown() override
{
- test_log_counts();
-
- g_log_set_default_handler(realLogHandler, this);
+ g_test_assert_expected_messages ();
g_clear_pointer(&loop, g_main_loop_unref);
}
+ void expectLogMessage (const gchar *domain, GLogLevelFlags level, const gchar *pattern)
+ {
+ g_test_expect_message (domain, level, pattern);
+ }
+
private:
static gboolean
wait_for_signal__timeout(gpointer name)
{
- g_error("%s: timed out waiting for signal '%s'", G_STRLOC, static_cast<char*>(name));
+ g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name);
return G_SOURCE_REMOVE;
}
@@ -115,7 +89,7 @@ class GlibFixture : public ::testing::Test
protected:
/* convenience func to loop while waiting for a GObject's signal */
- void wait_for_signal(gpointer o, const gchar * signal, const guint timeout_seconds=5)
+ void wait_for_signal(gpointer o, const gchar * signal, const int timeout_seconds=5)
{
// wait for the signal or for timeout, whichever comes first
const auto handler_id = g_signal_connect_swapped(o, signal,
@@ -130,12 +104,125 @@ class GlibFixture : public ::testing::Test
}
/* convenience func to loop for N msec */
- void wait_msec(guint msec=50)
+ void wait_msec(int msec=50)
{
const auto id = g_timeout_add(msec, wait_msec__timeout, loop);
g_main_loop_run(loop);
g_source_remove(id);
}
- GMainLoop * loop;
+ bool wait_for(std::function<bool()> test_function, guint timeout_msec=1000)
+ {
+ auto timer = std::shared_ptr<GTimer>(g_timer_new(), [](GTimer* t){g_timer_destroy(t);});
+ const auto timeout_sec = timeout_msec / 1000.0;
+ for (;;) {
+ if (test_function())
+ return true;
+ //g_message("%f ... %f", g_timer_elapsed(timer.get(), nullptr), timeout_sec);
+ if (g_timer_elapsed(timer.get(), nullptr) >= timeout_sec)
+ return false;
+ wait_msec();
+ }
+ }
+
+ bool wait_for_name_owned(
+ GDBusConnection* connection,
+ const gchar* name,
+ guint timeout_msec=1000,
+ GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
+ {
+ struct Data {
+ GMainLoop* loop = nullptr;
+ bool owned = false;
+ };
+ Data data;
+
+ auto on_name_appeared = [](GDBusConnection* /*connection*/,
+ const gchar* /*name_*/,
+ const gchar* name_owner,
+ gpointer gdata){
+ if (name_owner == nullptr)
+ return;
+ auto tmp = static_cast<Data*>(gdata);
+ tmp->owned = true;
+ g_main_loop_quit(tmp->loop);
+ };
+
+ const auto timeout_id = g_timeout_add(timeout_msec, wait_msec__timeout, loop);
+ data.loop = loop;
+ const auto watch_id = g_bus_watch_name_on_connection(
+ connection,
+ name,
+ flags,
+ on_name_appeared,
+ nullptr, // name_vanished
+ &data,
+ nullptr // user_data_free_func
+ );
+
+ g_main_loop_run(loop);
+
+ g_bus_unwatch_name(watch_id);
+ g_source_remove(timeout_id);
+
+ return data.owned;
+ }
+
+ void EXPECT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection,
+ const gchar* name,
+ guint timeout_msec=1000,
+ GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
+ {
+ EXPECT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name;
+ }
+
+ void EXPECT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection,
+ const gchar* name,
+ guint timeout_msec=1000,
+ GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
+ {
+ EXPECT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name;
+ }
+
+ void ASSERT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection,
+ const gchar* name,
+ guint timeout_msec=1000,
+ GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
+ {
+ ASSERT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name;
+ }
+
+ void ASSERT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection,
+ const gchar* name,
+ guint timeout_msec=1000,
+ GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
+ {
+ ASSERT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name;
+ }
+
+ using source_func = std::function<gboolean()>;
+
+ guint idle_add(source_func&& func)
+ {
+ return g_idle_add_full(
+ G_PRIORITY_DEFAULT_IDLE,
+ [](gpointer gf){return (*static_cast<source_func*>(gf))();},
+ new std::function<gboolean()>(func),
+ [](gpointer gf){delete static_cast<source_func*>(gf);}
+ );
+ }
+
+ guint timeout_add(source_func&& func, std::chrono::milliseconds msec)
+ {
+ return g_timeout_add_full(
+ G_PRIORITY_DEFAULT,
+ msec.count(),
+ [](gpointer gf){return (*static_cast<source_func*>(gf))();},
+ new std::function<gboolean()>(func),
+ [](gpointer gf){delete static_cast<source_func*>(gf);}
+ );
+ }
+
+ GMainLoop* loop {};
};
+
diff --git a/tests/test-device.cc b/tests/test-device.cc
index bdc29d1..a948857 100644
--- a/tests/test-device.cc
+++ b/tests/test-device.cc
@@ -53,7 +53,7 @@ class DeviceTest : public ::testing::Test
virtual void SetUp()
{
const GLogLevelFlags flags = GLogLevelFlags(G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING);
- log_handler_id = g_log_set_handler ("ayatana-indicator-power", flags, log_count_func, this);
+ log_handler_id = g_log_set_handler(G_LOG_DOMAIN, flags, log_count_func, this);
log_count_ipower_expected = 0;
log_count_ipower_actual = 0;
}
@@ -61,7 +61,7 @@ class DeviceTest : public ::testing::Test
virtual void TearDown()
{
ASSERT_EQ (log_count_ipower_expected, log_count_ipower_actual);
- g_log_remove_handler ("ayatana-indicator-power", log_handler_id);
+ g_log_remove_handler (G_LOG_DOMAIN, log_handler_id);
}
protected:
diff --git a/tests/test-notify.cc b/tests/test-notify.cc
index 63214c2..8d7f0d8 100644
--- a/tests/test-notify.cc
+++ b/tests/test-notify.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Canonical Ltd.
+ * Copyright 2014-2016 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
@@ -61,8 +61,6 @@ protected:
static constexpr int NOTIFICATION_CLOSED_API {3};
static constexpr int NOTIFICATION_CLOSED_UNDEFINED {4};
- static constexpr char const * APP_NAME {"ayatana-indicator-power-service"};
-
static constexpr char const * METHOD_CLOSE {"CloseNotification"};
static constexpr char const * METHOD_NOTIFY {"Notify"};
static constexpr char const * METHOD_GET_CAPS {"GetCapabilities"};
@@ -77,6 +75,8 @@ protected:
{
super::SetUp();
+ g_setenv ("XDG_DATA_HOME", XDG_DATA_HOME, TRUE);
+
// init DBusMock / dbus-test-runner
service = dbus_test_service_new(nullptr);
@@ -133,7 +133,7 @@ protected:
g_dbus_connection_set_exit_on_close(bus, FALSE);
g_object_add_weak_pointer(G_OBJECT(bus), reinterpret_cast<gpointer*>(&bus));
- notify_init(APP_NAME);
+ notify_init(SERVICE_EXEC);
}
virtual void TearDown()
@@ -157,6 +157,50 @@ protected:
super::TearDown();
}
+
+ /***
+ ****
+ ***/
+
+ int get_notify_call_count() const
+ {
+ guint len {0u};
+ GError* error {nullptr};
+ dbus_test_dbus_mock_object_get_method_calls(mock, obj, METHOD_NOTIFY, &len, &error);
+ g_assert_no_error(error);
+ return len;
+ }
+
+ std::string get_notify_call_sound_file(int call_number)
+ {
+ std::string ret;
+
+ guint len {0u};
+ GError* error {nullptr};
+ auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, METHOD_NOTIFY, &len, &error);
+ g_return_val_if_fail(int(len) > call_number, ret);
+ g_assert_no_error(error);
+
+ constexpr int HINTS_PARAM_POSITION {6};
+ const auto& call = calls[call_number];
+ g_return_val_if_fail(g_variant_n_children(call.params) > HINTS_PARAM_POSITION, ret);
+ auto hints = g_variant_get_child_value(call.params, HINTS_PARAM_POSITION);
+ const gchar* sound_file = nullptr;
+ auto success = g_variant_lookup(hints, "sound-file", "&s", &sound_file);
+ g_return_val_if_fail(success, ret);
+ if (sound_file != nullptr)
+ ret = sound_file;
+ g_clear_pointer(&hints, g_variant_unref);
+
+ return ret;
+ }
+
+ void clear_method_calls()
+ {
+ GError* error{nullptr};
+ ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, &error));
+ g_assert_no_error(error);
+ }
};
/***
@@ -317,8 +361,8 @@ TEST_F(NotifyFixture, LevelsDuringBatteryDrain)
// cleanup
g_dbus_connection_signal_unsubscribe (bus, sub_tag);
- g_object_unref (notifier);
g_object_unref (battery);
+ g_object_unref (notifier);
}
/***
@@ -346,6 +390,12 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications)
30,
TRUE);
+ // the file we expect to play on a low battery notification...
+ const char* expected_file = XDG_DATA_HOME "/" GETTEXT_PACKAGE "/sounds/" LOW_BATTERY_SOUND;
+ char* tmp = g_filename_to_uri(expected_file, nullptr, nullptr);
+ const std::string low_power_uri {tmp};
+ g_clear_pointer(&tmp, g_free);
+
// set up a notifier and give it the battery so changing the battery's
// charge should show up on the bus.
auto notifier = indicator_power_notifier_new ();
@@ -376,6 +426,9 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications)
EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields);
EXPECT_EQ (indicator_power_notifier_get_power_level(battery), changed_params.power_level);
EXPECT_TRUE (changed_params.is_warning);
+ EXPECT_EQ (1, get_notify_call_count());
+ EXPECT_EQ (low_power_uri, get_notify_call_sound_file(0));
+ clear_method_calls();
// now test that the warning changes if the level goes down even lower...
changed_params = ChangedParams();
@@ -383,6 +436,9 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications)
wait_msec();
EXPECT_EQ (FIELD_POWER_LEVEL, changed_params.fields);
EXPECT_STREQ (POWER_LEVEL_STR_VERY_LOW, changed_params.power_level.c_str());
+ EXPECT_EQ (1, get_notify_call_count());
+ EXPECT_EQ (low_power_uri, get_notify_call_sound_file(0));
+ clear_method_calls();
// ...and that the warning is taken down if the battery is plugged back in...
changed_params = ChangedParams();
@@ -390,6 +446,7 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications)
wait_msec();
EXPECT_EQ (FIELD_IS_WARNING, changed_params.fields);
EXPECT_FALSE (changed_params.is_warning);
+ EXPECT_EQ (0, get_notify_call_count());
// ...and that it comes back if we unplug again...
changed_params = ChangedParams();
@@ -397,6 +454,9 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications)
wait_msec();
EXPECT_EQ (FIELD_IS_WARNING, changed_params.fields);
EXPECT_TRUE (changed_params.is_warning);
+ EXPECT_EQ (1, get_notify_call_count());
+ EXPECT_EQ (low_power_uri, get_notify_call_sound_file(0));
+ clear_method_calls();
// ...and that it's taken down if the power level is OK
changed_params = ChangedParams();
@@ -405,6 +465,7 @@ TEST_F(NotifyFixture, EventsThatChangeNotifications)
EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields);
EXPECT_STREQ (POWER_LEVEL_STR_OK, changed_params.power_level.c_str());
EXPECT_FALSE (changed_params.is_warning);
+ EXPECT_EQ (0, get_notify_call_count());
// cleanup
g_dbus_connection_signal_unsubscribe (bus, sub_tag);