diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 34e79186..7e87ae80 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -25,7 +25,7 @@ jobs:
- name: Install Dependencies
run: |
apt update
- apt install -y libaccountsservice-dev libdbus-1-dev libgranite-dev libgeoclue-2-dev meson valac
+ apt install -y libaccountsservice-dev libdbus-1-dev libgranite-dev libgeoclue-2-dev libfwupd-dev meson valac
- name: Build
env:
DESTDIR: out
diff --git a/README.md b/README.md
index 8a009da2..c3e552b1 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,9 @@ You'll need the following dependencies:
* gobject-2.0
* libaccountsservice-dev
* libdbus-1-dev
-* libgranite-dev
+* libfwupd-dev
* libgeoclue-2-dev
+* libgranite-dev
* meson
* valac
diff --git a/data/autostart.desktop b/data/autostart.desktop
index 0d8633fa..ef712fc7 100644
--- a/data/autostart.desktop
+++ b/data/autostart.desktop
@@ -3,6 +3,7 @@ Type=Application
Name=Elementary Settings Daemon
Comment=Elementary Settings Daemon
Exec=io.elementary.settings-daemon
+Icon=io.elementary.settings-daemon
Terminal=false
Categories=System;Core;
NoDisplay=true
diff --git a/data/icons/128.svg b/data/icons/128.svg
new file mode 100644
index 00000000..71c5cc66
--- /dev/null
+++ b/data/icons/128.svg
@@ -0,0 +1,349 @@
+
+
diff --git a/data/icons/24.svg b/data/icons/24.svg
new file mode 100644
index 00000000..a70ef4d3
--- /dev/null
+++ b/data/icons/24.svg
@@ -0,0 +1,297 @@
+
+
diff --git a/data/icons/32.svg b/data/icons/32.svg
new file mode 100644
index 00000000..960f517a
--- /dev/null
+++ b/data/icons/32.svg
@@ -0,0 +1,301 @@
+
+
diff --git a/data/icons/48.svg b/data/icons/48.svg
new file mode 100644
index 00000000..46648295
--- /dev/null
+++ b/data/icons/48.svg
@@ -0,0 +1,481 @@
+
+
diff --git a/data/icons/64.svg b/data/icons/64.svg
new file mode 100644
index 00000000..ec7989aa
--- /dev/null
+++ b/data/icons/64.svg
@@ -0,0 +1,297 @@
+
+
diff --git a/data/io.elementary.settings-daemon.check-for-firmware-updates.service b/data/io.elementary.settings-daemon.check-for-firmware-updates.service
new file mode 100644
index 00000000..5f79b95e
--- /dev/null
+++ b/data/io.elementary.settings-daemon.check-for-firmware-updates.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=Check for firmware updates daily
+ConditionACPower=true
+After=network.target network-online.target systemd-networkd.service NetworkManager.service connman.service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/busctl --user call io.elementary.settings-daemon /io/elementary/settings_daemon org.freedesktop.Application ActivateAction sava{sv} "check-firmware-updates" 0 0
diff --git a/data/io.elementary.settings-daemon.check-for-firmware-updates.timer b/data/io.elementary.settings-daemon.check-for-firmware-updates.timer
new file mode 100644
index 00000000..43ed38aa
--- /dev/null
+++ b/data/io.elementary.settings-daemon.check-for-firmware-updates.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Check for firmware updates daily
+
+[Timer]
+OnCalendar=daily
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/data/meson.build b/data/meson.build
index 4db0d403..1c59294c 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -1,9 +1,15 @@
install_data(
'autostart.desktop',
- install_dir: join_paths(get_option('sysconfdir'), 'xdg', 'autostart'),
+ install_dir: join_paths(get_option('datadir'), 'applications'),
rename: meson.project_name() + '.desktop'
)
+meson.add_install_script(
+ symlink,
+ join_paths(get_option('datadir'), 'applications', meson.project_name() + '.desktop'),
+ join_paths(get_option('sysconfdir'), 'xdg', 'autostart', meson.project_name() + '.desktop'),
+)
+
dbus_dep = dependency('dbus-1')
dbus_interfaces_dir = dbus_dep.get_pkgconfig_variable('interfaces_dir', define_variable: ['datadir', datadir])
@@ -34,3 +40,31 @@ i18n.merge_file(
install: true,
install_dir: join_paths(get_option('datadir'), 'metainfo'),
)
+
+systemd = dependency('systemd')
+systemd_system_unit_dir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
+
+install_data(
+ meson.project_name() + '.check-for-firmware-updates.service',
+ install_dir: systemd_system_unit_dir
+)
+
+install_data(
+ meson.project_name() + '.check-for-firmware-updates.timer',
+ install_dir: systemd_system_unit_dir
+)
+
+icon_sizes = ['24', '32', '48', '64', '128']
+
+foreach i : icon_sizes
+ install_data(
+ join_paths('icons', i + '.svg'),
+ install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i, 'apps'),
+ rename: meson.project_name() + '.svg'
+ )
+ install_data(
+ join_paths('icons', i + '.svg'),
+ install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i + '@2', 'apps'),
+ rename: meson.project_name() + '.svg'
+ )
+endforeach
diff --git a/meson.build b/meson.build
index fbf051ad..99b937ac 100644
--- a/meson.build
+++ b/meson.build
@@ -4,10 +4,17 @@ project('io.elementary.settings-daemon',
license: 'GPL3',
)
+fwupd_dep = dependency('fwupd')
gio_dep = dependency ('gio-2.0')
glib_dep = dependency('glib-2.0')
granite_dep = dependency('granite', version: '>= 5.3.0')
i18n = import('i18n')
+gettext_name = meson.project_name()
+
+add_project_arguments(
+ '-DGETTEXT_PACKAGE="@0@"'.format(gettext_name),
+ language:'c'
+)
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required : false)
diff --git a/po/POTFILES b/po/POTFILES
index 31a08460..469c9356 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -1 +1,2 @@
data/settings-daemon.appdata.xml.in
+src/Application.vala
diff --git a/src/Application.vala b/src/Application.vala
index 39c8a5e1..543db2ac 100644
--- a/src/Application.vala
+++ b/src/Application.vala
@@ -48,6 +48,62 @@ public class SettingsDaemon.Application : GLib.Application {
add_main_option_entries (OPTIONS);
housekeeping = new Backends.Housekeeping ();
+
+ var check_firmware_updates_action = new SimpleAction ("check-firmware-updates", null);
+ check_firmware_updates_action.activate.connect (() => {
+ var fwupd_client = new Fwupd.Client ();
+ var num_updates = 0;
+ try {
+ var devices = fwupd_client.get_devices ();
+ for (int i = 0; i < devices.length; i++) {
+ var device = devices[i];
+ if (device.has_flag (Fwupd.DEVICE_FLAG_UPDATABLE)) {
+ Fwupd.Release? release = null;
+ try {
+ var upgrades = fwupd_client.get_upgrades (device.get_id ());
+
+ if (upgrades != null) {
+ release = upgrades[0];
+ }
+ } catch (Error e) {
+ warning (e.message);
+ }
+
+ if (release != null && device.get_version () != release.get_version ()) {
+ num_updates++;
+ }
+ }
+ }
+ } catch (Error e) {
+ warning (e.message);
+ }
+
+ if (num_updates != 0U) {
+ string title = ngettext ("Firmware Update Available", "Firmware Updates Available", num_updates);
+ string body = ngettext ("%u update is available for your hardware", "%u updates are available for your hardware", num_updates).printf (num_updates);
+
+ var notification = new Notification (title);
+ notification.set_body (body);
+ notification.set_icon (new ThemedIcon ("application-x-firmware"));
+ notification.set_default_action ("app.show-firmware-updates");
+
+ send_notification ("io.elementary.settings-daemon.firmware.updates", notification);
+ } else {
+ withdraw_notification ("io.elementary.settings-daemon.firmware.updates");
+ }
+ });
+
+ var show_firmware_updates_action = new SimpleAction ("show-firmware-updates", null);
+ show_firmware_updates_action.activate.connect (() => {
+ try {
+ Gtk.show_uri_on_window (null, "settings://about/firmware", Gdk.CURRENT_TIME);
+ } catch (Error e) {
+ critical (e.message);
+ }
+ });
+
+ add_action (check_firmware_updates_action);
+ add_action (show_firmware_updates_action);
}
public override int handle_local_options (VariantDict options) {
diff --git a/src/meson.build b/src/meson.build
index 7c726d97..a61bb9e6 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -14,6 +14,7 @@ executable(
sources,
dependencies: [
config_dep,
+ fwupd_dep,
gio_dep,
glib_dep,
granite_dep,