aboutsummaryrefslogtreecommitdiffstats
path: root/src/androidselect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/androidselect.c')
-rw-r--r--src/androidselect.c210
1 files changed, 193 insertions, 17 deletions
diff --git a/src/androidselect.c b/src/androidselect.c
index 61f1c6045db..04f4cf1573f 100644
--- a/src/androidselect.c
+++ b/src/androidselect.c
@@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
30#include "coding.h" 30#include "coding.h"
31#include "android.h" 31#include "android.h"
32#include "androidterm.h" 32#include "androidterm.h"
33#include "termhooks.h"
33 34
34/* Selection support on Android is confined to copying and pasting of 35/* Selection support on Android is confined to copying and pasting of
35 plain text and MIME data from the clipboard. There is no primary 36 plain text and MIME data from the clipboard. There is no primary
@@ -490,6 +491,9 @@ struct android_emacs_desktop_notification
490/* Methods provided by the EmacsDesktopNotification class. */ 491/* Methods provided by the EmacsDesktopNotification class. */
491static struct android_emacs_desktop_notification notification_class; 492static struct android_emacs_desktop_notification notification_class;
492 493
494/* Hash table pairing notification identifiers with callbacks. */
495static Lisp_Object notification_table;
496
493/* Initialize virtual function IDs and class pointers tied to the 497/* Initialize virtual function IDs and class pointers tied to the
494 EmacsDesktopNotification class. */ 498 EmacsDesktopNotification class. */
495 499
@@ -521,7 +525,8 @@ android_init_emacs_desktop_notification (void)
521 525
522 FIND_METHOD (init, "<init>", "(Ljava/lang/String;" 526 FIND_METHOD (init, "<init>", "(Ljava/lang/String;"
523 "Ljava/lang/String;Ljava/lang/String;" 527 "Ljava/lang/String;Ljava/lang/String;"
524 "Ljava/lang/String;II)V"); 528 "Ljava/lang/String;II[Ljava/lang/String;"
529 "[Ljava/lang/String;)V");
525 FIND_METHOD (display, "display", "()V"); 530 FIND_METHOD (display, "display", "()V");
526#undef FIND_METHOD 531#undef FIND_METHOD
527} 532}
@@ -562,25 +567,32 @@ android_locate_icon (const char *name)
562} 567}
563 568
564/* Display a desktop notification with the provided TITLE, BODY, 569/* Display a desktop notification with the provided TITLE, BODY,
565 REPLACES_ID, GROUP, ICON, and URGENCY. Return an identifier for 570 REPLACES_ID, GROUP, ICON, URGENCY, ACTIONS, ACTION_CB and CANCEL_CB.
566 the resulting notification. */ 571 Return an identifier for the resulting notification. */
567 572
568static intmax_t 573static intmax_t
569android_notifications_notify_1 (Lisp_Object title, Lisp_Object body, 574android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
570 Lisp_Object replaces_id, 575 Lisp_Object replaces_id,
571 Lisp_Object group, Lisp_Object icon, 576 Lisp_Object group, Lisp_Object icon,
572 Lisp_Object urgency) 577 Lisp_Object urgency, Lisp_Object actions,
578 Lisp_Object action_cb,
579 Lisp_Object cancel_cb)
573{ 580{
574 static intmax_t counter; 581 static intmax_t counter;
575 intmax_t id; 582 intmax_t id;
576 jstring title1, body1, group1, identifier1; 583 jstring title1, body1, group1, identifier1;
577 jint type, icon1; 584 jint type, icon1;
578 jobject notification; 585 jobject notification;
586 jobjectArray action_keys, action_titles;
579 char identifier[INT_STRLEN_BOUND (int) 587 char identifier[INT_STRLEN_BOUND (int)
580 + INT_STRLEN_BOUND (long int) 588 + INT_STRLEN_BOUND (long int)
581 + INT_STRLEN_BOUND (intmax_t) 589 + INT_STRLEN_BOUND (intmax_t)
582 + sizeof "..."]; 590 + sizeof "..."];
583 struct timespec boot_time; 591 struct timespec boot_time;
592 Lisp_Object key, value, tem;
593 jint nitems, i;
594 jstring item;
595 Lisp_Object length;
584 596
585 if (EQ (urgency, Qlow)) 597 if (EQ (urgency, Qlow))
586 type = 2; /* IMPORTANCE_LOW */ 598 type = 2; /* IMPORTANCE_LOW */
@@ -591,6 +603,29 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
591 else 603 else
592 signal_error ("Invalid notification importance given", urgency); 604 signal_error ("Invalid notification importance given", urgency);
593 605
606 nitems = 0;
607
608 /* If ACTIONS is provided, split it into two arrays of Java strings
609 holding keys and titles. */
610
611 if (!NILP (actions))
612 {
613 /* Count the number of items to be inserted. */
614
615 length = Flength (actions);
616 if (!TYPE_RANGED_FIXNUMP (jint, length))
617 error ("Action list too long");
618 nitems = XFIXNAT (length);
619 if (nitems & 1)
620 error ("Length of action list is invalid");
621 nitems /= 2;
622
623 /* Verify that the list consists exclusively of strings. */
624 tem = actions;
625 FOR_EACH_TAIL (tem)
626 CHECK_STRING (XCAR (tem));
627 }
628
594 if (NILP (replaces_id)) 629 if (NILP (replaces_id))
595 { 630 {
596 /* Generate a new identifier. */ 631 /* Generate a new identifier. */
@@ -626,14 +661,62 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
626 = (*android_java_env)->NewStringUTF (android_java_env, identifier); 661 = (*android_java_env)->NewStringUTF (android_java_env, identifier);
627 android_exception_check_3 (title1, body1, group1); 662 android_exception_check_3 (title1, body1, group1);
628 663
664 /* Create the arrays for action identifiers and titles if
665 provided. */
666
667 if (nitems)
668 {
669 action_keys = (*android_java_env)->NewObjectArray (android_java_env,
670 nitems,
671 java_string_class,
672 NULL);
673 android_exception_check_4 (title, body1, group1, identifier1);
674 action_titles = (*android_java_env)->NewObjectArray (android_java_env,
675 nitems,
676 java_string_class,
677 NULL);
678 android_exception_check_5 (title, body1, group1, identifier1,
679 action_keys);
680
681 for (i = 0; i < nitems; ++i)
682 {
683 key = XCAR (actions);
684 value = XCAR (XCDR (actions));
685 actions = XCDR (XCDR (actions));
686
687 /* Create a string for this action. */
688 item = android_build_string (key, body1, group1, identifier1,
689 action_keys, action_titles, NULL);
690 (*android_java_env)->SetObjectArrayElement (android_java_env,
691 action_keys, i,
692 item);
693 ANDROID_DELETE_LOCAL_REF (item);
694
695 /* Create a string for this title. */
696 item = android_build_string (value, body1, group1, identifier1,
697 action_keys, action_titles, NULL);
698 (*android_java_env)->SetObjectArrayElement (android_java_env,
699 action_titles, i,
700 item);
701 ANDROID_DELETE_LOCAL_REF (item);
702 }
703 }
704 else
705 {
706 action_keys = NULL;
707 action_titles = NULL;
708 }
709
629 /* Create the notification. */ 710 /* Create the notification. */
630 notification 711 notification
631 = (*android_java_env)->NewObject (android_java_env, 712 = (*android_java_env)->NewObject (android_java_env,
632 notification_class.class, 713 notification_class.class,
633 notification_class.init, 714 notification_class.init,
634 title1, body1, group1, 715 title1, body1, group1,
635 identifier1, icon1, type); 716 identifier1, icon1, type,
636 android_exception_check_4 (title1, body1, group1, identifier1); 717 action_keys, action_titles);
718 android_exception_check_6 (title1, body1, group1, identifier1,
719 action_titles, action_keys);
637 720
638 /* Delete unused local references. */ 721 /* Delete unused local references. */
639 ANDROID_DELETE_LOCAL_REF (title1); 722 ANDROID_DELETE_LOCAL_REF (title1);
@@ -641,6 +724,12 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
641 ANDROID_DELETE_LOCAL_REF (group1); 724 ANDROID_DELETE_LOCAL_REF (group1);
642 ANDROID_DELETE_LOCAL_REF (identifier1); 725 ANDROID_DELETE_LOCAL_REF (identifier1);
643 726
727 if (action_keys)
728 ANDROID_DELETE_LOCAL_REF (action_keys);
729
730 if (action_titles)
731 ANDROID_DELETE_LOCAL_REF (action_titles);
732
644 /* Display the notification. */ 733 /* Display the notification. */
645 (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, 734 (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
646 notification, 735 notification,
@@ -649,6 +738,12 @@ android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
649 android_exception_check_1 (notification); 738 android_exception_check_1 (notification);
650 ANDROID_DELETE_LOCAL_REF (notification); 739 ANDROID_DELETE_LOCAL_REF (notification);
651 740
741 /* If callbacks are provided, save them into notification_table. */
742
743 if (!NILP (action_cb) || !NILP (cancel_cb))
744 Fputhash (build_string (identifier), Fcons (action_cb, cancel_cb),
745 notification_table);
746
652 /* Return the ID. */ 747 /* Return the ID. */
653 return id; 748 return id;
654} 749}
@@ -659,14 +754,28 @@ DEFUN ("android-notifications-notify", Fandroid_notifications_notify,
659ARGS must contain keywords followed by values. Each of the following 754ARGS must contain keywords followed by values. Each of the following
660keywords is understood: 755keywords is understood:
661 756
662 :title The notification title. 757 :title The notification title.
663 :body The notification body. 758 :body The notification body.
664 :replaces-id The ID of a previous notification to supersede. 759 :replaces-id The ID of a previous notification to supersede.
665 :group The notification group, or nil. 760 :group The notification group, or nil.
666 :urgency One of the symbols `low', `normal' or `critical', 761 :urgency One of the symbols `low', `normal' or `critical',
667 defining the importance of the notification group. 762 defining the importance of the notification group.
668 :icon The name of a drawable resource to display as the 763 :icon The name of a drawable resource to display as the
669 notification's icon. 764 notification's icon.
765 :actions A list of actions of the form:
766 (KEY TITLE KEY TITLE ...)
767 where KEY and TITLE are both strings.
768 The action for which CALLBACK is called when the
769 notification itself is selected is named "default",
770 its existence is implied, and its TITLE is ignored.
771 No more than three actions can be defined, not
772 counting any action with "default" as its key.
773 :on-action Function to call when an action is invoked.
774 The notification id and the key of the action are
775 provided as arguments to the function.
776 :on-cancel Function to call if the notification is dismissed,
777 with the notification id and the symbol `undefined'
778 for arguments.
670 779
671The notification group is ignored on Android 7.1 and earlier versions 780The notification group is ignored on Android 7.1 and earlier versions
672of Android. Outside such older systems, it identifies a category that 781of Android. Outside such older systems, it identifies a category that
@@ -686,6 +795,9 @@ within the "android.R.drawable" class designating an icon with a
686transparent background. If no icon is provided (or the icon is absent 795transparent background. If no icon is provided (or the icon is absent
687from this system), it defaults to "ic_dialog_alert". 796from this system), it defaults to "ic_dialog_alert".
688 797
798Actions specified with :actions cannot be displayed on Android 4.0 and
799earlier versions of the system.
800
689When the system is running Android 13 or later, notifications sent 801When the system is running Android 13 or later, notifications sent
690will be silently disregarded unless permission to display 802will be silently disregarded unless permission to display
691notifications is expressly granted from the "App Info" settings panel 803notifications is expressly granted from the "App Info" settings panel
@@ -701,14 +813,15 @@ usage: (android-notifications-notify &rest ARGS) */)
701{ 813{
702 Lisp_Object title, body, replaces_id, group, urgency; 814 Lisp_Object title, body, replaces_id, group, urgency;
703 Lisp_Object icon; 815 Lisp_Object icon;
704 Lisp_Object key, value; 816 Lisp_Object key, value, actions, action_cb, cancel_cb;
705 ptrdiff_t i; 817 ptrdiff_t i;
706 818
707 if (!android_init_gui) 819 if (!android_init_gui)
708 error ("No Android display connection!"); 820 error ("No Android display connection!");
709 821
710 /* Clear each variable above. */ 822 /* Clear each variable above. */
711 title = body = replaces_id = group = icon = urgency = Qnil; 823 title = body = replaces_id = group = icon = urgency = actions = Qnil;
824 action_cb = cancel_cb = Qnil;
712 825
713 /* If NARGS is odd, error. */ 826 /* If NARGS is odd, error. */
714 827
@@ -734,6 +847,12 @@ usage: (android-notifications-notify &rest ARGS) */)
734 urgency = value; 847 urgency = value;
735 else if (EQ (key, QCicon)) 848 else if (EQ (key, QCicon))
736 icon = value; 849 icon = value;
850 else if (EQ (key, QCactions))
851 actions = value;
852 else if (EQ (key, QCon_action))
853 action_cb = value;
854 else if (EQ (key, QCon_cancel))
855 cancel_cb = value;
737 } 856 }
738 857
739 /* Demand at least TITLE and BODY be present. */ 858 /* Demand at least TITLE and BODY be present. */
@@ -758,7 +877,58 @@ usage: (android-notifications-notify &rest ARGS) */)
758 CHECK_STRING (icon); 877 CHECK_STRING (icon);
759 878
760 return make_int (android_notifications_notify_1 (title, body, replaces_id, 879 return make_int (android_notifications_notify_1 (title, body, replaces_id,
761 group, icon, urgency)); 880 group, icon, urgency,
881 actions, action_cb,
882 cancel_cb));
883}
884
885/* Run callbacks in response to a notification being deleted.
886 Save any input generated for the keyboard within *IE.
887 EVENT should be the notification deletion event. */
888
889void
890android_notification_deleted (struct android_notification_event *event,
891 struct input_event *ie)
892{
893 Lisp_Object item, tag;
894 intmax_t id;
895
896 tag = build_string (event->tag);
897 item = Fgethash (tag, notification_table, Qnil);
898
899 if (!NILP (item))
900 Fremhash (tag, notification_table);
901
902 if (CONSP (item) && FUNCTIONP (XCDR (item))
903 && sscanf (event->tag, "%*d.%*ld.%jd", &id) > 0)
904 {
905 ie->kind = NOTIFICATION_EVENT;
906 ie->arg = list3 (XCDR (item), make_int (id),
907 Qundefined);
908 }
909}
910
911/* Run callbacks in response to one of a notification's actions being
912 invoked, saving any input generated for the keyboard within *IE.
913 EVENT should be the notification deletion event, and ACTION the
914 action key. */
915
916void
917android_notification_action (struct android_notification_event *event,
918 struct input_event *ie, Lisp_Object action)
919{
920 Lisp_Object item, tag;
921 intmax_t id;
922
923 tag = build_string (event->tag);
924 item = Fgethash (tag, notification_table, Qnil);
925
926 if (CONSP (item) && FUNCTIONP (XCAR (item))
927 && sscanf (event->tag, "%*d.%*ld.%jd", &id) > 0)
928 {
929 ie->kind = NOTIFICATION_EVENT;
930 ie->arg = list3 (XCAR (item), make_int (id), action);
931 }
762} 932}
763 933
764 934
@@ -800,6 +970,9 @@ syms_of_androidselect (void)
800 DEFSYM (QCgroup, ":group"); 970 DEFSYM (QCgroup, ":group");
801 DEFSYM (QCurgency, ":urgency"); 971 DEFSYM (QCurgency, ":urgency");
802 DEFSYM (QCicon, ":icon"); 972 DEFSYM (QCicon, ":icon");
973 DEFSYM (QCactions, ":actions");
974 DEFSYM (QCon_action, ":on-action");
975 DEFSYM (QCon_cancel, ":on-cancel");
803 976
804 DEFSYM (Qlow, "low"); 977 DEFSYM (Qlow, "low");
805 DEFSYM (Qnormal, "normal"); 978 DEFSYM (Qnormal, "normal");
@@ -814,4 +987,7 @@ syms_of_androidselect (void)
814 defsubr (&Sandroid_get_clipboard_data); 987 defsubr (&Sandroid_get_clipboard_data);
815 988
816 defsubr (&Sandroid_notifications_notify); 989 defsubr (&Sandroid_notifications_notify);
990
991 notification_table = CALLN (Fmake_hash_table, QCtest, Qequal);
992 staticpro (&notification_table);
817} 993}