root/src/androidselect.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. android_init_emacs_clipboard
  2. DEFUN
  3. DEFUN
  4. DEFUN
  5. DEFUN
  6. DEFUN
  7. android_xfree_inside
  8. DEFUN
  9. android_init_emacs_desktop_notification
  10. android_locate_icon
  11. android_notifications_notify_1
  12. init_androidselect
  13. syms_of_androidselect

     1 /* Communication module for Android terminals.
     2 
     3 Copyright (C) 2023 Free Software Foundation, Inc.
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 #include <config.h>
    21 #include <assert.h>
    22 #include <minmax.h>
    23 #include <unistd.h>
    24 
    25 #include <boot-time.h>
    26 #include <sys/types.h>
    27 
    28 #include "lisp.h"
    29 #include "blockinput.h"
    30 #include "coding.h"
    31 #include "android.h"
    32 #include "androidterm.h"
    33 
    34 /* 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    selection.
    37 
    38    While newer versions of Android are supposed to have the necessary
    39    interfaces for transferring other kinds of selection data, doing so
    40    is too complicated, and involves registering ``content providers''
    41    and all kinds of other stuff; for this reason, Emacs does not
    42    support setting the clipboard contents to anything other than plain
    43    text.  */
    44 
    45 
    46 
    47 /* Structure describing the EmacsClipboard class.  */
    48 
    49 struct android_emacs_clipboard
    50 {
    51   jclass class;
    52   jmethodID set_clipboard;
    53   jmethodID owns_clipboard;
    54   jmethodID clipboard_exists;
    55   jmethodID get_clipboard;
    56   jmethodID make_clipboard;
    57   jmethodID get_clipboard_targets;
    58   jmethodID get_clipboard_data;
    59 };
    60 
    61 /* Methods associated with the EmacsClipboard class.  */
    62 static struct android_emacs_clipboard clipboard_class;
    63 
    64 /* Reference to the EmacsClipboard object.  */
    65 static jobject clipboard;
    66 
    67 
    68 
    69 static void
    70 android_init_emacs_clipboard (void)
    71 {
    72   jclass old;
    73 
    74   clipboard_class.class
    75     = (*android_java_env)->FindClass (android_java_env,
    76                                       "org/gnu/emacs/EmacsClipboard");
    77   eassert (clipboard_class.class);
    78 
    79   old = clipboard_class.class;
    80   clipboard_class.class
    81     = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
    82                                                   old);
    83   ANDROID_DELETE_LOCAL_REF (old);
    84 
    85   if (!clipboard_class.class)
    86     emacs_abort ();
    87 
    88 #define FIND_METHOD(c_name, name, signature)                    \
    89   clipboard_class.c_name                                        \
    90     = (*android_java_env)->GetMethodID (android_java_env,       \
    91                                         clipboard_class.class,  \
    92                                         name, signature);       \
    93   assert (clipboard_class.c_name);
    94 
    95   FIND_METHOD (set_clipboard, "setClipboard", "([B)V");
    96   FIND_METHOD (owns_clipboard, "ownsClipboard", "()I");
    97   FIND_METHOD (clipboard_exists, "clipboardExists", "()Z");
    98   FIND_METHOD (get_clipboard, "getClipboard", "()[B");
    99   FIND_METHOD (get_clipboard_targets, "getClipboardTargets",
   100                "()[[B");
   101   FIND_METHOD (get_clipboard_data, "getClipboardData",
   102                "([B)[J");
   103 
   104   clipboard_class.make_clipboard
   105     = (*android_java_env)->GetStaticMethodID (android_java_env,
   106                                               clipboard_class.class,
   107                                               "makeClipboard",
   108                                               "()Lorg/gnu/emacs/"
   109                                               "EmacsClipboard;");
   110   assert (clipboard_class.make_clipboard);
   111 
   112 #undef FIND_METHOD
   113 }
   114 
   115 
   116 
   117 
   118 DEFUN ("android-clipboard-owner-p", Fandroid_clipboard_owner_p,
   119        Sandroid_clipboard_owner_p, 0, 0, 0,
   120        doc: /* Return whether or not Emacs owns the clipboard.
   121 Alternatively, return the symbol `lambda' if that could not be
   122 determined.  */)
   123   (void)
   124 {
   125   jint rc;
   126 
   127   if (!android_init_gui)
   128     error ("Accessing clipboard without display connection");
   129 
   130   block_input ();
   131   rc = (*android_java_env)->CallIntMethod (android_java_env,
   132                                            clipboard,
   133                                            clipboard_class.owns_clipboard);
   134   android_exception_check ();
   135   unblock_input ();
   136 
   137   /* If rc is 0 or 1, then Emacs knows whether or not it owns the
   138      clipboard.  If rc is -1, then Emacs does not.  */
   139 
   140   if (rc < 0)
   141     return Qlambda;
   142 
   143   return rc ? Qt : Qnil;
   144 }
   145 
   146 DEFUN ("android-set-clipboard", Fandroid_set_clipboard,
   147        Sandroid_set_clipboard, 1, 1, 0,
   148        doc: /* Set the clipboard text to STRING.  */)
   149   (Lisp_Object string)
   150 {
   151   jarray bytes;
   152 
   153   if (!android_init_gui)
   154     error ("Accessing clipboard without display connection");
   155 
   156   CHECK_STRING (string);
   157   string = ENCODE_UTF_8 (string);
   158 
   159   bytes = (*android_java_env)->NewByteArray (android_java_env,
   160                                              SBYTES (string));
   161   android_exception_check ();
   162 
   163   (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
   164                                            0, SBYTES (string),
   165                                            (jbyte *) SDATA (string));
   166   (*android_java_env)->CallVoidMethod (android_java_env,
   167                                        clipboard,
   168                                        clipboard_class.set_clipboard,
   169                                        bytes);
   170   android_exception_check_1 (bytes);
   171 
   172   ANDROID_DELETE_LOCAL_REF (bytes);
   173   return Qnil;
   174 }
   175 
   176 DEFUN ("android-get-clipboard", Fandroid_get_clipboard,
   177        Sandroid_get_clipboard, 0, 0, 0,
   178        doc: /* Return the current contents of the clipboard.
   179 Value is a multibyte string containing decoded clipboard
   180 text.
   181 Alternatively, return nil if the clipboard is empty.  */)
   182   (void)
   183 {
   184   Lisp_Object string;
   185   jarray bytes;
   186   jmethodID method;
   187   size_t length;
   188   jbyte *data;
   189 
   190   if (!android_init_gui)
   191     error ("No Android display connection!");
   192 
   193   method = clipboard_class.get_clipboard;
   194   bytes
   195     = (*android_java_env)->CallObjectMethod (android_java_env,
   196                                              clipboard,
   197                                              method);
   198   android_exception_check ();
   199 
   200   if (!bytes)
   201     return Qnil;
   202 
   203   length = (*android_java_env)->GetArrayLength (android_java_env,
   204                                                 bytes);
   205   data = (*android_java_env)->GetByteArrayElements (android_java_env,
   206                                                     bytes, NULL);
   207   android_exception_check_nonnull (data, bytes);
   208 
   209   string = make_unibyte_string ((char *) data, length);
   210 
   211   (*android_java_env)->ReleaseByteArrayElements (android_java_env,
   212                                                  bytes, data,
   213                                                  JNI_ABORT);
   214   ANDROID_DELETE_LOCAL_REF (bytes);
   215 
   216   /* Now decode the resulting string.  */
   217   return code_convert_string_norecord (string, Qutf_8, false);
   218 }
   219 
   220 DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p,
   221        Sandroid_clipboard_exists_p, 0, 0, 0,
   222        doc: /* Return whether or not clipboard contents exist.  */)
   223   (void)
   224 {
   225   jboolean rc;
   226   jmethodID method;
   227 
   228   if (!android_init_gui)
   229     error ("No Android display connection");
   230 
   231   method = clipboard_class.clipboard_exists;
   232   rc = (*android_java_env)->CallBooleanMethod (android_java_env,
   233                                                clipboard,
   234                                                method);
   235   android_exception_check ();
   236 
   237   return rc ? Qt : Qnil;
   238 }
   239 
   240 DEFUN ("android-browse-url", Fandroid_browse_url,
   241        Sandroid_browse_url, 1, 2, 0,
   242        doc: /* Open URL in an external application.  URL should be a
   243 URL-encoded URL with a scheme specified unless SEND is non-nil.
   244 Signal an error upon failure.
   245 
   246 If SEND is nil, start a program that is able to display the URL, such
   247 as a web browser.  Otherwise, try to share URL using programs such as
   248 email clients.  */)
   249   (Lisp_Object url, Lisp_Object send)
   250 {
   251   Lisp_Object value;
   252 
   253   if (!android_init_gui)
   254     error ("No Android display connection!");
   255 
   256   CHECK_STRING (url);
   257   value = android_browse_url (url, send);
   258 
   259   /* Signal an error upon failure.  */
   260   if (!NILP (value))
   261     signal_error ("Error browsing URL", value);
   262 
   263   return Qnil;
   264 }
   265 
   266 
   267 
   268 /* MIME clipboard support.  This provides support for reading MIME
   269    data (but not text) from the clipboard.  */
   270 
   271 DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets,
   272        Sandroid_get_clipboard_targets, 0, 0, 0,
   273        doc: /* Return a list of data types in the clipboard.
   274 Value is a list of MIME types as strings, each defining a single extra
   275 data type available from the clipboard.  */)
   276   (void)
   277 {
   278   jarray bytes_array;
   279   jbyteArray bytes;
   280   jmethodID method;
   281   size_t length, length1, i;
   282   jbyte *data;
   283   Lisp_Object targets, tem;
   284 
   285   if (!android_init_gui)
   286     error ("No Android display connection!");
   287 
   288   targets = Qnil;
   289   block_input ();
   290   method = clipboard_class.get_clipboard_targets;
   291   bytes_array = (*android_java_env)->CallObjectMethod (android_java_env,
   292                                                        clipboard, method);
   293   android_exception_check ();
   294 
   295   if (!bytes_array)
   296     goto fail;
   297 
   298   length = (*android_java_env)->GetArrayLength (android_java_env,
   299                                                 bytes_array);
   300   for (i = 0; i < length; ++i)
   301     {
   302       /* Retireve the MIME type.  */
   303       bytes
   304         = (*android_java_env)->GetObjectArrayElement (android_java_env,
   305                                                       bytes_array, i);
   306       android_exception_check_nonnull (bytes, bytes_array);
   307 
   308       /* Cons it onto the list of targets.  */
   309       length1 = (*android_java_env)->GetArrayLength (android_java_env,
   310                                                      bytes);
   311       data = (*android_java_env)->GetByteArrayElements (android_java_env,
   312                                                         bytes, NULL);
   313       android_exception_check_nonnull_1 (data, bytes, bytes_array);
   314 
   315       /* Decode the string.  */
   316       tem = make_unibyte_string ((char *) data, length1);
   317       tem = code_convert_string_norecord (tem, Qutf_8, false);
   318       targets = Fcons (tem, targets);
   319 
   320       /* Delete the retrieved data.  */
   321       (*android_java_env)->ReleaseByteArrayElements (android_java_env,
   322                                                      bytes, data,
   323                                                      JNI_ABORT);
   324       ANDROID_DELETE_LOCAL_REF (bytes);
   325     }
   326   unblock_input ();
   327 
   328   ANDROID_DELETE_LOCAL_REF (bytes_array);
   329   return Fnreverse (targets);
   330 
   331  fail:
   332   unblock_input ();
   333   return Qnil;
   334 }
   335 
   336 /* Free the memory inside PTR, a pointer to a char pointer.  */
   337 
   338 static void
   339 android_xfree_inside (void *ptr)
   340 {
   341   xfree (*(char **) ptr);
   342 }
   343 
   344 DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data,
   345        Sandroid_get_clipboard_data, 1, 1, 0,
   346        doc: /* Return the clipboard data of the given MIME TYPE.
   347 Value is a unibyte string containing the entire contents of the
   348 clipboard, after its owner has converted the data to the given
   349 MIME type.  Value is nil if the conversion fails, or if the data
   350 is not present.
   351 
   352 Value is also nil if the clipboard data consists of a single URL which
   353 does not have any corresponding data.  In that case, use
   354 `android-get-clipboard' instead.  */)
   355   (Lisp_Object type)
   356 {
   357   jlongArray array;
   358   jbyteArray bytes;
   359   jmethodID method;
   360   int fd;
   361   ptrdiff_t rc;
   362   jlong offset, length, *longs;
   363   specpdl_ref ref;
   364   char *buffer, *start;
   365 
   366   if (!android_init_gui)
   367     error ("No Android display connection!");
   368 
   369   /* Encode the string as UTF-8.  */
   370   CHECK_STRING (type);
   371   type = ENCODE_UTF_8 (type);
   372 
   373   /* Then give it to the selection code.  */
   374   block_input ();
   375   bytes = (*android_java_env)->NewByteArray (android_java_env,
   376                                              SBYTES (type));
   377   (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
   378                                            0, SBYTES (type),
   379                                            (jbyte *) SDATA (type));
   380   android_exception_check ();
   381 
   382   method = clipboard_class.get_clipboard_data;
   383   array = (*android_java_env)->CallObjectMethod (android_java_env,
   384                                                  clipboard, method,
   385                                                  bytes);
   386   android_exception_check_1 (bytes);
   387   ANDROID_DELETE_LOCAL_REF (bytes);
   388 
   389   if (!array)
   390     goto fail;
   391 
   392   longs = (*android_java_env)->GetLongArrayElements (android_java_env,
   393                                                      array, NULL);
   394   android_exception_check_nonnull (longs, array);
   395 
   396   /* longs[0] is the file descriptor.
   397      longs[1] is an offset to apply to the file.
   398      longs[2] is either -1, or the number of bytes to read from the
   399      file.  */
   400   fd = longs[0];
   401   offset = longs[1];
   402   length = longs[2];
   403 
   404   (*android_java_env)->ReleaseLongArrayElements (android_java_env,
   405                                                  array, longs,
   406                                                  JNI_ABORT);
   407   ANDROID_DELETE_LOCAL_REF (array);
   408   unblock_input ();
   409 
   410   /* Now begin reading from longs[0].  */
   411   ref = SPECPDL_INDEX ();
   412   record_unwind_protect_int (close_file_unwind, fd);
   413 
   414   if (length != -1)
   415     {
   416       buffer = xmalloc (MIN (length, PTRDIFF_MAX));
   417       record_unwind_protect_ptr (xfree, buffer);
   418 
   419       rc = emacs_read_quit (fd, buffer,
   420                             MIN (length, PTRDIFF_MAX));
   421 
   422       /* Return nil upon an IO problem.  */
   423       if (rc < 0)
   424         return unbind_to (ref, Qnil);
   425 
   426       /* Return the data as a unibyte string.  */
   427       return unbind_to (ref, make_unibyte_string (buffer, rc));
   428     }
   429 
   430   /* Otherwise, read BUFSIZ bytes at a time.  */
   431   buffer = xmalloc (BUFSIZ);
   432   length = 0;
   433   start = buffer;
   434 
   435   record_unwind_protect_ptr (android_xfree_inside, &buffer);
   436 
   437   /* Seek to the start of the data.  */
   438 
   439   if (offset)
   440     {
   441       if (lseek (fd, offset, SEEK_SET) < 0)
   442         return unbind_to (ref, Qnil);
   443     }
   444 
   445   while (true)
   446     {
   447       rc = emacs_read_quit (fd, start, BUFSIZ);
   448 
   449       if (!INT_ADD_OK (rc, length, &length)
   450           || PTRDIFF_MAX - length < BUFSIZ)
   451         memory_full (PTRDIFF_MAX);
   452 
   453       if (rc < 0)
   454         return unbind_to (ref, Qnil);
   455 
   456       if (rc < BUFSIZ)
   457         break;
   458 
   459       buffer = xrealloc (buffer, length + BUFSIZ);
   460       start = buffer + length;
   461     }
   462 
   463   return unbind_to (ref, make_unibyte_string (buffer, rc));
   464 
   465  fail:
   466   unblock_input ();
   467   return Qnil;
   468 }
   469 
   470 
   471 
   472 /* Desktop notifications.  `android-desktop-notify' implements a
   473    facsimile of `notifications-notify'.  */
   474 
   475 /* Structure describing the EmacsDesktopNotification class.  */
   476 
   477 struct android_emacs_desktop_notification
   478 {
   479   jclass class;
   480   jmethodID init;
   481   jmethodID display;
   482 };
   483 
   484 /* Methods provided by the EmacsDesktopNotification class.  */
   485 static struct android_emacs_desktop_notification notification_class;
   486 
   487 /* Initialize virtual function IDs and class pointers tied to the
   488    EmacsDesktopNotification class.  */
   489 
   490 static void
   491 android_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;II)V");
   519   FIND_METHOD (display, "display", "()V");
   520 #undef FIND_METHOD
   521 }
   522 
   523 /* Return the numeric resource ID designating the icon within the
   524    ``android.R.drawable'' package by the supplied NAME.
   525 
   526    If no icon is found, return that of
   527    ``android.R.drawable.ic_dialog_alert''.  */
   528 
   529 static jint
   530 android_locate_icon (const char *name)
   531 {
   532   jclass drawable;
   533   jfieldID field;
   534   jint rc;
   535 
   536   if (android_verify_jni_string (name))
   537     /* If NAME isn't valid, return the default value.  */
   538     return 17301543; /* android.R.drawable.ic_dialog_alert.  */
   539 
   540   drawable = (*android_java_env)->FindClass (android_java_env,
   541                                              "android/R$drawable");
   542   android_exception_check ();
   543 
   544   field = (*android_java_env)->GetStaticFieldID (android_java_env,
   545                                                  drawable, name, "I");
   546   (*android_java_env)->ExceptionClear (android_java_env);
   547 
   548   if (!field)
   549     rc = 17301543; /* android.R.drawable.ic_dialog_alert.  */
   550   else
   551     rc = (*android_java_env)->GetStaticIntField (android_java_env,
   552                                                  drawable, field);
   553 
   554   ANDROID_DELETE_LOCAL_REF (drawable);
   555   return rc;
   556 }
   557 
   558 /* Display a desktop notification with the provided TITLE, BODY,
   559    REPLACES_ID, GROUP, ICON, and URGENCY.  Return an identifier for
   560    the resulting notification.  */
   561 
   562 static intmax_t
   563 android_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
   564                                 Lisp_Object replaces_id,
   565                                 Lisp_Object group, Lisp_Object icon,
   566                                 Lisp_Object urgency)
   567 {
   568   static intmax_t counter;
   569   intmax_t id;
   570   jstring title1, body1, group1, identifier1;
   571   jint type, icon1;
   572   jobject notification;
   573   char identifier[INT_STRLEN_BOUND (int)
   574                   + INT_STRLEN_BOUND (long int)
   575                   + INT_STRLEN_BOUND (intmax_t)
   576                   + sizeof "..."];
   577   struct timespec boot_time;
   578 
   579   if (EQ (urgency, Qlow))
   580     type = 2; /* IMPORTANCE_LOW */
   581   else if (EQ (urgency, Qnormal))
   582     type = 3; /* IMPORTANCE_DEFAULT */
   583   else if (EQ (urgency, Qcritical))
   584     type = 4; /* IMPORTANCE_HIGH */
   585   else
   586     signal_error ("Invalid notification importance given", urgency);
   587 
   588   if (NILP (replaces_id))
   589     {
   590       /* Generate a new identifier.  */
   591       INT_ADD_WRAPV (counter, 1, &counter);
   592       id = counter;
   593     }
   594   else
   595     {
   596       CHECK_INTEGER (replaces_id);
   597       if (!integer_to_intmax (replaces_id, &id))
   598         id = -1; /* Overflow.  */
   599     }
   600 
   601   /* Locate the integer ID linked to ICON.  */
   602   icon1 = android_locate_icon (SSDATA (icon));
   603 
   604   /* Generate a unique identifier for this notification.  Because
   605      Android persists notifications past system shutdown, also include
   606      the boot time within IDENTIFIER.  Scale it down to avoid being
   607      perturbed by minor instabilities in the returned boot time,
   608      however.  */
   609 
   610   boot_time.tv_sec = 0;
   611   get_boot_time (&boot_time);
   612   sprintf (identifier, "%d.%ld.%jd", (int) getpid (),
   613            (long int) (boot_time.tv_sec / 2), id);
   614 
   615   /* Encode all strings into their Java counterparts.  */
   616   title1 = android_build_string (title);
   617   body1  = android_build_string (body);
   618   group1 = android_build_string (group);
   619   identifier1 = android_build_jstring (identifier);
   620 
   621   /* Create the notification.  */
   622   notification
   623     = (*android_java_env)->NewObject (android_java_env,
   624                                       notification_class.class,
   625                                       notification_class.init,
   626                                       title1, body1, group1,
   627                                       identifier1, icon1, type);
   628   android_exception_check_4 (title1, body1, group1, identifier1);
   629 
   630   /* Delete unused local references.  */
   631   ANDROID_DELETE_LOCAL_REF (title1);
   632   ANDROID_DELETE_LOCAL_REF (body1);
   633   ANDROID_DELETE_LOCAL_REF (group1);
   634   ANDROID_DELETE_LOCAL_REF (identifier1);
   635 
   636   /* Display the notification.  */
   637   (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
   638                                                  notification,
   639                                                  notification_class.class,
   640                                                  notification_class.display);
   641   android_exception_check_1 (notification);
   642   ANDROID_DELETE_LOCAL_REF (notification);
   643 
   644   /* Return the ID.  */
   645   return id;
   646 }
   647 
   648 DEFUN ("android-notifications-notify", Fandroid_notifications_notify,
   649        Sandroid_notifications_notify, 0, MANY, 0, doc:
   650        /* Display a desktop notification.
   651 ARGS must contain keywords followed by values.  Each of the following
   652 keywords is understood:
   653 
   654   :title        The notification title.
   655   :body         The notification body.
   656   :replaces-id  The ID of a previous notification to supersede.
   657   :group        The notification group, or nil.
   658   :urgency      One of the symbols `low', `normal' or `critical',
   659                 defining the importance of the notification group.
   660   :icon         The name of a drawable resource to display as the
   661                 notification's icon.
   662 
   663 The notification group and urgency are ignored on Android 7.1 and
   664 earlier versions of Android.  Outside such older systems, it
   665 identifies a category that will be displayed in the system Settings
   666 menu.  The urgency provided always extends to affect all notifications
   667 displayed within that category.  If the group is not provided, it
   668 defaults to the string "Desktop Notifications".
   669 
   670 The provided icon should be the name of a "drawable resource" present
   671 within the "android.R.drawable" class designating an icon with a
   672 transparent background.  If no icon is provided (or the icon is absent
   673 from this system), it defaults to "ic_dialog_alert".
   674 
   675 When the system is running Android 13 or later, notifications sent
   676 will be silently disregarded unless permission to display
   677 notifications is expressly granted from the "App Info" settings panel
   678 corresponding to Emacs.
   679 
   680 A title and body must be supplied.  Value is an integer (fixnum or
   681 bignum) uniquely designating the notification displayed, which may
   682 subsequently be specified as the `:replaces-id' of another call to
   683 this function.
   684 
   685 usage: (android-notifications-notify &rest ARGS) */)
   686   (ptrdiff_t nargs, Lisp_Object *args)
   687 {
   688   Lisp_Object title, body, replaces_id, group, urgency;
   689   Lisp_Object icon;
   690   Lisp_Object key, value;
   691   ptrdiff_t i;
   692 
   693   if (!android_init_gui)
   694     error ("No Android display connection!");
   695 
   696   /* Clear each variable above.  */
   697   title = body = replaces_id = group = icon = urgency = Qnil;
   698 
   699   /* If NARGS is odd, error.  */
   700 
   701   if (nargs & 1)
   702     error ("Odd number of arguments in call to `android-notifications-notify'");
   703 
   704   /* Next, iterate through ARGS, searching for arguments.  */
   705 
   706   for (i = 0; i < nargs; i += 2)
   707     {
   708       key = args[i];
   709       value = args[i + 1];
   710 
   711       if (EQ (key, QCtitle))
   712         title = value;
   713       else if (EQ (key, QCbody))
   714         body = value;
   715       else if (EQ (key, QCreplaces_id))
   716         replaces_id = value;
   717       else if (EQ (key, QCgroup))
   718         group = value;
   719       else if (EQ (key, QCurgency))
   720         urgency = value;
   721       else if (EQ (key, QCicon))
   722         icon = value;
   723     }
   724 
   725   /* Demand at least TITLE and BODY be present.  */
   726 
   727   if (NILP (title) || NILP (body))
   728     error ("Title or body not provided");
   729 
   730   /* Now check the type and possibly expand each non-nil argument.  */
   731 
   732   CHECK_STRING (title);
   733   CHECK_STRING (body);
   734 
   735   if (NILP (urgency))
   736     urgency = Qlow;
   737 
   738   if (NILP (group))
   739     group = build_string ("Desktop Notifications");
   740 
   741   if (NILP (icon))
   742     icon = build_string ("ic_dialog_alert");
   743   else
   744     CHECK_STRING (icon);
   745 
   746   return make_int (android_notifications_notify_1 (title, body, replaces_id,
   747                                                    group, icon, urgency));
   748 }
   749 
   750 
   751 
   752 void
   753 init_androidselect (void)
   754 {
   755   jobject tem;
   756   jmethodID make_clipboard;
   757 
   758   if (!android_init_gui)
   759     return;
   760 
   761   android_init_emacs_clipboard ();
   762   android_init_emacs_desktop_notification ();
   763 
   764   make_clipboard = clipboard_class.make_clipboard;
   765   tem
   766     = (*android_java_env)->CallStaticObjectMethod (android_java_env,
   767                                                    clipboard_class.class,
   768                                                    make_clipboard);
   769   if (!tem)
   770     emacs_abort ();
   771 
   772   clipboard = (*android_java_env)->NewGlobalRef (android_java_env, tem);
   773 
   774   if (!clipboard)
   775     emacs_abort ();
   776 
   777   ANDROID_DELETE_LOCAL_REF (tem);
   778 }
   779 
   780 void
   781 syms_of_androidselect (void)
   782 {
   783   DEFSYM (QCtitle, ":title");
   784   DEFSYM (QCbody, ":body");
   785   DEFSYM (QCreplaces_id, ":replaces-id");
   786   DEFSYM (QCgroup, ":group");
   787   DEFSYM (QCurgency, ":urgency");
   788   DEFSYM (QCicon, ":icon");
   789 
   790   DEFSYM (Qlow, "low");
   791   DEFSYM (Qnormal, "normal");
   792   DEFSYM (Qcritical, "critical");
   793 
   794   defsubr (&Sandroid_clipboard_owner_p);
   795   defsubr (&Sandroid_set_clipboard);
   796   defsubr (&Sandroid_get_clipboard);
   797   defsubr (&Sandroid_clipboard_exists_p);
   798   defsubr (&Sandroid_browse_url);
   799   defsubr (&Sandroid_get_clipboard_targets);
   800   defsubr (&Sandroid_get_clipboard_data);
   801 
   802   defsubr (&Sandroid_notifications_notify);
   803 }

/* [<][>][^][v][top][bottom][index][help] */