diff options
Diffstat (limited to 'src/androidselect.c')
| -rw-r--r-- | src/androidselect.c | 210 |
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. */ |
| 491 | static struct android_emacs_desktop_notification notification_class; | 492 | static struct android_emacs_desktop_notification notification_class; |
| 492 | 493 | ||
| 494 | /* Hash table pairing notification identifiers with callbacks. */ | ||
| 495 | static 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 | ||
| 568 | static intmax_t | 573 | static intmax_t |
| 569 | android_notifications_notify_1 (Lisp_Object title, Lisp_Object body, | 574 | android_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, | |||
| 659 | ARGS must contain keywords followed by values. Each of the following | 754 | ARGS must contain keywords followed by values. Each of the following |
| 660 | keywords is understood: | 755 | keywords 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 | ||
| 671 | The notification group is ignored on Android 7.1 and earlier versions | 780 | The notification group is ignored on Android 7.1 and earlier versions |
| 672 | of Android. Outside such older systems, it identifies a category that | 781 | of 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 | |||
| 686 | transparent background. If no icon is provided (or the icon is absent | 795 | transparent background. If no icon is provided (or the icon is absent |
| 687 | from this system), it defaults to "ic_dialog_alert". | 796 | from this system), it defaults to "ic_dialog_alert". |
| 688 | 797 | ||
| 798 | Actions specified with :actions cannot be displayed on Android 4.0 and | ||
| 799 | earlier versions of the system. | ||
| 800 | |||
| 689 | When the system is running Android 13 or later, notifications sent | 801 | When the system is running Android 13 or later, notifications sent |
| 690 | will be silently disregarded unless permission to display | 802 | will be silently disregarded unless permission to display |
| 691 | notifications is expressly granted from the "App Info" settings panel | 803 | notifications 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 | |||
| 889 | void | ||
| 890 | android_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 | |||
| 916 | void | ||
| 917 | android_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 (¬ification_table); | ||
| 817 | } | 993 | } |