aboutsummaryrefslogtreecommitdiffstats
path: root/java/org
diff options
context:
space:
mode:
authorPo Lu2023-12-30 10:57:11 +0800
committerPo Lu2023-12-30 10:57:11 +0800
commit94e3d1159334d08fd1d54464bf6173755ba606b7 (patch)
treeba28a88365830b45015af5fd8a0051367ae42683 /java/org
parentfe2b68d405251498518b279a17a87f4cbcf82479 (diff)
downloademacs-94e3d1159334d08fd1d54464bf6173755ba606b7.tar.gz
emacs-94e3d1159334d08fd1d54464bf6173755ba606b7.zip
Simplify code relating to UI thread synchronization
* java/org/gnu/emacs/EmacsContextMenu.java (display): * java/org/gnu/emacs/EmacsDialog.java (display): * java/org/gnu/emacs/EmacsService.java (getEmacsView) (getLocationOnScreen, getClipboardManager) (requestDirectoryAccess): Replace manual synchronization within Runnable objects by usage of FutureTask. (syncRunnable): Accept FutureTask<V> in place of Runnables, and obtain and return results from calls to its get method.
Diffstat (limited to 'java/org')
-rw-r--r--java/org/gnu/emacs/EmacsContextMenu.java28
-rw-r--r--java/org/gnu/emacs/EmacsDialog.java25
-rw-r--r--java/org/gnu/emacs/EmacsService.java208
3 files changed, 115 insertions, 146 deletions
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java
index 2652f35b545..b6c63c3cbe1 100644
--- a/java/org/gnu/emacs/EmacsContextMenu.java
+++ b/java/org/gnu/emacs/EmacsContextMenu.java
@@ -22,6 +22,9 @@ package org.gnu.emacs;
22import java.util.List; 22import java.util.List;
23import java.util.ArrayList; 23import java.util.ArrayList;
24 24
25import java.util.concurrent.Callable;
26import java.util.concurrent.FutureTask;
27
25import android.content.Context; 28import android.content.Context;
26import android.content.Intent; 29import android.content.Intent;
27 30
@@ -344,8 +347,7 @@ public final class EmacsContextMenu
344 display (final EmacsWindow window, final int xPosition, 347 display (final EmacsWindow window, final int xPosition,
345 final int yPosition, final int serial) 348 final int yPosition, final int serial)
346 { 349 {
347 Runnable runnable; 350 FutureTask<Boolean> task;
348 final EmacsHolder<Boolean> rc;
349 351
350 /* Android will permanently cease to display any popup menus at 352 /* Android will permanently cease to display any popup menus at
351 all if the list of menu items is empty. Prevent this by 353 all if the list of menu items is empty. Prevent this by
@@ -354,25 +356,17 @@ public final class EmacsContextMenu
354 if (menuItems.isEmpty ()) 356 if (menuItems.isEmpty ())
355 return false; 357 return false;
356 358
357 rc = new EmacsHolder<Boolean> (); 359 task = new FutureTask<Boolean> (new Callable<Boolean> () {
358 rc.thing = false;
359
360 runnable = new Runnable () {
361 @Override 360 @Override
362 public void 361 public Boolean
363 run () 362 call ()
364 { 363 {
365 synchronized (this) 364 lastMenuEventSerial = serial;
366 { 365 return display1 (window, xPosition, yPosition);
367 lastMenuEventSerial = serial;
368 rc.thing = display1 (window, xPosition, yPosition);
369 notify ();
370 }
371 } 366 }
372 }; 367 });
373 368
374 EmacsService.syncRunnable (runnable); 369 return EmacsService.<Boolean>syncRunnable (task);
375 return rc.thing;
376 } 370 }
377 371
378 /* Dismiss this context menu. WINDOW is the window where the 372 /* Dismiss this context menu. WINDOW is the window where the
diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java
index bad1ddde227..7552b16b370 100644
--- a/java/org/gnu/emacs/EmacsDialog.java
+++ b/java/org/gnu/emacs/EmacsDialog.java
@@ -22,6 +22,9 @@ package org.gnu.emacs;
22import java.util.List; 22import java.util.List;
23import java.util.ArrayList; 23import java.util.ArrayList;
24 24
25import java.util.concurrent.Callable;
26import java.util.concurrent.FutureTask;
27
25import android.app.AlertDialog; 28import android.app.AlertDialog;
26 29
27import android.content.Context; 30import android.content.Context;
@@ -388,26 +391,18 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener
388 public boolean 391 public boolean
389 display () 392 display ()
390 { 393 {
391 Runnable runnable; 394 FutureTask<Boolean> task;
392 final EmacsHolder<Boolean> rc;
393 395
394 rc = new EmacsHolder<Boolean> (); 396 task = new FutureTask<Boolean> (new Callable<Boolean> () {
395 rc.thing = false;
396 runnable = new Runnable () {
397 @Override 397 @Override
398 public void 398 public Boolean
399 run () 399 call ()
400 { 400 {
401 synchronized (this) 401 return display1 ();
402 {
403 rc.thing = display1 ();
404 notify ();
405 }
406 } 402 }
407 }; 403 });
408 404
409 EmacsService.syncRunnable (runnable); 405 return EmacsService.<Boolean>syncRunnable (task);
410 return rc.thing;
411 } 406 }
412 407
413 408
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
index c71670b3e47..7934d6f9cd3 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -27,6 +27,10 @@ import java.util.ArrayList;
27import java.util.HashSet; 27import java.util.HashSet;
28import java.util.List; 28import java.util.List;
29 29
30import java.util.concurrent.Callable;
31import java.util.concurrent.ExecutionException;
32import java.util.concurrent.FutureTask;
33
30import java.util.concurrent.atomic.AtomicInteger; 34import java.util.concurrent.atomic.AtomicInteger;
31 35
32import android.database.Cursor; 36import android.database.Cursor;
@@ -331,52 +335,45 @@ public final class EmacsService extends Service
331 final boolean isFocusedByDefault) 335 final boolean isFocusedByDefault)
332 { 336 {
333 Runnable runnable; 337 Runnable runnable;
334 final EmacsHolder<EmacsView> view; 338 FutureTask<EmacsView> task;
335
336 view = new EmacsHolder<EmacsView> ();
337 339
338 runnable = new Runnable () { 340 task = new FutureTask<EmacsView> (new Callable<EmacsView> () {
339 @Override 341 @Override
340 public void 342 public EmacsView
341 run () 343 call ()
342 { 344 {
343 synchronized (this) 345 EmacsView view;
344 {
345 view.thing = new EmacsView (window);
346 view.thing.setVisibility (visibility);
347 346
348 /* The following function is only present on Android 26 347 view = new EmacsView (window);
349 or later. */ 348 view.setVisibility (visibility);
350 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
351 view.thing.setFocusedByDefault (isFocusedByDefault);
352 349
353 notify (); 350 /* The following function is only present on Android 26
354 } 351 or later. */
352 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
353 view.setFocusedByDefault (isFocusedByDefault);
354
355 return view;
355 } 356 }
356 }; 357 });
357 358
358 syncRunnable (runnable); 359 return EmacsService.<EmacsView>syncRunnable (task);
359 return view.thing;
360 } 360 }
361 361
362 public void 362 public void
363 getLocationOnScreen (final EmacsView view, final int[] coordinates) 363 getLocationOnScreen (final EmacsView view, final int[] coordinates)
364 { 364 {
365 Runnable runnable; 365 FutureTask<Void> task;
366 366
367 runnable = new Runnable () { 367 task = new FutureTask<Void> (new Callable<Void> () {
368 public void 368 public Void
369 run () 369 call ()
370 { 370 {
371 synchronized (this) 371 view.getLocationOnScreen (coordinates);
372 { 372 return null;
373 view.getLocationOnScreen (coordinates);
374 notify ();
375 }
376 } 373 }
377 }; 374 });
378 375
379 syncRunnable (runnable); 376 EmacsService.<Void>syncRunnable (task);
380 } 377 }
381 378
382 379
@@ -702,28 +699,17 @@ public final class EmacsService extends Service
702 public ClipboardManager 699 public ClipboardManager
703 getClipboardManager () 700 getClipboardManager ()
704 { 701 {
705 final EmacsHolder<ClipboardManager> manager; 702 FutureTask<Object> task;
706 Runnable runnable;
707 703
708 manager = new EmacsHolder<ClipboardManager> (); 704 task = new FutureTask<Object> (new Callable<Object> () {
709 705 public Object
710 runnable = new Runnable () { 706 call ()
711 public void
712 run ()
713 { 707 {
714 Object tem; 708 return getSystemService (Context.CLIPBOARD_SERVICE);
715
716 synchronized (this)
717 {
718 tem = getSystemService (Context.CLIPBOARD_SERVICE);
719 manager.thing = (ClipboardManager) tem;
720 notify ();
721 }
722 } 709 }
723 }; 710 });
724 711
725 syncRunnable (runnable); 712 return (ClipboardManager) EmacsService.<Object>syncRunnable (task);
726 return manager.thing;
727 } 713 }
728 714
729 public void 715 public void
@@ -738,33 +724,37 @@ public final class EmacsService extends Service
738 System.exit (0); 724 System.exit (0);
739 } 725 }
740 726
741 /* Wait synchronously for the specified RUNNABLE to complete in the 727 /* Wait synchronously for the specified TASK to complete in the UI
742 UI thread. Must be called from the Emacs thread. */ 728 thread, then return its result. Must be called from the Emacs
729 thread. */
743 730
744 public static void 731 public static <V> V
745 syncRunnable (Runnable runnable) 732 syncRunnable (FutureTask<V> task)
746 { 733 {
734 V object;
735
747 EmacsNative.beginSynchronous (); 736 EmacsNative.beginSynchronous ();
737 SERVICE.runOnUiThread (task);
748 738
749 synchronized (runnable) 739 try
750 { 740 {
751 SERVICE.runOnUiThread (runnable); 741 object = task.get ();
752 742 }
753 while (true) 743 catch (ExecutionException exception)
754 { 744 {
755 try 745 /* Wrap this exception in a RuntimeException and signal it to
756 { 746 the caller. */
757 runnable.wait (); 747 throw new RuntimeException (exception.getCause ());
758 break; 748 }
759 } 749 catch (InterruptedException exception)
760 catch (InterruptedException e) 750 {
761 { 751 EmacsNative.emacsAbort ();
762 continue; 752 object = null;
763 }
764 }
765 } 753 }
766 754
767 EmacsNative.endSynchronous (); 755 EmacsNative.endSynchronous ();
756
757 return object;
768 } 758 }
769 759
770 760
@@ -1283,71 +1273,61 @@ public final class EmacsService extends Service
1283 public int 1273 public int
1284 requestDirectoryAccess () 1274 requestDirectoryAccess ()
1285 { 1275 {
1286 Runnable runnable; 1276 FutureTask<Integer> task;
1287 final EmacsHolder<Integer> rc;
1288 1277
1289 /* Return 1 if Android is too old to support this feature. */ 1278 /* Return 1 if Android is too old to support this feature. */
1290 1279
1291 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) 1280 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
1292 return 1; 1281 return 1;
1293 1282
1294 rc = new EmacsHolder<Integer> (); 1283 task = new FutureTask<Integer> (new Callable<Integer> () {
1295 rc.thing = Integer.valueOf (1);
1296
1297 runnable = new Runnable () {
1298 @Override 1284 @Override
1299 public void 1285 public Integer
1300 run () 1286 call ()
1301 { 1287 {
1302 EmacsActivity activity; 1288 EmacsActivity activity;
1303 Intent intent; 1289 Intent intent;
1304 int id; 1290 int id, rc;
1291
1292 /* Try to obtain an activity that will receive the response
1293 from the file chooser dialog. */
1305 1294
1306 synchronized (this) 1295 if (EmacsActivity.focusedActivities.isEmpty ())
1307 { 1296 {
1308 /* Try to obtain an activity that will receive the 1297 /* If focusedActivities is empty then this dialog may
1309 response from the file chooser dialog. */ 1298 have been displayed immediately after another popup
1299 dialog was dismissed. Try the EmacsActivity to be
1300 focused. */
1310 1301
1311 if (EmacsActivity.focusedActivities.isEmpty ()) 1302 activity = EmacsActivity.lastFocusedActivity;
1312 {
1313 /* If focusedActivities is empty then this dialog
1314 may have been displayed immediately after another
1315 popup dialog was dismissed. Try the
1316 EmacsActivity to be focused. */
1317
1318 activity = EmacsActivity.lastFocusedActivity;
1319
1320 if (activity == null)
1321 {
1322 /* Still no luck. Return failure. */
1323 notify ();
1324 return;
1325 }
1326 }
1327 else
1328 activity = EmacsActivity.focusedActivities.get (0);
1329 1303
1330 /* Now create the intent. */ 1304 if (activity == null)
1331 intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE); 1305 /* Still no luck. Return failure. */
1306 return 1;
1307 }
1308 else
1309 activity = EmacsActivity.focusedActivities.get (0);
1332 1310
1333 try 1311 /* Now create the intent. */
1334 { 1312 intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE);
1335 id = EmacsActivity.ACCEPT_DOCUMENT_TREE; 1313 rc = 1;
1336 activity.startActivityForResult (intent, id, null);
1337 rc.thing = Integer.valueOf (0);
1338 }
1339 catch (Exception e)
1340 {
1341 e.printStackTrace ();
1342 }
1343 1314
1344 notify (); 1315 try
1316 {
1317 id = EmacsActivity.ACCEPT_DOCUMENT_TREE;
1318 activity.startActivityForResult (intent, id, null);
1319 rc = 0;
1345 } 1320 }
1321 catch (Exception e)
1322 {
1323 e.printStackTrace ();
1324 }
1325
1326 return rc;
1346 } 1327 }
1347 }; 1328 });
1348 1329
1349 syncRunnable (runnable); 1330 return EmacsService.<Integer>syncRunnable (task);
1350 return rc.thing;
1351 } 1331 }
1352 1332
1353 /* Return an array of each tree provided by the document PROVIDER 1333 /* Return an array of each tree provided by the document PROVIDER
@@ -1969,7 +1949,7 @@ public final class EmacsService extends Service
1969 /* Now request these permissions. */ 1949 /* Now request these permissions. */
1970 activity.requestPermissions (new String[] { permission, 1950 activity.requestPermissions (new String[] { permission,
1971 permission1, }, 1951 permission1, },
1972 0); 1952 0);
1973 } 1953 }
1974 }; 1954 };
1975 1955