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 Background Service",
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
964
965
966
967
968 public boolean
969 checkContentUri (String name, boolean readable, boolean writable)
970 {
971 String mode;
972 ParcelFileDescriptor fd;
973 Uri uri;
974 int rc, flags;
975
976 uri = Uri.parse (name);
977 flags = 0;
978
979 if (readable)
980 flags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
981
982 if (writable)
983 flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
984
985 rc = checkCallingUriPermission (uri, flags);
986 return rc == PackageManager.PERMISSION_GRANTED;
987 }
988
989
990
991
992
993
994
995
996
997
998
999 public static String
1000 buildContentName (Uri uri)
1001 {
1002 StringBuilder builder;
1003
1004 builder = new StringBuilder ("/content/by-authority/");
1005 builder.append (uri.getAuthority ());
1006
1007
1008
1009 for (String segment : uri.getPathSegments ())
1010 {
1011
1012 builder.append ('/');
1013 builder.append (uri.encode (segment));
1014 }
1015
1016
1017
1018 if (uri.getEncodedQuery () != null)
1019 builder.append ('?').append (uri.getEncodedQuery ());
1020
1021 return builder.toString ();
1022 }
1023
1024
1025
1026 private long[]
1027 queryBattery19 ()
1028 {
1029 IntentFilter filter;
1030 Intent battery;
1031 long capacity, chargeCounter, currentAvg, currentNow;
1032 long status, remaining, plugged, temp;
1033
1034 filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
1035 battery = registerReceiver (null, filter);
1036
1037 if (battery == null)
1038 return null;
1039
1040 capacity = battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 0);
1041 chargeCounter
1042 = (battery.getIntExtra (BatteryManager.EXTRA_SCALE, 0)
1043 / battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 100) * 100);
1044 currentAvg = 0;
1045 currentNow = 0;
1046 status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
1047 remaining = -1;
1048 plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
1049 temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
1050
1051 return new long[] { capacity, chargeCounter, currentAvg,
1052 currentNow, remaining, status, plugged,
1053 temp, };
1054 }
1055
1056
1057
1058
1059
1060
1061
1062 public long[]
1063 queryBattery ()
1064 {
1065 Object tem;
1066 BatteryManager manager;
1067 long capacity, chargeCounter, currentAvg, currentNow;
1068 long status, remaining, plugged, temp;
1069 int prop;
1070 IntentFilter filter;
1071 Intent battery;
1072
1073
1074
1075
1076 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
1077 return queryBattery19 ();
1078
1079 tem = getSystemService (Context.BATTERY_SERVICE);
1080 manager = (BatteryManager) tem;
1081 remaining = -1;
1082
1083 prop = BatteryManager.BATTERY_PROPERTY_CAPACITY;
1084 capacity = manager.getLongProperty (prop);
1085 prop = BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER;
1086 chargeCounter = manager.getLongProperty (prop);
1087 prop = BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE;
1088 currentAvg = manager.getLongProperty (prop);
1089 prop = BatteryManager.BATTERY_PROPERTY_CURRENT_NOW;
1090 currentNow = manager.getLongProperty (prop);
1091
1092
1093
1094
1095 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
1096 status
1097 = manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS);
1098 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1099 status = (manager.isCharging ()
1100 ? BatteryManager.BATTERY_STATUS_CHARGING
1101 : BatteryManager.BATTERY_STATUS_DISCHARGING);
1102 else
1103 status = (currentNow > 0
1104 ? BatteryManager.BATTERY_STATUS_CHARGING
1105 : BatteryManager.BATTERY_STATUS_DISCHARGING);
1106
1107 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
1108 remaining = manager.computeChargeTimeRemaining ();
1109
1110 plugged = -1;
1111 temp = -1;
1112
1113
1114
1115 filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
1116 battery = registerReceiver (null, filter);
1117
1118 if (battery != null)
1119 {
1120 plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
1121 temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
1122
1123
1124 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
1125 status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
1126 }
1127
1128 return new long[] { capacity, chargeCounter, currentAvg,
1129 currentNow, remaining, status, plugged,
1130 temp, };
1131 }
1132
1133 public void
1134 updateExtractedText (EmacsWindow window, ExtractedText text,
1135 int token)
1136 {
1137 if (DEBUG_IC)
1138 Log.d (TAG, "updateExtractedText: @" + token + ", " + text);
1139
1140 window.view.imManager.updateExtractedText (window.view,
1141 token, text);
1142 }
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152 public String[]
1153 getDocumentAuthorities ()
1154 {
1155 List<UriPermission> permissions;
1156 HashSet<String> allProviders;
1157 Uri uri;
1158
1159 permissions = resolver.getPersistedUriPermissions ();
1160 allProviders = new HashSet<String> ();
1161
1162 for (UriPermission permission : permissions)
1163 {
1164 uri = permission.getUri ();
1165
1166 if (DocumentsContract.isTreeUri (uri)
1167 && permission.isReadPermission ())
1168 allProviders.add (uri.getAuthority ());
1169 }
1170
1171 return allProviders.toArray (new String[0]);
1172 }
1173
1174
1175
1176
1177
1178
1179
1180 public int
1181 requestDirectoryAccess ()
1182 {
1183 Runnable runnable;
1184 final EmacsHolder<Integer> rc;
1185
1186
1187
1188 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
1189 return 1;
1190
1191 rc = new EmacsHolder<Integer> ();
1192 rc.thing = Integer.valueOf (1);
1193
1194 runnable = new Runnable () {
1195 @Override
1196 public void
1197 run ()
1198 {
1199 EmacsActivity activity;
1200 Intent intent;
1201 int id;
1202
1203 synchronized (this)
1204 {
1205
1206
1207
1208 if (EmacsActivity.focusedActivities.isEmpty ())
1209 {
1210
1211
1212
1213
1214
1215 activity = EmacsActivity.lastFocusedActivity;
1216
1217 if (activity == null)
1218 {
1219
1220 notify ();
1221 return;
1222 }
1223 }
1224 else
1225 activity = EmacsActivity.focusedActivities.get (0);
1226
1227
1228 intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE);
1229
1230 try
1231 {
1232 id = EmacsActivity.ACCEPT_DOCUMENT_TREE;
1233 activity.startActivityForResult (intent, id, null);
1234 rc.thing = Integer.valueOf (0);
1235 }
1236 catch (Exception e)
1237 {
1238 e.printStackTrace ();
1239 }
1240
1241 notify ();
1242 }
1243 }
1244 };
1245
1246 syncRunnable (runnable);
1247 return rc.thing;
1248 }
1249
1250
1251
1252
1253
1254
1255
1256 public String[]
1257 getDocumentTrees (byte provider[])
1258 {
1259 String providerName;
1260 List<String> treeList;
1261 List<UriPermission> permissions;
1262 Uri uri;
1263
1264 try
1265 {
1266 providerName = new String (provider, "US-ASCII");
1267 }
1268 catch (UnsupportedEncodingException exception)
1269 {
1270 return null;
1271 }
1272
1273 permissions = resolver.getPersistedUriPermissions ();
1274 treeList = new ArrayList<String> ();
1275
1276 for (UriPermission permission : permissions)
1277 {
1278 uri = permission.getUri ();
1279
1280 if (DocumentsContract.isTreeUri (uri)
1281 && uri.getAuthority ().equals (providerName)
1282 && permission.isReadPermission ())
1283
1284
1285
1286
1287 treeList.add (Uri.encode (DocumentsContract.getTreeDocumentId (uri),
1288 " +:&?#"));
1289 }
1290
1291 return treeList.toArray (new String[0]);
1292 }
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313 private int
1314 documentIdFromName (String tree_uri, String name, String[] id_return)
1315 {
1316
1317
1318
1319 if (storageThread == null)
1320 {
1321 storageThread = new EmacsSafThread (resolver);
1322 storageThread.start ();
1323 }
1324
1325 return storageThread.documentIdFromName (tree_uri, name,
1326 id_return);
1327 }
1328
1329
1330
1331
1332
1333
1334
1335 public String
1336 getTreeUri (String tree, String authority)
1337 {
1338 Uri uri, grantedUri;
1339 List<UriPermission> permissions;
1340
1341
1342 tree = Uri.decode (tree);
1343 uri = DocumentsContract.buildTreeDocumentUri (authority, tree);
1344
1345
1346
1347 permissions = resolver.getPersistedUriPermissions ();
1348
1349 for (UriPermission permission : permissions)
1350 {
1351
1352
1353
1354 if (!permission.isReadPermission ())
1355 continue;
1356
1357 grantedUri = permission.getUri ();
1358
1359 if (grantedUri.equals (uri))
1360 return uri.toString ();
1361 }
1362
1363
1364 return null;
1365 }
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384 public long[]
1385 statDocument (String uri, String documentId, boolean noCache)
1386 {
1387
1388
1389
1390 if (storageThread == null)
1391 {
1392 storageThread = new EmacsSafThread (resolver);
1393 storageThread.start ();
1394 }
1395
1396 return storageThread.statDocument (uri, documentId, noCache);
1397 }
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418 public int
1419 accessDocument (String uri, String documentId, boolean writable)
1420 {
1421
1422
1423
1424 if (storageThread == null)
1425 {
1426 storageThread = new EmacsSafThread (resolver);
1427 storageThread.start ();
1428 }
1429
1430 return storageThread.accessDocument (uri, documentId, writable);
1431 }
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443 public Cursor
1444 openDocumentDirectory (String uri, String documentId)
1445 {
1446
1447
1448
1449 if (storageThread == null)
1450 {
1451 storageThread = new EmacsSafThread (resolver);
1452 storageThread.start ();
1453 }
1454
1455 return storageThread.openDocumentDirectory (uri, documentId);
1456 }
1457
1458
1459
1460
1461
1462 public EmacsDirectoryEntry
1463 readDirectoryEntry (Cursor cursor)
1464 {
1465 EmacsDirectoryEntry entry;
1466 int index;
1467 String name, type;
1468
1469 entry = new EmacsDirectoryEntry ();
1470
1471 while (true)
1472 {
1473 if (!cursor.moveToNext ())
1474 return null;
1475
1476
1477 index = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
1478
1479 if (index < 0)
1480
1481 return entry;
1482
1483 try
1484 {
1485 name = cursor.getString (index);
1486 }
1487 catch (Exception exception)
1488 {
1489 return entry;
1490 }
1491
1492
1493
1494
1495
1496 if (name == null || name.equals ("..")
1497 || name.equals (".") || name.contains ("/")
1498 || name.contains ("\0"))
1499 continue;
1500
1501
1502
1503 index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
1504
1505 if (index < 0)
1506
1507 return entry;
1508
1509 try
1510 {
1511 type = cursor.getString (index);
1512 }
1513 catch (Exception exception)
1514 {
1515 return entry;
1516 }
1517
1518 if (type != null
1519 && type.equals (Document.MIME_TYPE_DIR))
1520 entry.d_type = 1;
1521 entry.d_name = name;
1522 return entry;
1523 }
1524
1525
1526 }
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548 public ParcelFileDescriptor
1549 openDocument (String uri, String documentId,
1550 boolean read, boolean write, boolean truncate)
1551 {
1552
1553
1554
1555 if (storageThread == null)
1556 {
1557 storageThread = new EmacsSafThread (resolver);
1558 storageThread.start ();
1559 }
1560
1561 return storageThread.openDocument (uri, documentId, read, write,
1562 truncate);
1563 }
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578 public String
1579 createDocument (String uri, String documentId, String name)
1580 throws FileNotFoundException
1581 {
1582 String mimeType, separator, mime, extension;
1583 int index;
1584 MimeTypeMap singleton;
1585 Uri treeUri, directoryUri, docUri;
1586
1587
1588
1589
1590 mimeType = "application/octet-stream";
1591
1592
1593
1594 index = name.lastIndexOf ('.');
1595
1596 if (index > 0)
1597 {
1598 singleton = MimeTypeMap.getSingleton ();
1599 extension = name.substring (index + 1);
1600 mime = singleton.getMimeTypeFromExtension (extension);
1601
1602 if (mime != null)
1603 mimeType = mime;
1604 }
1605
1606
1607 treeUri = Uri.parse (uri);
1608
1609 if (documentId == null)
1610 documentId = DocumentsContract.getTreeDocumentId (treeUri);
1611
1612
1613
1614 directoryUri
1615 = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri,
1616 documentId);
1617
1618 docUri = DocumentsContract.createDocument (resolver,
1619 directoryUri,
1620 mimeType, name);
1621
1622 if (docUri == null)
1623 return null;
1624
1625
1626
1627 if (storageThread != null)
1628 storageThread.postInvalidateStat (treeUri, documentId);
1629
1630
1631 return DocumentsContract.getDocumentId (docUri);
1632 }
1633
1634
1635
1636
1637 public String
1638 createDirectory (String uri, String documentId, String name)
1639 throws FileNotFoundException
1640 {
1641 int index;
1642 Uri treeUri, directoryUri, docUri;
1643
1644
1645 treeUri = Uri.parse (uri);
1646
1647 if (documentId == null)
1648 documentId = DocumentsContract.getTreeDocumentId (treeUri);
1649
1650
1651
1652 directoryUri
1653 = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri,
1654 documentId);
1655
1656
1657
1658
1659 if (name.endsWith ("/"))
1660 name = name.substring (0, name.length () - 1);
1661
1662
1663
1664
1665 docUri = DocumentsContract.createDocument (resolver,
1666 directoryUri,
1667 Document.MIME_TYPE_DIR,
1668 name);
1669
1670 if (docUri == null)
1671 return null;
1672
1673
1674
1675
1676 if (storageThread != null)
1677 storageThread.postInvalidateStat (treeUri, documentId);
1678
1679 return DocumentsContract.getDocumentId (docUri);
1680 }
1681
1682
1683
1684
1685
1686
1687
1688
1689 public int
1690 deleteDocument (String uri, String id, String name)
1691 throws FileNotFoundException
1692 {
1693 Uri uriObject, tree;
1694
1695 tree = Uri.parse (uri);
1696 uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, id);
1697
1698 if (DocumentsContract.deleteDocument (resolver, uriObject))
1699 {
1700 if (storageThread != null)
1701 storageThread.postInvalidateCache (tree, id, name);
1702
1703 return 0;
1704 }
1705
1706 return -1;
1707 }
1708
1709
1710
1711
1712
1713
1714 public int
1715 renameDocument (String uri, String docId, String dir, String name)
1716 throws FileNotFoundException
1717 {
1718 Uri tree, uriObject;
1719
1720 tree = Uri.parse (uri);
1721 uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, docId);
1722
1723 if (DocumentsContract.renameDocument (resolver, uriObject,
1724 name)
1725 != null)
1726 {
1727
1728 if (storageThread != null)
1729 storageThread.postInvalidateCacheDir (tree, docId,
1730 name);
1731 return 0;
1732 }
1733
1734
1735
1736 return -1;
1737 }
1738
1739
1740
1741
1742
1743
1744
1745
1746 public String
1747 moveDocument (String uri, String docId, String dirName,
1748 String dstId, String srcId)
1749 throws FileNotFoundException
1750 {
1751 Uri uri1, docId1, dstId1, srcId1;
1752 Uri name;
1753
1754 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
1755 throw new UnsupportedOperationException ("Documents aren't capable"
1756 + " of being moved on Android"
1757 + " versions before 7.0.");
1758
1759 uri1 = Uri.parse (uri);
1760 docId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, docId);
1761 dstId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, dstId);
1762 srcId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, srcId);
1763
1764
1765
1766 name = DocumentsContract.moveDocument (resolver, docId1,
1767 srcId1, dstId1);
1768
1769
1770
1771 if (storageThread != null)
1772 {
1773 storageThread.postInvalidateCacheDir (uri1, docId, dirName);
1774
1775
1776
1777
1778 storageThread.postInvalidateStat (uri1, dstId);
1779 storageThread.postInvalidateStat (uri1, srcId);
1780 }
1781
1782 return (name != null
1783 ? DocumentsContract.getDocumentId (name)
1784 : null);
1785 }
1786
1787
1788
1789
1790
1791 public boolean
1792 validAuthority (String authority)
1793 {
1794 List<UriPermission> permissions;
1795 Uri uri;
1796
1797 permissions = resolver.getPersistedUriPermissions ();
1798
1799 for (UriPermission permission : permissions)
1800 {
1801 uri = permission.getUri ();
1802
1803 if (DocumentsContract.isTreeUri (uri)
1804 && permission.isReadPermission ()
1805 && uri.getAuthority ().equals (authority))
1806 return true;
1807 }
1808
1809 return false;
1810 }
1811 };