This source file includes following definitions.
- getLibraryDirectory
- onStartCommand
- onBind
- getApkFile
- onCreate
- runOnUiThread
- getEmacsView
- getLocationOnScreen
- checkEmacsThread
- fillRectangle
- fillPolygon
- drawRectangle
- drawLine
- drawPoint
- clearWindow
- clearArea
- ringBell
- queryTree
- getScreenWidth
- getScreenHeight
- detectMouse
- nameKeysym
- startEmacsService
- browseUrl
- getClipboardManager
- restartEmacs
- syncRunnable
- icBeginSynchronous
- icEndSynchronous
- viewGetSelection
- updateIC
- resetIC
- updateCursorAnchorInfo
- openContentUri
- checkContentUri
- buildContentName
- queryBattery19
- queryBattery
- updateExtractedText
- getDocumentAuthorities
- requestDirectoryAccess
- getDocumentTrees
- documentIdFromName
- getTreeUri
- statDocument
- accessDocument
- openDocumentDirectory
- readDirectoryEntry
- openDocument
- createDocument
- createDirectory
- deleteDocument
- renameDocument
- moveDocument
- validAuthority
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.gnu.emacs;
21
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.UnsupportedEncodingException;
25
26 import java.util.ArrayList;
27 import java.util.HashSet;
28 import java.util.List;
29
30 import java.util.concurrent.atomic.AtomicInteger;
31
32 import android.database.Cursor;
33
34 import android.graphics.Matrix;
35 import android.graphics.Point;
36
37 import android.webkit.MimeTypeMap;
38
39 import android.view.InputDevice;
40 import android.view.KeyEvent;
41 import android.view.inputmethod.CursorAnchorInfo;
42 import android.view.inputmethod.ExtractedText;
43
44 import android.app.Notification;
45 import android.app.NotificationManager;
46 import android.app.NotificationChannel;
47 import android.app.Service;
48
49 import android.content.ClipboardManager;
50 import android.content.Context;
51 import android.content.ContentResolver;
52 import android.content.Intent;
53 import android.content.IntentFilter;
54 import android.content.UriPermission;
55
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.PackageManager.ApplicationInfoFlags;
58 import android.content.pm.PackageManager;
59
60 import android.content.res.AssetManager;
61
62 import android.hardware.input.InputManager;
63
64 import android.net.Uri;
65
66 import android.os.BatteryManager;
67 import android.os.Build;
68 import android.os.Looper;
69 import android.os.IBinder;
70 import android.os.Handler;
71 import android.os.ParcelFileDescriptor;
72 import android.os.Vibrator;
73 import android.os.VibratorManager;
74 import android.os.VibrationEffect;
75
76 import android.provider.DocumentsContract;
77 import android.provider.DocumentsContract.Document;
78
79 import android.util.Log;
80 import android.util.DisplayMetrics;
81
82 import android.widget.Toast;
83
84
85
86
87 public final class EmacsService extends Service
88 {
89 public static final String TAG = "EmacsService";
90
91
92 public static EmacsService SERVICE;
93
94
95
96 public static String extraStartupArgument;
97
98
99 private EmacsThread thread;
100
101
102 private Handler handler;
103
104
105 private ContentResolver resolver;
106
107
108 public static final int IC_MODE_NULL = 0;
109 public static final int IC_MODE_ACTION = 1;
110 public static final int IC_MODE_TEXT = 2;
111
112
113 public DisplayMetrics metrics;
114
115
116
117 public static final boolean DEBUG_IC = false;
118
119
120
121 private static final boolean DEBUG_THREADS = false;
122
123
124
125
126
127
128 public static final AtomicInteger servicingQuery;
129
130
131
132 private EmacsSafThread storageThread;
133
134 static
135 {
136 servicingQuery = new AtomicInteger ();
137 };
138
139
140
141
142 public static String
143 getLibraryDirectory (Context context)
144 {
145 int apiLevel;
146
147 apiLevel = Build.VERSION.SDK_INT;
148
149 if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
150 return context.getApplicationInfo ().nativeLibraryDir;
151
152 return context.getApplicationInfo ().dataDir + "/lib";
153 }
154
155 @Override
156 public int
157 onStartCommand (Intent intent, int flags, int startId)
158 {
159 Notification notification;
160 NotificationManager manager;
161 NotificationChannel channel;
162 String infoBlurb;
163 Object tem;
164
165 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
166 {
167 tem = getSystemService (Context.NOTIFICATION_SERVICE);
168 manager = (NotificationManager) tem;
169 infoBlurb = ("This notification is displayed to keep Emacs"
170 + " running while it is in the background. You"
171 + " may disable it if you want;"
172 + " see (emacs)Android Environment.");
173 channel
174 = new NotificationChannel ("emacs", "Emacs persistent notification",
175 NotificationManager.IMPORTANCE_DEFAULT);
176 manager.createNotificationChannel (channel);
177 notification = (new Notification.Builder (this, "emacs")
178 .setContentTitle ("Emacs")
179 .setContentText (infoBlurb)
180 .setSmallIcon (android.R.drawable.sym_def_app_icon)
181 .build ());
182 manager.notify (1, notification);
183 startForeground (1, notification);
184 }
185
186 return START_NOT_STICKY;
187 }
188
189 @Override
190 public IBinder
191 onBind (Intent intent)
192 {
193 return null;
194 }
195
196 @SuppressWarnings ("deprecation")
197 private String
198 getApkFile ()
199 {
200 PackageManager manager;
201 ApplicationInfo info;
202
203 manager = getPackageManager ();
204
205 try
206 {
207 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
208 info = manager.getApplicationInfo ("org.gnu.emacs", 0);
209 else
210 info = manager.getApplicationInfo ("org.gnu.emacs",
211 ApplicationInfoFlags.of (0));
212
213
214
215 if (info.sourceDir != null)
216 return info.sourceDir;
217
218 return "";
219 }
220 catch (Exception e)
221 {
222 return "";
223 }
224 }
225
226 @Override
227 public void
228 onCreate ()
229 {
230 final AssetManager manager;
231 Context app_context;
232 final String filesDir, libDir, cacheDir, classPath;
233 final double pixelDensityX;
234 final double pixelDensityY;
235 final double scaledDensity;
236 double tempScaledDensity;
237
238 SERVICE = this;
239 handler = new Handler (Looper.getMainLooper ());
240 manager = getAssets ();
241 app_context = getApplicationContext ();
242 metrics = getResources ().getDisplayMetrics ();
243 pixelDensityX = metrics.xdpi;
244 pixelDensityY = metrics.ydpi;
245 tempScaledDensity = ((metrics.scaledDensity
246 / metrics.density)
247 * pixelDensityX);
248 resolver = getContentResolver ();
249
250
251
252
253
254
255
256
257
258 if (tempScaledDensity < 160)
259 tempScaledDensity = 160;
260
261
262
263 scaledDensity = tempScaledDensity;
264
265 try
266 {
267
268
269 filesDir = app_context.getFilesDir ().getCanonicalPath ();
270 libDir = getLibraryDirectory (this);
271 cacheDir = app_context.getCacheDir ().getCanonicalPath ();
272
273
274
275
276 classPath = getApkFile ();
277
278 Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
279 + ", libDir = " + libDir + ", and classPath = " + classPath
280 + "; fileToOpen = " + EmacsOpenActivity.fileToOpen
281 + "; display density: " + pixelDensityX + " by "
282 + pixelDensityY + " scaled to " + scaledDensity);
283
284
285 thread = new EmacsThread (this, new Runnable () {
286 @Override
287 public void
288 run ()
289 {
290 EmacsNative.setEmacsParams (manager, filesDir, libDir,
291 cacheDir, (float) pixelDensityX,
292 (float) pixelDensityY,
293 (float) scaledDensity,
294 classPath, EmacsService.this,
295 Build.VERSION.SDK_INT);
296 }
297 }, extraStartupArgument,
298
299 EmacsOpenActivity.fileToOpen);
300 thread.start ();
301 }
302 catch (IOException exception)
303 {
304 EmacsNative.emacsAbort ();
305 return;
306 }
307 }
308
309
310
311
312
313
314 public void
315 runOnUiThread (Runnable runnable)
316 {
317 handler.post (runnable);
318 }
319
320 public EmacsView
321 getEmacsView (final EmacsWindow window, final int visibility,
322 final boolean isFocusedByDefault)
323 {
324 Runnable runnable;
325 final EmacsHolder<EmacsView> view;
326
327 view = new EmacsHolder<EmacsView> ();
328
329 runnable = new Runnable () {
330 @Override
331 public void
332 run ()
333 {
334 synchronized (this)
335 {
336 view.thing = new EmacsView (window);
337 view.thing.setVisibility (visibility);
338
339
340
341 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
342 view.thing.setFocusedByDefault (isFocusedByDefault);
343
344 notify ();
345 }
346 }
347 };
348
349 syncRunnable (runnable);
350 return view.thing;
351 }
352
353 public void
354 getLocationOnScreen (final EmacsView view, final int[] coordinates)
355 {
356 Runnable runnable;
357
358 runnable = new Runnable () {
359 public void
360 run ()
361 {
362 synchronized (this)
363 {
364 view.getLocationOnScreen (coordinates);
365 notify ();
366 }
367 }
368 };
369
370 syncRunnable (runnable);
371 }
372
373
374
375 public static void
376 checkEmacsThread ()
377 {
378 if (DEBUG_THREADS)
379 {
380 if (Thread.currentThread () instanceof EmacsThread)
381 return;
382
383 throw new RuntimeException ("Emacs thread function"
384 + " called from other thread!");
385 }
386 }
387
388
389
390
391 public void
392 fillRectangle (EmacsDrawable drawable, EmacsGC gc,
393 int x, int y, int width, int height)
394 {
395 checkEmacsThread ();
396 EmacsFillRectangle.perform (drawable, gc, x, y,
397 width, height);
398 }
399
400 public void
401 fillPolygon (EmacsDrawable drawable, EmacsGC gc,
402 Point points[])
403 {
404 checkEmacsThread ();
405 EmacsFillPolygon.perform (drawable, gc, points);
406 }
407
408 public void
409 drawRectangle (EmacsDrawable drawable, EmacsGC gc,
410 int x, int y, int width, int height)
411 {
412 checkEmacsThread ();
413 EmacsDrawRectangle.perform (drawable, gc, x, y,
414 width, height);
415 }
416
417 public void
418 drawLine (EmacsDrawable drawable, EmacsGC gc,
419 int x, int y, int x2, int y2)
420 {
421 checkEmacsThread ();
422 EmacsDrawLine.perform (drawable, gc, x, y,
423 x2, y2);
424 }
425
426 public void
427 drawPoint (EmacsDrawable drawable, EmacsGC gc,
428 int x, int y)
429 {
430 checkEmacsThread ();
431 EmacsDrawPoint.perform (drawable, gc, x, y);
432 }
433
434 public void
435 clearWindow (EmacsWindow window)
436 {
437 checkEmacsThread ();
438 window.clearWindow ();
439 }
440
441 public void
442 clearArea (EmacsWindow window, int x, int y, int width,
443 int height)
444 {
445 checkEmacsThread ();
446 window.clearArea (x, y, width, height);
447 }
448
449 @SuppressWarnings ("deprecation")
450 public void
451 ringBell ()
452 {
453 Vibrator vibrator;
454 VibrationEffect effect;
455 VibratorManager vibratorManager;
456 Object tem;
457
458 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
459 {
460 tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
461 vibratorManager = (VibratorManager) tem;
462 vibrator = vibratorManager.getDefaultVibrator ();
463 }
464 else
465 vibrator
466 = (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
467
468 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
469 {
470 effect
471 = VibrationEffect.createOneShot (50,
472 VibrationEffect.DEFAULT_AMPLITUDE);
473 vibrator.vibrate (effect);
474 }
475 else
476 vibrator.vibrate (50);
477 }
478
479 public short[]
480 queryTree (EmacsWindow window)
481 {
482 short[] array;
483 List<EmacsWindow> windowList;
484 int i;
485
486 if (window == null)
487
488 windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
489 else
490 windowList = window.children;
491
492 array = new short[windowList.size () + 1];
493 i = 1;
494
495 array[0] = (window == null
496 ? 0 : (window.parent != null
497 ? window.parent.handle : 0));
498
499 for (EmacsWindow treeWindow : windowList)
500 array[i++] = treeWindow.handle;
501
502 return array;
503 }
504
505 public int
506 getScreenWidth (boolean mmWise)
507 {
508 DisplayMetrics metrics;
509
510 metrics = getResources ().getDisplayMetrics ();
511
512 if (!mmWise)
513 return metrics.widthPixels;
514 else
515 return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
516 }
517
518 public int
519 getScreenHeight (boolean mmWise)
520 {
521 DisplayMetrics metrics;
522
523 metrics = getResources ().getDisplayMetrics ();
524
525 if (!mmWise)
526 return metrics.heightPixels;
527 else
528 return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
529 }
530
531 public boolean
532 detectMouse ()
533 {
534 InputManager manager;
535 InputDevice device;
536 int[] ids;
537 int i;
538
539 if (Build.VERSION.SDK_INT
540
541
542 < Build.VERSION_CODES.JELLY_BEAN)
543 return false;
544
545 manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
546 ids = manager.getInputDeviceIds ();
547
548 for (i = 0; i < ids.length; ++i)
549 {
550 device = manager.getInputDevice (ids[i]);
551
552 if (device == null)
553 continue;
554
555 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
556 {
557 if (device.supportsSource (InputDevice.SOURCE_MOUSE))
558 return true;
559 }
560 else
561 {
562
563
564
565
566 if ((device.getSources () & InputDevice.SOURCE_MOUSE) != 0)
567 return true;
568 }
569 }
570
571 return false;
572 }
573
574 public String
575 nameKeysym (int keysym)
576 {
577 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
578 return KeyEvent.keyCodeToString (keysym);
579
580 return String.valueOf (keysym);
581 }
582
583
584
585
586
587
588
589
590
591
592 public static void
593 startEmacsService (Context context)
594 {
595 if (EmacsService.SERVICE == null)
596 {
597 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
598
599 context.startService (new Intent (context,
600 EmacsService.class));
601 else
602
603
604 context.startForegroundService (new Intent (context,
605 EmacsService.class));
606 }
607 }
608
609
610
611
612
613
614
615
616
617
618
619 public String
620 browseUrl (String url, boolean send)
621 {
622 Intent intent;
623 Uri uri;
624
625 try
626 {
627
628 if (!send)
629 {
630 uri = Uri.parse (url);
631
632 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
633 {
634
635
636
637
638
639 if (uri.getScheme ().equals ("file")
640 && uri.getPath () != null)
641 uri
642 = DocumentsContract.buildDocumentUri ("org.gnu.emacs",
643 uri.getPath ());
644 }
645
646 Log.d (TAG, ("browseUri: browsing " + url
647 + " --> " + uri.getPath ()
648 + " --> " + uri));
649
650 intent = new Intent (Intent.ACTION_VIEW, uri);
651 intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK
652 | Intent.FLAG_GRANT_READ_URI_PERMISSION);
653 }
654 else
655 {
656 intent = new Intent (Intent.ACTION_SEND);
657 intent.setType ("text/plain");
658 intent.putExtra (Intent.EXTRA_SUBJECT, "Sharing link");
659 intent.putExtra (Intent.EXTRA_TEXT, url);
660
661
662 intent = Intent.createChooser (intent, "Send");
663
664
665
666 intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
667 }
668
669 startActivity (intent);
670 }
671 catch (Exception e)
672 {
673 return e.toString ();
674 }
675
676 return null;
677 }
678
679
680
681
682
683
684 public ClipboardManager
685 getClipboardManager ()
686 {
687 final EmacsHolder<ClipboardManager> manager;
688 Runnable runnable;
689
690 manager = new EmacsHolder<ClipboardManager> ();
691
692 runnable = new Runnable () {
693 public void
694 run ()
695 {
696 Object tem;
697
698 synchronized (this)
699 {
700 tem = getSystemService (Context.CLIPBOARD_SERVICE);
701 manager.thing = (ClipboardManager) tem;
702 notify ();
703 }
704 }
705 };
706
707 syncRunnable (runnable);
708 return manager.thing;
709 }
710
711 public void
712 restartEmacs ()
713 {
714 Intent intent;
715
716 intent = new Intent (this, EmacsActivity.class);
717 intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
718 | Intent.FLAG_ACTIVITY_CLEAR_TASK);
719 startActivity (intent);
720 System.exit (0);
721 }
722
723
724
725
726 public static void
727 syncRunnable (Runnable runnable)
728 {
729 EmacsNative.beginSynchronous ();
730
731 synchronized (runnable)
732 {
733 SERVICE.runOnUiThread (runnable);
734
735 while (true)
736 {
737 try
738 {
739 runnable.wait ();
740 break;
741 }
742 catch (InterruptedException e)
743 {
744 continue;
745 }
746 }
747 }
748
749 EmacsNative.endSynchronous ();
750 }
751
752
753
754
755
756
757
758
759
760
761
762 public static void
763 icBeginSynchronous ()
764 {
765
766
767
768 if (servicingQuery.getAndSet (2) == 1)
769
770
771 EmacsNative.answerQuerySpin ();
772 }
773
774 public static void
775 icEndSynchronous ()
776 {
777 if (servicingQuery.getAndSet (0) != 2)
778 throw new RuntimeException ("incorrect value of `servicingQuery': "
779 + "likely 1");
780 }
781
782 public static int[]
783 viewGetSelection (short window)
784 {
785 int[] selection;
786
787
788
789 if (!servicingQuery.compareAndSet (0, 1))
790 return null;
791
792
793
794
795
796 selection = EmacsNative.getSelection (window);
797
798
799
800
801 servicingQuery.compareAndSet (1, 0);
802 return selection;
803 }
804
805
806
807 public void
808 updateIC (EmacsWindow window, int newSelectionStart,
809 int newSelectionEnd, int composingRegionStart,
810 int composingRegionEnd)
811 {
812 if (DEBUG_IC)
813 Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
814 + " " + newSelectionEnd + " "
815 + composingRegionStart + " "
816 + composingRegionEnd));
817
818 icBeginSynchronous ();
819 window.view.imManager.updateSelection (window.view,
820 newSelectionStart,
821 newSelectionEnd,
822 composingRegionStart,
823 composingRegionEnd);
824 icEndSynchronous ();
825 }
826
827 public void
828 resetIC (EmacsWindow window, int icMode)
829 {
830 int oldMode;
831
832 if (DEBUG_IC)
833 Log.d (TAG, "resetIC: " + window + ", " + icMode);
834
835 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
836 && (oldMode = window.view.getICMode ()) == icMode
837
838
839 && oldMode != IC_MODE_NULL)
840 {
841 if (DEBUG_IC)
842 Log.d (TAG, "resetIC: calling invalidateInput");
843
844
845
846
847
848
849 icBeginSynchronous ();
850 window.view.imManager.invalidateInput (window.view);
851 icEndSynchronous ();
852
853 return;
854 }
855
856 window.view.setICMode (icMode);
857
858 icBeginSynchronous ();
859 window.view.icGeneration++;
860 window.view.imManager.restartInput (window.view);
861 icEndSynchronous ();
862 }
863
864 public void
865 updateCursorAnchorInfo (EmacsWindow window, float x,
866 float y, float yBaseline,
867 float yBottom)
868 {
869 CursorAnchorInfo info;
870 CursorAnchorInfo.Builder builder;
871 Matrix matrix;
872 int[] offsets;
873
874 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
875 return;
876
877 offsets = new int[2];
878 builder = new CursorAnchorInfo.Builder ();
879 matrix = new Matrix (window.view.getMatrix ());
880 window.view.getLocationOnScreen (offsets);
881 matrix.postTranslate (offsets[0], offsets[1]);
882 builder.setMatrix (matrix);
883 builder.setInsertionMarkerLocation (x, y, yBaseline, yBottom,
884 0);
885 info = builder.build ();
886
887
888
889 if (DEBUG_IC)
890 Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y
891 + " " + yBaseline + "-" + yBottom));
892
893 icBeginSynchronous ();
894 window.view.imManager.updateCursorAnchorInfo (window.view, info);
895 icEndSynchronous ();
896 }
897
898
899
900
901
902
903
904
905
906
907
908 public int
909 openContentUri (byte[] bytes, boolean writable, boolean readable,
910 boolean truncate)
911 {
912 String name, mode;
913 ParcelFileDescriptor fd;
914 int i;
915
916
917
918 mode = "";
919
920 if (readable)
921 mode += "r";
922
923 if (writable)
924 mode += "w";
925
926 if (truncate)
927 mode += "t";
928
929
930
931 try
932 {
933
934
935
936 name = new String (bytes, "UTF-8");
937 fd = resolver.openFileDescriptor (Uri.parse (name), mode);
938
939
940
941
942 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
943 {
944 i = fd.detachFd ();
945 fd.close ();
946
947 return i;
948 }
949 else
950 {
951 i = EmacsNative.dup (fd.getFd ());
952 fd.close ();
953
954 return i;
955 }
956 }
957 catch (Exception exception)
958 {
959 return -1;
960 }
961 }
962
963 public boolean
964 checkContentUri (byte[] string, boolean readable, boolean writable)
965 {
966 String mode, name;
967 ParcelFileDescriptor fd;
968
969
970
971 try
972 {
973
974
975 name = new String (string, "UTF-8");
976 }
977 catch (UnsupportedEncodingException exception)
978 {
979 name = null;
980 throw new RuntimeException (exception);
981 }
982
983 mode = "r";
984
985 if (writable)
986 mode += "w";
987
988 try
989 {
990 fd = resolver.openFileDescriptor (Uri.parse (name), mode);
991 fd.close ();
992
993 return true;
994 }
995 catch (Exception exception)
996 {
997
998 }
999
1000 return false;
1001 }
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013 public static String
1014 buildContentName (Uri uri)
1015 {
1016 StringBuilder builder;
1017
1018 builder = new StringBuilder ("/content/by-authority/");
1019 builder.append (uri.getAuthority ());
1020
1021
1022
1023 for (String segment : uri.getPathSegments ())
1024 {
1025
1026 builder.append ('/');
1027 builder.append (uri.encode (segment));
1028 }
1029
1030
1031
1032 if (uri.getEncodedQuery () != null)
1033 builder.append ('?').append (uri.getEncodedQuery ());
1034
1035 return builder.toString ();
1036 }
1037
1038
1039
1040 private long[]
1041 queryBattery19 ()
1042 {
1043 IntentFilter filter;
1044 Intent battery;
1045 long capacity, chargeCounter, currentAvg, currentNow;
1046 long status, remaining, plugged, temp;
1047
1048 filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
1049 battery = registerReceiver (null, filter);
1050
1051 if (battery == null)
1052 return null;
1053
1054 capacity = battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 0);
1055 chargeCounter
1056 = (battery.getIntExtra (BatteryManager.EXTRA_SCALE, 0)
1057 / battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 100) * 100);
1058 currentAvg = 0;
1059 currentNow = 0;
1060 status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
1061 remaining = -1;
1062 plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
1063 temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
1064
1065 return new long[] { capacity, chargeCounter, currentAvg,
1066 currentNow, remaining, status, plugged,
1067 temp, };
1068 }
1069
1070
1071
1072
1073
1074
1075
1076 public long[]
1077 queryBattery ()
1078 {
1079 Object tem;
1080 BatteryManager manager;
1081 long capacity, chargeCounter, currentAvg, currentNow;
1082 long status, remaining, plugged, temp;
1083 int prop;
1084 IntentFilter filter;
1085 Intent battery;
1086
1087
1088
1089
1090 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
1091 return queryBattery19 ();
1092
1093 tem = getSystemService (Context.BATTERY_SERVICE);
1094 manager = (BatteryManager) tem;
1095 remaining = -1;
1096
1097 prop = BatteryManager.BATTERY_PROPERTY_CAPACITY;
1098 capacity = manager.getLongProperty (prop);
1099 prop = BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER;
1100 chargeCounter = manager.getLongProperty (prop);
1101 prop = BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE;
1102 currentAvg = manager.getLongProperty (prop);
1103 prop = BatteryManager.BATTERY_PROPERTY_CURRENT_NOW;
1104 currentNow = manager.getLongProperty (prop);
1105
1106
1107
1108
1109 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
1110 status
1111 = manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS);
1112 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1113 status = (manager.isCharging ()
1114 ? BatteryManager.BATTERY_STATUS_CHARGING
1115 : BatteryManager.BATTERY_STATUS_DISCHARGING);
1116 else
1117 status = (currentNow > 0
1118 ? BatteryManager.BATTERY_STATUS_CHARGING
1119 : BatteryManager.BATTERY_STATUS_DISCHARGING);
1120
1121 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
1122 remaining = manager.computeChargeTimeRemaining ();
1123
1124 plugged = -1;
1125 temp = -1;
1126
1127
1128
1129 filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
1130 battery = registerReceiver (null, filter);
1131
1132 if (battery != null)
1133 {
1134 plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
1135 temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
1136
1137
1138 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
1139 status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
1140 }
1141
1142 return new long[] { capacity, chargeCounter, currentAvg,
1143 currentNow, remaining, status, plugged,
1144 temp, };
1145 }
1146
1147 public void
1148 updateExtractedText (EmacsWindow window, ExtractedText text,
1149 int token)
1150 {
1151 if (DEBUG_IC)
1152 Log.d (TAG, "updateExtractedText: @" + token + ", " + text);
1153
1154 window.view.imManager.updateExtractedText (window.view,
1155 token, text);
1156 }
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166 public String[]
1167 getDocumentAuthorities ()
1168 {
1169 List<UriPermission> permissions;
1170 HashSet<String> allProviders;
1171 Uri uri;
1172
1173 permissions = resolver.getPersistedUriPermissions ();
1174 allProviders = new HashSet<String> ();
1175
1176 for (UriPermission permission : permissions)
1177 {
1178 uri = permission.getUri ();
1179
1180 if (DocumentsContract.isTreeUri (uri)
1181 && permission.isReadPermission ())
1182 allProviders.add (uri.getAuthority ());
1183 }
1184
1185 return allProviders.toArray (new String[0]);
1186 }
1187
1188
1189
1190
1191
1192
1193
1194 public int
1195 requestDirectoryAccess ()
1196 {
1197 Runnable runnable;
1198 final EmacsHolder<Integer> rc;
1199
1200
1201
1202 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
1203 return 1;
1204
1205 rc = new EmacsHolder<Integer> ();
1206 rc.thing = Integer.valueOf (1);
1207
1208 runnable = new Runnable () {
1209 @Override
1210 public void
1211 run ()
1212 {
1213 EmacsActivity activity;
1214 Intent intent;
1215 int id;
1216
1217 synchronized (this)
1218 {
1219
1220
1221
1222 if (EmacsActivity.focusedActivities.isEmpty ())
1223 {
1224
1225
1226
1227
1228
1229 activity = EmacsActivity.lastFocusedActivity;
1230
1231 if (activity == null)
1232 {
1233
1234 notify ();
1235 return;
1236 }
1237 }
1238 else
1239 activity = EmacsActivity.focusedActivities.get (0);
1240
1241
1242 intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE);
1243
1244 try
1245 {
1246 id = EmacsActivity.ACCEPT_DOCUMENT_TREE;
1247 activity.startActivityForResult (intent, id, null);
1248 rc.thing = Integer.valueOf (0);
1249 }
1250 catch (Exception e)
1251 {
1252 e.printStackTrace ();
1253 }
1254
1255 notify ();
1256 }
1257 }
1258 };
1259
1260 syncRunnable (runnable);
1261 return rc.thing;
1262 }
1263
1264
1265
1266
1267
1268
1269
1270 public String[]
1271 getDocumentTrees (byte provider[])
1272 {
1273 String providerName;
1274 List<String> treeList;
1275 List<UriPermission> permissions;
1276 Uri uri;
1277
1278 try
1279 {
1280 providerName = new String (provider, "US-ASCII");
1281 }
1282 catch (UnsupportedEncodingException exception)
1283 {
1284 return null;
1285 }
1286
1287 permissions = resolver.getPersistedUriPermissions ();
1288 treeList = new ArrayList<String> ();
1289
1290 for (UriPermission permission : permissions)
1291 {
1292 uri = permission.getUri ();
1293
1294 if (DocumentsContract.isTreeUri (uri)
1295 && uri.getAuthority ().equals (providerName)
1296 && permission.isReadPermission ())
1297
1298
1299
1300
1301 treeList.add (Uri.encode (DocumentsContract.getTreeDocumentId (uri),
1302 " +:&?#"));
1303 }
1304
1305 return treeList.toArray (new String[0]);
1306 }
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327 private int
1328 documentIdFromName (String tree_uri, String name, String[] id_return)
1329 {
1330
1331
1332
1333 if (storageThread == null)
1334 {
1335 storageThread = new EmacsSafThread (resolver);
1336 storageThread.start ();
1337 }
1338
1339 return storageThread.documentIdFromName (tree_uri, name,
1340 id_return);
1341 }
1342
1343
1344
1345
1346
1347
1348
1349 public String
1350 getTreeUri (String tree, String authority)
1351 {
1352 Uri uri, grantedUri;
1353 List<UriPermission> permissions;
1354
1355
1356 tree = Uri.decode (tree);
1357 uri = DocumentsContract.buildTreeDocumentUri (authority, tree);
1358
1359
1360
1361 permissions = resolver.getPersistedUriPermissions ();
1362
1363 for (UriPermission permission : permissions)
1364 {
1365
1366
1367
1368 if (!permission.isReadPermission ())
1369 continue;
1370
1371 grantedUri = permission.getUri ();
1372
1373 if (grantedUri.equals (uri))
1374 return uri.toString ();
1375 }
1376
1377
1378 return null;
1379 }
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395 public long[]
1396 statDocument (String uri, String documentId)
1397 {
1398
1399
1400
1401 if (storageThread == null)
1402 {
1403 storageThread = new EmacsSafThread (resolver);
1404 storageThread.start ();
1405 }
1406
1407 return storageThread.statDocument (uri, documentId);
1408 }
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429 public int
1430 accessDocument (String uri, String documentId, boolean writable)
1431 {
1432
1433
1434
1435 if (storageThread == null)
1436 {
1437 storageThread = new EmacsSafThread (resolver);
1438 storageThread.start ();
1439 }
1440
1441 return storageThread.accessDocument (uri, documentId, writable);
1442 }
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454 public Cursor
1455 openDocumentDirectory (String uri, String documentId)
1456 {
1457
1458
1459
1460 if (storageThread == null)
1461 {
1462 storageThread = new EmacsSafThread (resolver);
1463 storageThread.start ();
1464 }
1465
1466 return storageThread.openDocumentDirectory (uri, documentId);
1467 }
1468
1469
1470
1471
1472
1473 public EmacsDirectoryEntry
1474 readDirectoryEntry (Cursor cursor)
1475 {
1476 EmacsDirectoryEntry entry;
1477 int index;
1478 String name, type;
1479
1480 entry = new EmacsDirectoryEntry ();
1481
1482 while (true)
1483 {
1484 if (!cursor.moveToNext ())
1485 return null;
1486
1487
1488 index = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
1489
1490 if (index < 0)
1491
1492 return entry;
1493
1494 try
1495 {
1496 name = cursor.getString (index);
1497 }
1498 catch (Exception exception)
1499 {
1500 return entry;
1501 }
1502
1503
1504
1505 if (name.equals ("..") || name.equals (".") || name.contains ("/"))
1506 continue;
1507
1508
1509
1510 index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
1511
1512 if (index < 0)
1513
1514 return entry;
1515
1516 try
1517 {
1518 type = cursor.getString (index);
1519 }
1520 catch (Exception exception)
1521 {
1522 return entry;
1523 }
1524
1525 if (type != null
1526 && type.equals (Document.MIME_TYPE_DIR))
1527 entry.d_type = 1;
1528 entry.d_name = name;
1529 return entry;
1530 }
1531
1532
1533 }
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557 public ParcelFileDescriptor
1558 openDocument (String uri, String documentId, boolean write,
1559 boolean truncate)
1560 {
1561
1562
1563
1564 if (storageThread == null)
1565 {
1566 storageThread = new EmacsSafThread (resolver);
1567 storageThread.start ();
1568 }
1569
1570 return storageThread.openDocument (uri, documentId, write,
1571 truncate);
1572 }
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587 public String
1588 createDocument (String uri, String documentId, String name)
1589 throws FileNotFoundException
1590 {
1591 String mimeType, separator, mime, extension;
1592 int index;
1593 MimeTypeMap singleton;
1594 Uri treeUri, directoryUri, docUri;
1595
1596
1597
1598
1599 mimeType = "application/octet-stream";
1600
1601
1602
1603 index = name.lastIndexOf ('.');
1604
1605 if (index > 0)
1606 {
1607 singleton = MimeTypeMap.getSingleton ();
1608 extension = name.substring (index + 1);
1609 mime = singleton.getMimeTypeFromExtension (extension);
1610
1611 if (mime != null)
1612 mimeType = mime;
1613 }
1614
1615
1616 treeUri = Uri.parse (uri);
1617
1618 if (documentId == null)
1619 documentId = DocumentsContract.getTreeDocumentId (treeUri);
1620
1621
1622
1623 directoryUri
1624 = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri,
1625 documentId);
1626
1627 docUri = DocumentsContract.createDocument (resolver,
1628 directoryUri,
1629 mimeType, name);
1630
1631 if (docUri == null)
1632 return null;
1633
1634
1635
1636 if (storageThread != null)
1637 storageThread.postInvalidateStat (treeUri, documentId);
1638
1639
1640 return DocumentsContract.getDocumentId (docUri);
1641 }
1642
1643
1644
1645
1646 public String
1647 createDirectory (String uri, String documentId, String name)
1648 throws FileNotFoundException
1649 {
1650 int index;
1651 Uri treeUri, directoryUri, docUri;
1652
1653
1654 treeUri = Uri.parse (uri);
1655
1656 if (documentId == null)
1657 documentId = DocumentsContract.getTreeDocumentId (treeUri);
1658
1659
1660
1661 directoryUri
1662 = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri,
1663 documentId);
1664
1665
1666
1667
1668 if (name.endsWith ("/"))
1669 name = name.substring (0, name.length () - 1);
1670
1671
1672
1673
1674 docUri = DocumentsContract.createDocument (resolver,
1675 directoryUri,
1676 Document.MIME_TYPE_DIR,
1677 name);
1678
1679 if (docUri == null)
1680 return null;
1681
1682
1683
1684
1685 if (storageThread != null)
1686 storageThread.postInvalidateStat (treeUri, documentId);
1687
1688 return DocumentsContract.getDocumentId (docUri);
1689 }
1690
1691
1692
1693
1694
1695
1696
1697
1698 public int
1699 deleteDocument (String uri, String id, String name)
1700 throws FileNotFoundException
1701 {
1702 Uri uriObject, tree;
1703
1704 tree = Uri.parse (uri);
1705 uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, id);
1706
1707 if (DocumentsContract.deleteDocument (resolver, uriObject))
1708 {
1709 if (storageThread != null)
1710 storageThread.postInvalidateCache (tree, id, name);
1711
1712 return 0;
1713 }
1714
1715 return -1;
1716 }
1717
1718
1719
1720
1721
1722
1723 public int
1724 renameDocument (String uri, String docId, String dir, String name)
1725 throws FileNotFoundException
1726 {
1727 Uri tree, uriObject;
1728
1729 tree = Uri.parse (uri);
1730 uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, docId);
1731
1732 if (DocumentsContract.renameDocument (resolver, uriObject,
1733 name)
1734 != null)
1735 {
1736
1737 if (storageThread != null)
1738 storageThread.postInvalidateCacheDir (tree, docId,
1739 name);
1740 return 0;
1741 }
1742
1743
1744
1745 return -1;
1746 }
1747
1748
1749
1750
1751
1752
1753
1754
1755 public String
1756 moveDocument (String uri, String docId, String dirName,
1757 String dstId, String srcId)
1758 throws FileNotFoundException
1759 {
1760 Uri uri1, docId1, dstId1, srcId1;
1761 Uri name;
1762
1763 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
1764 throw new UnsupportedOperationException ("Documents aren't capable"
1765 + " of being moved on Android"
1766 + " versions before 7.0.");
1767
1768 uri1 = Uri.parse (uri);
1769 docId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, docId);
1770 dstId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, dstId);
1771 srcId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, srcId);
1772
1773
1774
1775 name = DocumentsContract.moveDocument (resolver, docId1,
1776 srcId1, dstId1);
1777
1778
1779
1780 if (storageThread != null)
1781 {
1782 storageThread.postInvalidateCacheDir (uri1, docId, dirName);
1783
1784
1785
1786
1787 storageThread.postInvalidateStat (uri1, dstId);
1788 storageThread.postInvalidateStat (uri1, srcId);
1789 }
1790
1791 return (name != null
1792 ? DocumentsContract.getDocumentId (name)
1793 : null);
1794 }
1795
1796
1797
1798
1799
1800 public boolean
1801 validAuthority (String authority)
1802 {
1803 List<UriPermission> permissions;
1804 Uri uri;
1805
1806 permissions = resolver.getPersistedUriPermissions ();
1807
1808 for (UriPermission permission : permissions)
1809 {
1810 uri = permission.getUri ();
1811
1812 if (DocumentsContract.isTreeUri (uri)
1813 && permission.isReadPermission ()
1814 && uri.getAuthority ().equals (authority))
1815 return true;
1816 }
1817
1818 return false;
1819 }
1820 };