1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-21 06:55:39 +00:00

Port more notification senders to non-XDG systems

* doc/lispref/os.texi (Desktop Notifications): Document that
`:timeout' is now implemented.

* java/org/gnu/emacs/EmacsDesktopNotification.java
(EmacsDesktopNotification): New field delay.
(display1): Set delay on Android 8.0 and up.

* lisp/erc/erc-desktop-notifications.el
(erc-notifications-notify): Call Android or Haiku notification
functions on those systems.

* lisp/gnus/gnus-notifications.el (gnus-notifications-action)
(gnus-notification-close): Remove dismissed notifications from
the notification to message map.
(gnus-notifications-notify): Call android-notifications-notify
if possible.

* src/androidselect.c (android_init_emacs_desktop_notification):
Update accordingly.
(android_notifications_notify_1): New argument TIMEOUT.
(Fandroid_notifications_notify): New argument QCtimeout.
(syms_of_androidselect) <QCtimeout>: New symbol.
This commit is contained in:
Po Lu 2024-03-13 10:59:39 +08:00
parent 4afafa0370
commit 6b40d557c4
5 changed files with 115 additions and 47 deletions

View File

@ -3244,6 +3244,7 @@ of parameters analogous to its namesake in
@item :on-action @var{on-action}
@item :on-cancel @var{on-close}
@item :actions @var{actions}
@item :timeout @var{timeout}
@item :resident @var{resident}
These have the same meaning as they do when used in calls to
@code{notifications-notify}, except that no more than three non-default

View File

@ -83,11 +83,16 @@ public final class EmacsDesktopNotification
notification. */
public final String[] actions, titles;
/* Delay in miliseconds after which this notification should be
automatically dismissed. */
public final long delay;
public
EmacsDesktopNotification (String title, String content,
String group, String tag, int icon,
int importance,
String[] actions, String[] titles)
String[] actions, String[] titles,
long delay)
{
this.content = content;
this.title = title;
@ -97,6 +102,7 @@ public final class EmacsDesktopNotification
this.importance = importance;
this.actions = actions;
this.titles = titles;
this.delay = delay;
}
@ -191,6 +197,8 @@ public final class EmacsDesktopNotification
builder.setContentTitle (title);
builder.setContentText (content);
builder.setSmallIcon (icon);
builder.setTimeoutAfter (delay);
insertActions (context, builder);
notification = builder.build ();
}

View File

@ -54,6 +54,9 @@
(defvar dbus-debug) ; used in the macroexpansion of dbus-ignore-errors
(declare-function haiku-notifications-notify "haikuselect.c")
(declare-function android-notifications-notify "androidselect.c")
(defun erc-notifications-notify (nick msg &optional privp)
"Notify that NICK send some MSG, where PRIVP should be non-nil for PRIVMSGs.
This will replace the last notification sent with this function."
@ -64,14 +67,19 @@ This will replace the last notification sent with this function."
(let* ((channel (if privp (erc-get-buffer nick) (current-buffer)))
(title (format "%s in %s" (xml-escape-string nick t) channel))
(body (xml-escape-string (erc-controls-strip msg) t)))
(notifications-notify :bus erc-notifications-bus
:title title
:body body
:replaces-id erc-notifications-last-notification
:app-icon erc-notifications-icon
:actions '("default" "Switch to buffer")
:on-action (lambda (&rest _)
(pop-to-buffer channel)))))))
(funcall (cond ((featurep 'android)
#'android-notifications-notify)
((featurep 'haiku)
#'haiku-notifications-notify)
(t #'notifications-notify))
:bus erc-notifications-bus
:title title
:body body
:replaces-id erc-notifications-last-notification
:app-icon erc-notifications-icon
:actions '("default" "Switch to buffer")
:on-action (lambda (&rest _)
(pop-to-buffer channel)))))))
(defun erc-notifications-PRIVMSG (_proc parsed)
(let ((nick (car (erc-parse-user (erc-response.sender parsed))))

View File

@ -83,27 +83,46 @@ not get notifications."
group
(delq article (gnus-list-of-unread-articles group)))
;; gnus-group-refresh-group
(gnus-group-update-group group)))))))
(gnus-group-update-group group))))))
;; Notifications are removed unless otherwise specified once they (or
;; an action of theirs) are selected
(assoc-delete-all id gnus-notifications-id-to-msg))
(defun gnus-notification-close (id reason)
"Remove ID from the alist of notification identifiers to messages.
REASON is ignored."
(assoc-delete-all id gnus-notifications-id-to-msg))
(defun gnus-notifications-notify (from subject photo-file)
"Send a notification about a new mail.
Return a notification id if any, or t on success."
(if (fboundp 'notifications-notify)
(if (featurep 'android)
(gnus-funcall-no-warning
'notifications-notify
'android-notifications-notify
:title from
:body subject
:actions '("read" "Read" "mark-read" "Mark As Read")
:on-action 'gnus-notifications-action
:app-icon (gnus-funcall-no-warning
'image-search-load-path "gnus/gnus.png")
:image-path photo-file
:app-name "Gnus"
:category "email.arrived"
:on-close 'gnus-notifications-close
:group "Email arrivals"
:timeout gnus-notifications-timeout)
(message "New message from %s: %s" from subject)
;; Don't return an id
t))
(if (fboundp 'notifications-notify)
(gnus-funcall-no-warning
'notifications-notify
:title from
:body subject
:actions '("read" "Read" "mark-read" "Mark As Read")
:on-action 'gnus-notifications-action
:on-close 'gnus-notifications-close
:app-icon (gnus-funcall-no-warning
'image-search-load-path "gnus/gnus.png")
:image-path photo-file
:app-name "Gnus"
:category "email.arrived"
:timeout gnus-notifications-timeout)
(message "New message from %s: %s" from subject)
;; Don't return an id
t)))
(declare-function gravatar-retrieve-synchronously "gravatar.el"
(mail-address))

View File

@ -526,7 +526,7 @@ android_init_emacs_desktop_notification (void)
FIND_METHOD (init, "<init>", "(Ljava/lang/String;"
"Ljava/lang/String;Ljava/lang/String;"
"Ljava/lang/String;II[Ljava/lang/String;"
"[Ljava/lang/String;)V");
"[Ljava/lang/String;J)V");
FIND_METHOD (display, "display", "()V");
#undef FIND_METHOD
}
@ -567,16 +567,17 @@ android_locate_icon (const char *name)
}
/* Display a desktop notification with the provided TITLE, BODY,
REPLACES_ID, GROUP, ICON, URGENCY, ACTIONS, RESIDENT, ACTION_CB and
CLOSE_CB. Return an identifier for the resulting notification. */
REPLACES_ID, GROUP, ICON, URGENCY, ACTIONS, TIMEOUT, RESIDENT,
ACTION_CB and CLOSE_CB. Return an identifier for the resulting
notification. */
static intmax_t
android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
Lisp_Object replaces_id,
Lisp_Object group, Lisp_Object icon,
Lisp_Object urgency, Lisp_Object actions,
Lisp_Object resident, Lisp_Object action_cb,
Lisp_Object close_cb)
Lisp_Object timeout, Lisp_Object resident,
Lisp_Object action_cb, Lisp_Object close_cb)
{
static intmax_t counter;
intmax_t id;
@ -593,6 +594,7 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
jint nitems, i;
jstring item;
Lisp_Object length;
jlong timeout_val;
if (EQ (urgency, Qlow))
type = 2; /* IMPORTANCE_LOW */
@ -603,6 +605,23 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
else
signal_error ("Invalid notification importance given", urgency);
/* Decode the timeout. */
timeout_val = 0;
if (!NILP (timeout))
{
CHECK_INTEGER (timeout);
if (!integer_to_intmax (timeout, &id)
|| id > TYPE_MAXIMUM (jlong)
|| id < TYPE_MINIMUM (jlong))
signal_error ("Invalid timeout", timeout);
if (id > 0)
timeout_val = id;
}
nitems = 0;
/* If ACTIONS is provided, split it into two arrays of Java strings
@ -714,7 +733,8 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
notification_class.init,
title1, body1, group1,
identifier1, icon1, type,
action_keys, action_titles);
action_keys, action_titles,
timeout_val);
android_exception_check_6 (title1, body1, group1, identifier1,
action_titles, action_keys);
@ -723,12 +743,8 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
ANDROID_DELETE_LOCAL_REF (body1);
ANDROID_DELETE_LOCAL_REF (group1);
ANDROID_DELETE_LOCAL_REF (identifier1);
if (action_keys)
ANDROID_DELETE_LOCAL_REF (action_keys);
if (action_titles)
ANDROID_DELETE_LOCAL_REF (action_titles);
ANDROID_DELETE_LOCAL_REF (action_keys);
ANDROID_DELETE_LOCAL_REF (action_titles);
/* Display the notification. */
(*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
@ -769,8 +785,14 @@ keywords is understood:
The action for which CALLBACK is called when the
notification itself is selected is named "default",
its existence is implied, and its TITLE is ignored.
No more than three actions can be defined, not
counting any action with "default" as its key.
No more than three actions defined here will be
displayed, not counting any with "default" as its
key.
:timeout Number of miliseconds from the display of the
notification at which it will be automatically
dismissed, or a value of zero or smaller if it
is to remain until user action is taken to dismiss
it.
:resident When set the notification will not be automatically
dismissed when it or an action is selected.
:on-action Function to call when an action is invoked.
@ -780,12 +802,15 @@ keywords is understood:
with the notification id and the symbol `undefined'
for arguments.
The notification group is ignored on Android 7.1 and earlier versions
of Android. Outside such older systems, it identifies a category that
will be displayed in the system Settings menu, and the urgency
provided always extends to affect all notifications displayed within
that category. If the group is not provided, it defaults to the
string "Desktop Notifications".
The notification group and timeout are ignored on Android 7.1 and
earlier versions of Android. On more recent versions, the urgency
identifies a category that will be displayed in the system Settings
menu, and the urgency provided always extends to affect all
notifications displayed within that category, though it may be ignored
if higher than any previously-specified urgency or if the user have
already configured a different urgency for this category from Settings.
If the group is not provided, it defaults to the string "Desktop
Notifications" with the urgency suffixed.
Each caller should strive to provide one unchanging combination of
notification group and urgency for each kind of notification it sends,
@ -795,8 +820,8 @@ first notification sent to its notification group.
The provided icon should be the name of a "drawable resource" present
within the "android.R.drawable" class designating an icon with a
transparent background. If no icon is provided (or the icon is absent
from this system), it defaults to "ic_dialog_alert".
transparent background. Should no icon be provided (or the icon is
absent from this system), it defaults to "ic_dialog_alert".
Actions specified with :actions cannot be displayed on Android 4.0 and
earlier versions of the system.
@ -814,17 +839,18 @@ this function.
usage: (android-notifications-notify &rest ARGS) */)
(ptrdiff_t nargs, Lisp_Object *args)
{
Lisp_Object title, body, replaces_id, group, urgency, resident;
Lisp_Object title, body, replaces_id, group, urgency, timeout, resident;
Lisp_Object icon;
Lisp_Object key, value, actions, action_cb, close_cb;
ptrdiff_t i;
AUTO_STRING (default_icon, "ic_dialog_alert");
if (!android_init_gui)
error ("No Android display connection!");
/* Clear each variable above. */
title = body = replaces_id = group = icon = urgency = actions = Qnil;
resident = action_cb = close_cb = Qnil;
timeout = resident = action_cb = close_cb = Qnil;
/* If NARGS is odd, error. */
@ -852,6 +878,8 @@ usage: (android-notifications-notify &rest ARGS) */)
icon = value;
else if (EQ (key, QCactions))
actions = value;
else if (EQ (key, QCtimeout))
timeout = value;
else if (EQ (key, QCresident))
resident = value;
else if (EQ (key, QCon_action))
@ -874,16 +902,19 @@ usage: (android-notifications-notify &rest ARGS) */)
urgency = Qlow;
if (NILP (group))
group = build_string ("Desktop Notifications");
{
AUTO_STRING (format, "Desktop Notifications (%s importance)");
group = CALLN (Fformat, format, urgency);
}
if (NILP (icon))
icon = build_string ("ic_dialog_alert");
icon = default_icon;
else
CHECK_STRING (icon);
return make_int (android_notifications_notify_1 (title, body, replaces_id,
group, icon, urgency,
actions, resident,
actions, timeout, resident,
action_cb, close_cb));
}
@ -1001,6 +1032,7 @@ syms_of_androidselect (void)
DEFSYM (QCurgency, ":urgency");
DEFSYM (QCicon, ":icon");
DEFSYM (QCactions, ":actions");
DEFSYM (QCtimeout, ":timeout");
DEFSYM (QCresident, ":resident");
DEFSYM (QCon_action, ":on-action");
DEFSYM (QCon_close, ":on-close");