[gnome-software/947-review-timing-and-content-of-software-updates-notifications: 3/3] gs-update-monitor: Review timing and content of software updates notifications
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/947-review-timing-and-content-of-software-updates-notifications: 3/3] gs-update-monitor: Review timing and content of software updates notifications
- Date: Tue, 5 Jan 2021 18:37:12 +0000 (UTC)
commit 70be111a7fe4c36a874a7af39226df8a786d87e2
Author: Milan Crha <mcrha redhat com>
Date: Tue Jan 5 19:26:31 2021 +0100
gs-update-monitor: Review timing and content of software updates notifications
Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/947
data/org.gnome.software.gschema.xml | 4 +
lib/gs-utils.c | 5 +
src/gs-update-monitor.c | 262 +++++++++++++++++++++---------------
3 files changed, 166 insertions(+), 105 deletions(-)
---
diff --git a/data/org.gnome.software.gschema.xml b/data/org.gnome.software.gschema.xml
index ce09c0a0..36a219c1 100644
--- a/data/org.gnome.software.gschema.xml
+++ b/data/org.gnome.software.gschema.xml
@@ -55,6 +55,10 @@
<default>0</default>
<summary>The last upgrade notification timestamp</summary>
</key>
+ <key name="update-notification-timestamp" type="x">
+ <default>0</default>
+ <summary>The last update notification timestamp</summary>
+ </key>
<key name="security-timestamp" type="x">
<default>0</default>
<summary>The timestamp of the first security update, cleared after update</summary>
diff --git a/lib/gs-utils.c b/lib/gs-utils.c
index 1ba5976a..a66bc496 100644
--- a/lib/gs-utils.c
+++ b/lib/gs-utils.c
@@ -1211,6 +1211,9 @@ gs_utils_parse_evr (const gchar *evr,
* Sets the value of online-updates-timestamp to current epoch. "online-updates-timestamp" represents
* the last time the system was online and got any updates.
*
+ * It also sets the "update-notification-timestamp", to not receive
+ * notifications about available updates too early after the actual
+ * update happened.
**/
void
gs_utils_set_online_updates_timestamp (GSettings *settings)
@@ -1221,6 +1224,8 @@ gs_utils_set_online_updates_timestamp (GSettings *settings)
now = g_date_time_new_now_local ();
g_settings_set (settings, "online-updates-timestamp", "x", g_date_time_to_unix (now));
+
+ g_settings_set (settings, "update-notification-timestamp", "x", g_date_time_to_unix (now));
}
/* vim: set noexpandtab: */
diff --git a/src/gs-update-monitor.c b/src/gs-update-monitor.c
index 7a4b3c92..e026baf8 100644
--- a/src/gs-update-monitor.c
+++ b/src/gs-update-monitor.c
@@ -80,103 +80,82 @@ reenable_offline_update_notification (gpointer data)
}
static void
-notify_offline_update_available (GsUpdateMonitor *monitor)
+check_updates_kind (GsAppList *apps,
+ gboolean *out_has_important,
+ gboolean *out_all_downloaded,
+ gboolean *out_any_downloaded)
{
- const gchar *title;
- const gchar *body;
- guint64 elapsed_security = 0;
- guint64 security_timestamp = 0;
- g_autoptr(GNotification) n = NULL;
-
- if (gs_application_has_active_window (GS_APPLICATION (monitor->application)))
- return;
- if (monitor->notification_blocked_id > 0)
- return;
-
- /* rate limit update notifications to once per hour */
- monitor->notification_blocked_id = g_timeout_add_seconds (SECONDS_IN_AN_HOUR,
reenable_offline_update_notification, monitor);
+ gboolean has_important, all_downloaded, any_downloaded;
+ guint ii, len;
+ GsApp *app;
- /* get time in days since we saw the first unapplied security update */
- g_settings_get (monitor->settings,
- "security-timestamp", "x", &security_timestamp);
- if (security_timestamp > 0) {
- elapsed_security = (guint64) g_get_monotonic_time () - security_timestamp;
- elapsed_security /= G_USEC_PER_SEC;
- elapsed_security /= 60 * 60 * 24;
- }
+ len = gs_app_list_length (apps);
+ has_important = FALSE;
+ all_downloaded = len > 0;
+ any_downloaded = FALSE;
- /* only show the scary warning after the user has ignored
- * security updates for a full day */
- if (elapsed_security > 1) {
- title = _("Security Updates Pending");
- body = _("It is recommended that you install important updates now");
- n = g_notification_new (title);
- g_notification_set_body (n, body);
- g_notification_add_button (n, _("Restart & Install"), "app.reboot-and-install");
- g_notification_set_default_action_and_target (n, "app.set-mode", "s", "updates");
- g_application_send_notification (monitor->application, "updates-available", n);
- } else {
- title = _("Software Updates Available");
- body = _("Important OS and application updates are ready to be installed");
- n = g_notification_new (title);
- g_notification_set_body (n, body);
- g_notification_add_button (n, _("Not Now"), "app.nop");
- g_notification_add_button_with_target (n, _("View"), "app.set-mode", "s", "updates");
- g_notification_set_default_action_and_target (n, "app.set-mode", "s", "updates");
- g_application_send_notification (monitor->application, "updates-available", n);
- }
-}
+ for (ii = 0; ii < len && (!has_important || all_downloaded || !any_downloaded); ii++) {
+ app = gs_app_list_index (apps, ii);
-static gboolean
-has_important_updates (GsAppList *apps)
-{
- guint i;
- GsApp *app;
+ has_important = has_important ||
+ gs_app_get_update_urgency (app) == AS_URGENCY_KIND_CRITICAL ||
+ gs_app_get_update_urgency (app) == AS_URGENCY_KIND_HIGH;
- for (i = 0; i < gs_app_list_length (apps); i++) {
- app = gs_app_list_index (apps, i);
- if (gs_app_get_update_urgency (app) == AS_URGENCY_KIND_CRITICAL ||
- gs_app_get_update_urgency (app) == AS_URGENCY_KIND_HIGH)
- return TRUE;
+ /* took from gs-updates-section.c: _all_offline_updates_downloaded();
+ the app is considered downloaded, when its download size is 0 */
+ if (gs_app_get_size_download (app))
+ all_downloaded = FALSE;
+ else
+ any_downloaded = TRUE;
}
- return FALSE;
+ *out_has_important = has_important;
+ *out_all_downloaded = all_downloaded;
+ *out_any_downloaded = any_downloaded;
}
static gboolean
-check_if_timestamp_more_than_a_week_ago (GsUpdateMonitor *monitor, const gchar *timestamp)
+get_timestamp_difference_days (GsUpdateMonitor *monitor, const gchar *timestamp, gint64 *out_days)
{
- GTimeSpan d;
gint64 tmp;
g_autoptr(GDateTime) last_update = NULL;
g_autoptr(GDateTime) now = NULL;
+ g_return_val_if_fail (out_days != NULL, FALSE);
+
g_settings_get (monitor->settings, timestamp, "x", &tmp);
if (tmp == 0)
- return TRUE;
+ return FALSE;
last_update = g_date_time_new_from_unix_local (tmp);
if (last_update == NULL) {
g_warning ("failed to set timestamp %" G_GINT64_FORMAT, tmp);
- return TRUE;
+ return FALSE;
}
now = g_date_time_new_now_local ();
- d = g_date_time_difference (now, last_update);
- if (d >= 7 * G_TIME_SPAN_DAY)
- return TRUE;
- return FALSE;
+ *out_days = g_date_time_difference (now, last_update) / G_TIME_SPAN_DAY;
+
+ return TRUE;
}
static gboolean
-no_updates_for_a_week (GsUpdateMonitor *monitor)
+check_if_timestamp_more_than_days_ago (GsUpdateMonitor *monitor, const gchar *timestamp, guint days)
{
- if (check_if_timestamp_more_than_a_week_ago (monitor, "install-timestamp") ||
- check_if_timestamp_more_than_a_week_ago (monitor, "online-updates-timestamp"))
+ gint64 timestamp_days;
+
+ if (!get_timestamp_difference_days (monitor, timestamp, ×tamp_days))
return TRUE;
- return FALSE;
+ return timestamp_days >= days;
+}
+
+static gboolean
+no_notification_for_days (GsUpdateMonitor *monitor,
+ guint days)
+{
+ return check_if_timestamp_more_than_days_ago (monitor, "update-notification-timestamp", days);
}
static gboolean
@@ -189,6 +168,108 @@ should_download_updates (GsUpdateMonitor *monitor)
#endif
}
+/* The days below are discussed at https://gitlab.gnome.org/GNOME/gnome-software/-/issues/947 */
+static gboolean
+should_notify_about_pending_updates (GsUpdateMonitor *monitor,
+ GsAppList *apps,
+ const gchar **out_title,
+ const gchar **out_body)
+{
+ gboolean has_important = FALSE, all_downloaded = FALSE, any_downloaded = FALSE;
+ gboolean should_download, res = FALSE;
+ gint64 timestamp_days;
+
+ if (!get_timestamp_difference_days (monitor, "update-notification-timestamp", ×tamp_days)) {
+ /* Large-enough number to succeed for the initial test */
+ timestamp_days = 365;
+ }
+
+ should_download = should_download_updates (monitor);
+ check_updates_kind (apps, &has_important, &all_downloaded, &any_downloaded);
+
+ if (!gs_app_list_length (apps)) {
+ /* Notify only when the download is disabled and it's the 4th day or it's more than 7 days */
+ if (!should_download && (timestamp_days >= 7 || timestamp_days == 4)) {
+ *out_title = _("Software Updates Are Out of Date");
+ *out_body = _("Please check for software updates.");
+ res = TRUE;
+ }
+ } else if (has_important) {
+ if (timestamp_days >= 1) {
+ if (all_downloaded) {
+ *out_title = _("Critical Software Update Ready to Install");
+ *out_body = _("An important software update is ready to be installed.");
+ res = TRUE;
+ } else {
+ *out_title = _("Critical Software Updates Available to Download");
+ *out_body = _("Important: critical software updates are waiting.");
+ res = TRUE;
+ }
+ }
+ } else if (all_downloaded) {
+ if (timestamp_days >= 3) {
+ *out_title = _("Software Updates Ready to Install");
+ *out_body = _("Software updates are waiting and ready to be installed.");
+ res = TRUE;
+ }
+ /* To not hide downloaded updates for 14 days when new updates were discovered meanwhile */
+ } else if (timestamp_days >= (any_downloaded ? 3 : 14)) {
+ *out_title = _("Software Updates Available to Download");
+ *out_body = _("Please download waiting software updates.");
+ res = TRUE;
+ }
+
+ g_debug ("%s: last_test_days:%" G_GINT64_FORMAT " n-apps:%u should_download:%d has_important:%d "
+ "all_downloaded:%d any_downloaded:%d res:%d%s%s%s%s", G_STRFUNC,
+ timestamp_days, gs_app_list_length (apps), should_download, has_important, all_downloaded,
any_downloaded, res,
+ res ? " reason:" : "",
+ res ? *out_title : "",
+ res ? "|" : "",
+ res ? *out_body : "");
+
+ return res;
+}
+
+static void
+reset_update_notification_timestamp (GsUpdateMonitor *monitor)
+{
+ g_autoptr(GDateTime) now = NULL;
+
+ now = g_date_time_new_now_local ();
+ g_settings_set (monitor->settings, "update-notification-timestamp", "x",
+ g_date_time_to_unix (now));
+}
+
+static void
+notify_about_pending_updates (GsUpdateMonitor *monitor,
+ GsAppList *apps)
+{
+ const gchar *title = NULL, *body = NULL;
+ g_autoptr(GNotification) nn = NULL;
+
+ if (monitor->notification_blocked_id > 0)
+ return;
+
+ /* rate limit update notifications to once per day */
+ monitor->notification_blocked_id = g_timeout_add_seconds (24 * SECONDS_IN_AN_HOUR,
reenable_offline_update_notification, monitor);
+
+ if (!should_notify_about_pending_updates (monitor, apps, &title, &body))
+ return;
+
+ g_debug ("Notify about update: '%s'", title);
+
+ nn = g_notification_new (title);
+ g_notification_set_body (nn, body);
+ g_notification_set_default_action_and_target (nn, "app.set-mode", "s", "updates");
+ g_application_send_notification (monitor->application, "updates-available", nn);
+
+ /* Keep the old notification time when there are no updates and the update download is disabled,
+ to notify the user every day after 7 days of no update check */
+ if (gs_app_list_length (apps) ||
+ should_download_updates (monitor))
+ reset_update_notification_timestamp (monitor);
+}
+
static gboolean
_filter_by_app_kind (GsApp *app, gpointer user_data)
{
@@ -389,12 +470,8 @@ download_finished_cb (GObject *object, GAsyncResult *res, gpointer data)
}
/* show a notification for offline updates */
- if (gs_app_list_length (update_offline) > 0) {
- if (has_important_updates (update_offline) ||
- no_updates_for_a_week (monitor)) {
- notify_offline_update_available (monitor);
- }
- }
+ if (gs_app_list_length (update_offline) > 0)
+ notify_about_pending_updates (monitor, update_offline);
}
static void
@@ -406,7 +483,6 @@ get_updates_finished_cb (GObject *object, GAsyncResult *res, gpointer data)
guint64 security_timestamp_old = 0;
g_autoptr(GError) error = NULL;
g_autoptr(GsAppList) apps = NULL;
- gboolean download_updates;
/* get result */
apps = gs_plugin_loader_job_process_finish (GS_PLUGIN_LOADER (object), res, &error);
@@ -441,13 +517,9 @@ get_updates_finished_cb (GObject *object, GAsyncResult *res, gpointer data)
g_debug ("got %u updates", gs_app_list_length (apps));
-#ifdef HAVE_MOGWAI
- download_updates = TRUE;
-#else
- download_updates = g_settings_get_boolean (monitor->settings, "download-updates");
-#endif
-
- if (download_updates) {
+ if (should_download_updates (monitor) &&
+ (security_timestamp_old != security_timestamp ||
+ no_notification_for_days (monitor, 14))) {
g_autoptr(GsPluginJob) plugin_job = NULL;
/* download any updates; individual plugins are responsible for deciding
@@ -464,37 +536,16 @@ get_updates_finished_cb (GObject *object, GAsyncResult *res, gpointer data)
download_finished_cb,
monitor);
} else {
- /* notify immediately if auto-updates are turned off */
- if (has_important_updates (apps) ||
- no_updates_for_a_week (monitor)) {
- notify_offline_update_available (monitor);
- }
+ notify_about_pending_updates (monitor, apps);
+
+ reset_update_notification_timestamp (monitor);
}
}
static gboolean
should_show_upgrade_notification (GsUpdateMonitor *monitor)
{
- GTimeSpan d;
- gint64 tmp;
- g_autoptr(GDateTime) now = NULL;
- g_autoptr(GDateTime) then = NULL;
-
- g_settings_get (monitor->settings, "upgrade-notification-timestamp", "x", &tmp);
- if (tmp == 0)
- return TRUE;
- then = g_date_time_new_from_unix_local (tmp);
- if (then == NULL) {
- g_warning ("failed to parse timestamp %" G_GINT64_FORMAT, tmp);
- return TRUE;
- }
-
- now = g_date_time_new_now_local ();
- d = g_date_time_difference (now, then);
- if (d >= 7 * G_TIME_SPAN_DAY)
- return TRUE;
-
- return FALSE;
+ return check_if_timestamp_more_than_days_ago (monitor, "upgrade-notification-timestamp", 7);
}
static void
@@ -1042,6 +1093,7 @@ get_updates_historical_cb (GObject *object, GAsyncResult *res, gpointer data)
g_settings_set (monitor->settings,
"install-timestamp", "x", gs_app_get_install_date (app));
+ reset_update_notification_timestamp (monitor);
}
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]