aboutsummaryrefslogtreecommitdiffstats
path: root/src/androidselect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/androidselect.c')
-rw-r--r--src/androidselect.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/androidselect.c b/src/androidselect.c
index 9910e7921de..5551598032d 100644
--- a/src/androidselect.c
+++ b/src/androidselect.c
@@ -22,6 +22,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
22#include <minmax.h> 22#include <minmax.h>
23#include <unistd.h> 23#include <unistd.h>
24 24
25#include <boot-time.h>
26#include <sys/types.h>
27
25#include "lisp.h" 28#include "lisp.h"
26#include "blockinput.h" 29#include "blockinput.h"
27#include "coding.h" 30#include "coding.h"
@@ -466,6 +469,232 @@ does not have any corresponding data. In that case, use
466 469
467 470
468 471
472/* Desktop notifications. `android-desktop-notify' implements a
473 facsimile of `notifications-notify'. */
474
475/* Structure describing the EmacsDesktopNotification class. */
476
477struct android_emacs_desktop_notification
478{
479 jclass class;
480 jmethodID init;
481 jmethodID display;
482};
483
484/* Methods provided by the EmacsDesktopNotification class. */
485static struct android_emacs_desktop_notification notification_class;
486
487/* Initialize virtual function IDs and class pointers tied to the
488 EmacsDesktopNotification class. */
489
490static void
491android_init_emacs_desktop_notification (void)
492{
493 jclass old;
494
495 notification_class.class
496 = (*android_java_env)->FindClass (android_java_env,
497 "org/gnu/emacs/EmacsDesktopNotification");
498 eassert (notification_class.class);
499
500 old = notification_class.class;
501 notification_class.class
502 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
503 old);
504 ANDROID_DELETE_LOCAL_REF (old);
505
506 if (!notification_class.class)
507 emacs_abort ();
508
509#define FIND_METHOD(c_name, name, signature) \
510 notification_class.c_name \
511 = (*android_java_env)->GetMethodID (android_java_env, \
512 notification_class.class, \
513 name, signature); \
514 assert (notification_class.c_name);
515
516 FIND_METHOD (init, "<init>", "(Ljava/lang/String;"
517 "Ljava/lang/String;Ljava/lang/String;"
518 "Ljava/lang/String;I)V");
519 FIND_METHOD (display, "display", "()V");
520#undef FIND_METHOD
521}
522
523/* Display a desktop notification with the provided TITLE, BODY,
524 REPLACES_ID, GROUP and URGENCY. Return an identifier for the
525 resulting notification. */
526
527static intmax_t
528android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
529 Lisp_Object replaces_id,
530 Lisp_Object group, Lisp_Object urgency)
531{
532 static intmax_t counter;
533 intmax_t id;
534 jstring title1, body1, group1, identifier1;
535 jint type;
536 jobject notification;
537 char identifier[INT_STRLEN_BOUND (int)
538 + INT_STRLEN_BOUND (long int)
539 + INT_STRLEN_BOUND (intmax_t)
540 + sizeof "..."];
541 struct timespec boot_time;
542
543 if (EQ (urgency, Qlow))
544 type = 2; /* IMPORTANCE_LOW */
545 else if (EQ (urgency, Qnormal))
546 type = 3; /* IMPORTANCE_DEFAULT */
547 else if (EQ (urgency, Qcritical))
548 type = 4; /* IMPORTANCE_HIGH */
549 else
550 signal_error ("Invalid notification importance given", urgency);
551
552 if (NILP (replaces_id))
553 {
554 /* Generate a new identifier. */
555 INT_ADD_WRAPV (counter, 1, &counter);
556 id = counter;
557 }
558 else
559 {
560 CHECK_INTEGER (replaces_id);
561 if (!integer_to_intmax (replaces_id, &id))
562 id = -1; /* Overflow. */
563 }
564
565 /* Generate a unique identifier for this notification. Because
566 Android persists notifications past system shutdown, also include
567 the boot time within IDENTIFIER. Scale it down to avoid being
568 perturbed by minor instabilities in the returned boot time,
569 however. */
570
571 boot_time.tv_sec = 0;
572 get_boot_time (&boot_time);
573 sprintf (identifier, "%d.%ld.%jd", (int) getpid (),
574 (long int) (boot_time.tv_sec / 2), id);
575
576 /* Encode all strings into their Java counterparts. */
577 title1 = android_build_string (title);
578 body1 = android_build_string (body);
579 group1 = android_build_string (group);
580 identifier1 = android_build_jstring (identifier);
581
582 /* Create the notification. */
583 notification
584 = (*android_java_env)->NewObject (android_java_env,
585 notification_class.class,
586 notification_class.init,
587 title1, body1, group1,
588 identifier1, type);
589 android_exception_check_4 (title1, body1, group1, identifier1);
590
591 /* Delete unused local references. */
592 ANDROID_DELETE_LOCAL_REF (title1);
593 ANDROID_DELETE_LOCAL_REF (body1);
594 ANDROID_DELETE_LOCAL_REF (group1);
595 ANDROID_DELETE_LOCAL_REF (identifier1);
596
597 /* Display the notification. */
598 (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
599 notification,
600 notification_class.class,
601 notification_class.display);
602 android_exception_check_1 (notification);
603 ANDROID_DELETE_LOCAL_REF (notification);
604
605 /* Return the ID. */
606 return id;
607}
608
609DEFUN ("android-notifications-notify", Fandroid_notifications_notify,
610 Sandroid_notifications_notify, 0, MANY, 0, doc:
611 /* Display a desktop notification.
612ARGS must contain keywords followed by values. Each of the following
613keywords is understood:
614
615 :title The notification title.
616 :body The notification body.
617 :replaces-id The ID of a previous notification to supersede.
618 :group The notification group, or nil.
619 :urgency One of the symbols `low', `normal' or `critical',
620 defining the importance of the notification group.
621
622The notification group and urgency are ignored on Android 7.1 and
623earlier versions of Android. Outside such older systems, it
624identifies a category that will be displayed in the system Settings
625menu. The urgency provided always extends to affect all notifications
626displayed within that category. If the group is not provided, it
627defaults to the string "Desktop Notifications".
628
629When the system is running Android 13 or later, notifications sent
630will be silently disregarded unless permission to display
631notifications is expressly granted from the "App Info" settings panel
632corresponding to Emacs.
633
634A title and body must be supplied. Value is an integer (fixnum or
635bignum) uniquely designating the notification displayed, which may
636subsequently be specified as the `:replaces-id' of another call to
637this function.
638
639usage: (android-notifications-notify &rest ARGS) */)
640 (ptrdiff_t nargs, Lisp_Object *args)
641{
642 Lisp_Object title, body, replaces_id, group, urgency;
643 Lisp_Object key, value;
644 ptrdiff_t i;
645
646 if (!android_init_gui)
647 error ("No Android display connection!");
648
649 /* Clear each variable above. */
650 title = body = replaces_id = group = urgency = Qnil;
651
652 /* If NARGS is odd, error. */
653
654 if (nargs & 1)
655 error ("Odd number of arguments in call to `android-notifications-notify'");
656
657 /* Next, iterate through ARGS, searching for arguments. */
658
659 for (i = 0; i < nargs; i += 2)
660 {
661 key = args[i];
662 value = args[i + 1];
663
664 if (EQ (key, QCtitle))
665 title = value;
666 else if (EQ (key, QCbody))
667 body = value;
668 else if (EQ (key, QCreplaces_id))
669 replaces_id = value;
670 else if (EQ (key, QCgroup))
671 group = value;
672 else if (EQ (key, QCurgency))
673 urgency = value;
674 }
675
676 /* Demand at least TITLE and BODY be present. */
677
678 if (NILP (title) || NILP (body))
679 error ("Title or body not provided");
680
681 /* Now check the type and possibly expand each non-nil argument. */
682
683 CHECK_STRING (title);
684 CHECK_STRING (body);
685
686 if (NILP (urgency))
687 urgency = Qlow;
688
689 if (NILP (group))
690 group = build_string ("Desktop Notifications");
691
692 return make_int (android_notifications_notify_1 (title, body, replaces_id,
693 group, urgency));
694}
695
696
697
469void 698void
470init_androidselect (void) 699init_androidselect (void)
471{ 700{
@@ -476,6 +705,7 @@ init_androidselect (void)
476 return; 705 return;
477 706
478 android_init_emacs_clipboard (); 707 android_init_emacs_clipboard ();
708 android_init_emacs_desktop_notification ();
479 709
480 make_clipboard = clipboard_class.make_clipboard; 710 make_clipboard = clipboard_class.make_clipboard;
481 tem 711 tem
@@ -496,6 +726,16 @@ init_androidselect (void)
496void 726void
497syms_of_androidselect (void) 727syms_of_androidselect (void)
498{ 728{
729 DEFSYM (QCtitle, ":title");
730 DEFSYM (QCbody, ":body");
731 DEFSYM (QCreplaces_id, ":replaces-id");
732 DEFSYM (QCgroup, ":group");
733 DEFSYM (QCurgency, ":urgency");
734
735 DEFSYM (Qlow, "low");
736 DEFSYM (Qnormal, "normal");
737 DEFSYM (Qcritical, "critical");
738
499 defsubr (&Sandroid_clipboard_owner_p); 739 defsubr (&Sandroid_clipboard_owner_p);
500 defsubr (&Sandroid_set_clipboard); 740 defsubr (&Sandroid_set_clipboard);
501 defsubr (&Sandroid_get_clipboard); 741 defsubr (&Sandroid_get_clipboard);
@@ -503,4 +743,6 @@ syms_of_androidselect (void)
503 defsubr (&Sandroid_browse_url); 743 defsubr (&Sandroid_browse_url);
504 defsubr (&Sandroid_get_clipboard_targets); 744 defsubr (&Sandroid_get_clipboard_targets);
505 defsubr (&Sandroid_get_clipboard_data); 745 defsubr (&Sandroid_get_clipboard_data);
746
747 defsubr (&Sandroid_notifications_notify);
506} 748}