From aa1e466e5f7684a8b624c34d466dda9d10a331d2 Mon Sep 17 00:00:00 2001 From: Nate Graham Date: Sun, 6 Jul 2025 23:20:47 -0400 Subject: [PATCH 1/2] Improve UX of USB plug/unplug notifications when popup is shown 1. When plugged or unplugged, revoke the opposite notification if it's visible. 2. Set the urgency to low so it won't clutter up the notification history. --- devicenotifications/devicenotifications.cpp | 55 ++++++++++++++++----- devicenotifications/devicenotifications.h | 5 ++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/devicenotifications/devicenotifications.cpp b/devicenotifications/devicenotifications.cpp index cc462f58f5..f326691c11 100644 --- a/devicenotifications/devicenotifications.cpp +++ b/devicenotifications/devicenotifications.cpp @@ -15,6 +15,7 @@ #include +#include #include K_PLUGIN_CLASS_WITH_JSON(KdedDeviceNotifications, "devicenotifications.json") @@ -375,13 +376,28 @@ void KdedDeviceNotifications::onDeviceAdded(const UdevDevice &device) return; } - const QString text = !displayName.isEmpty() ? i18n("%1 has been plugged in.", displayName.toHtmlEscaped()) : i18n("A USB device has been plugged in."); + // If the user unplugged something and then immediately plugged it in again, + // there's no need to keep the unplug notification around. + if (m_usbDeviceRemovedNotification) { + m_usbDeviceRemovedNotification->close(); + } + + // Only show one of these at a time. We already suppressed creating a bunch + // in quick succession for the dock/hub use case, so any that are created + // over that time limit anyway are not necessary to stack up. + if (m_usbDeviceAddedNotification) { + m_usbDeviceAddedNotification->close(); + } + + const QString text = !displayName.isEmpty() ? i18n("%1 has been connected.", displayName.toHtmlEscaped()) : i18n("A USB device has been connected."); + + m_usbDeviceAddedNotification = new KNotification(QStringLiteral("deviceAdded")); + m_usbDeviceAddedNotification->setFlags(KNotification::DefaultEvent); + m_usbDeviceAddedNotification->setIconName(QStringLiteral("drive-removable-media-usb")); + m_usbDeviceAddedNotification->setTitle(i18nc("@title:notifications", "USB Device Detected")); + m_usbDeviceAddedNotification->setText(text); + m_usbDeviceAddedNotification->sendEvent(); - KNotification::event(QStringLiteral("deviceAdded"), - i18nc("@title:notifications", "USB Device Detected"), - text, - QStringLiteral("drive-removable-media-usb"), - KNotification::DefaultEvent); m_deviceAddedTimer.start(); } @@ -401,13 +417,28 @@ void KdedDeviceNotifications::onDeviceRemoved(const UdevDevice &device) return; } - const QString text = !displayName.isEmpty() ? i18n("%1 has been unplugged.", displayName.toHtmlEscaped()) : i18n("A USB device has been unplugged."); + // If the user plugged something in and then immediately unplugged it again, + // there's no need to keep the plug notification around. + if (m_usbDeviceAddedNotification) { + m_usbDeviceAddedNotification->close(); + } + + // Only show one of these at a time. We already suppressed removing a bunch + // in quick succession for the dock/hub use case, so any that are removed + // over that time limit anyway are not necessary to stack up. + if (m_usbDeviceRemovedNotification) { + m_usbDeviceRemovedNotification->close(); + } + + const QString text = !displayName.isEmpty() ? i18n("%1 has been disconnected.", displayName.toHtmlEscaped()) : i18n("A USB device has been disconnected."); + + m_usbDeviceRemovedNotification = new KNotification(QStringLiteral("deviceRemoved")); + m_usbDeviceRemovedNotification->setFlags(KNotification::DefaultEvent); + m_usbDeviceRemovedNotification->setIconName(QStringLiteral("drive-removable-media-usb")); + m_usbDeviceRemovedNotification->setTitle(i18nc("@title:notifications", "USB Device Went Away")); + m_usbDeviceRemovedNotification->setText(text); + m_usbDeviceRemovedNotification->sendEvent(); - KNotification::event(QStringLiteral("deviceRemoved"), - i18nc("@title:notifications", "USB Device Removed"), - text, - QStringLiteral("drive-removable-media-usb"), - KNotification::DefaultEvent); m_deviceRemovedTimer.start(); } diff --git a/devicenotifications/devicenotifications.h b/devicenotifications/devicenotifications.h index 11334008b0..ab7e6b3ff9 100644 --- a/devicenotifications/devicenotifications.h +++ b/devicenotifications/devicenotifications.h @@ -8,11 +8,13 @@ #include #include +#include #include #include #include #include +#include #include @@ -98,4 +100,7 @@ private: QTimer m_deviceAddedTimer; QTimer m_deviceRemovedTimer; + + QPointer m_usbDeviceAddedNotification; + QPointer m_usbDeviceRemovedNotification; }; -- 2.51.0 From 05f72383fd0b29105f3b5494759500d26b38ffc2 Mon Sep 17 00:00:00 2001 From: Nate Graham Date: Fri, 11 Jul 2025 11:25:23 -0600 Subject: [PATCH 2/2] Delete closed notifications too Closing is async; make sure we actually delete them when we want them gone. --- devicenotifications/devicenotifications.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devicenotifications/devicenotifications.cpp b/devicenotifications/devicenotifications.cpp index f326691c11..987d65d805 100644 --- a/devicenotifications/devicenotifications.cpp +++ b/devicenotifications/devicenotifications.cpp @@ -380,6 +380,7 @@ void KdedDeviceNotifications::onDeviceAdded(const UdevDevice &device) // there's no need to keep the unplug notification around. if (m_usbDeviceRemovedNotification) { m_usbDeviceRemovedNotification->close(); + m_usbDeviceRemovedNotification = nullptr; } // Only show one of these at a time. We already suppressed creating a bunch @@ -387,6 +388,7 @@ void KdedDeviceNotifications::onDeviceAdded(const UdevDevice &device) // over that time limit anyway are not necessary to stack up. if (m_usbDeviceAddedNotification) { m_usbDeviceAddedNotification->close(); + m_usbDeviceAddedNotification = nullptr; } const QString text = !displayName.isEmpty() ? i18n("%1 has been connected.", displayName.toHtmlEscaped()) : i18n("A USB device has been connected."); @@ -421,6 +423,7 @@ void KdedDeviceNotifications::onDeviceRemoved(const UdevDevice &device) // there's no need to keep the plug notification around. if (m_usbDeviceAddedNotification) { m_usbDeviceAddedNotification->close(); + m_usbDeviceAddedNotification = nullptr; } // Only show one of these at a time. We already suppressed removing a bunch @@ -428,6 +431,7 @@ void KdedDeviceNotifications::onDeviceRemoved(const UdevDevice &device) // over that time limit anyway are not necessary to stack up. if (m_usbDeviceRemovedNotification) { m_usbDeviceRemovedNotification->close(); + m_usbDeviceRemovedNotification = nullptr; } const QString text = !displayName.isEmpty() ? i18n("%1 has been disconnected.", displayName.toHtmlEscaped()) : i18n("A USB device has been disconnected."); -- 2.51.0