This source file includes following definitions.
- android_init_emacs_clipboard
- DEFUN
- DEFUN
- DEFUN
- DEFUN
- DEFUN
- android_xfree_inside
- DEFUN
- android_init_emacs_desktop_notification
- android_locate_icon
- android_notifications_notify_1
- init_androidselect
- syms_of_androidselect
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
35
36
37
38
39
40
41
42
43
44
45
46
47
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
62 static struct android_emacs_clipboard clipboard_class;
63
64
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:
121
122 )
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
138
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: )
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:
179
180
181 )
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
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: )
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:
243
244
245
246
247
248 )
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
260 if (!NILP (value))
261 signal_error ("Error browsing URL", value);
262
263 return Qnil;
264 }
265
266
267
268
269
270
271 DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets,
272 Sandroid_get_clipboard_targets, 0, 0, 0,
273 doc:
274
275 )
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
303 bytes
304 = (*android_java_env)->GetObjectArrayElement (android_java_env,
305 bytes_array, i);
306 android_exception_check_nonnull (bytes, bytes_array);
307
308
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
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
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
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:
347
348
349
350
351
352
353
354 )
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
370 CHECK_STRING (type);
371 type = ENCODE_UTF_8 (type);
372
373
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
397
398
399
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
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
423 if (rc < 0)
424 return unbind_to (ref, Qnil);
425
426
427 return unbind_to (ref, make_unibyte_string (buffer, rc));
428 }
429
430
431 buffer = xmalloc (BUFSIZ);
432 length = 0;
433 start = buffer;
434
435 record_unwind_protect_ptr (android_xfree_inside, &buffer);
436
437
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
473
474
475
476
477 struct android_emacs_desktop_notification
478 {
479 jclass class;
480 jmethodID init;
481 jmethodID display;
482 };
483
484
485 static struct android_emacs_desktop_notification notification_class;
486
487
488
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
524
525
526
527
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
538 return 17301543;
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;
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
559
560
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;
581 else if (EQ (urgency, Qnormal))
582 type = 3;
583 else if (EQ (urgency, Qcritical))
584 type = 4;
585 else
586 signal_error ("Invalid notification importance given", urgency);
587
588 if (NILP (replaces_id))
589 {
590
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;
599 }
600
601
602 icon1 = android_locate_icon (SSDATA (icon));
603
604
605
606
607
608
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
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
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
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
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
645 return id;
646 }
647
648 DEFUN ("android-notifications-notify", Fandroid_notifications_notify,
649 Sandroid_notifications_notify, 0, MANY, 0, doc:
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685 )
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
697 title = body = replaces_id = group = icon = urgency = Qnil;
698
699
700
701 if (nargs & 1)
702 error ("Odd number of arguments in call to `android-notifications-notify'");
703
704
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
726
727 if (NILP (title) || NILP (body))
728 error ("Title or body not provided");
729
730
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 }