diff options
| author | Po Lu | 2023-12-30 10:57:11 +0800 |
|---|---|---|
| committer | Po Lu | 2023-12-30 10:57:11 +0800 |
| commit | 94e3d1159334d08fd1d54464bf6173755ba606b7 (patch) | |
| tree | ba28a88365830b45015af5fd8a0051367ae42683 /java/org | |
| parent | fe2b68d405251498518b279a17a87f4cbcf82479 (diff) | |
| download | emacs-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.java | 28 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsDialog.java | 25 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsService.java | 208 |
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; | |||
| 22 | import java.util.List; | 22 | import java.util.List; |
| 23 | import java.util.ArrayList; | 23 | import java.util.ArrayList; |
| 24 | 24 | ||
| 25 | import java.util.concurrent.Callable; | ||
| 26 | import java.util.concurrent.FutureTask; | ||
| 27 | |||
| 25 | import android.content.Context; | 28 | import android.content.Context; |
| 26 | import android.content.Intent; | 29 | import 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; | |||
| 22 | import java.util.List; | 22 | import java.util.List; |
| 23 | import java.util.ArrayList; | 23 | import java.util.ArrayList; |
| 24 | 24 | ||
| 25 | import java.util.concurrent.Callable; | ||
| 26 | import java.util.concurrent.FutureTask; | ||
| 27 | |||
| 25 | import android.app.AlertDialog; | 28 | import android.app.AlertDialog; |
| 26 | 29 | ||
| 27 | import android.content.Context; | 30 | import 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; | |||
| 27 | import java.util.HashSet; | 27 | import java.util.HashSet; |
| 28 | import java.util.List; | 28 | import java.util.List; |
| 29 | 29 | ||
| 30 | import java.util.concurrent.Callable; | ||
| 31 | import java.util.concurrent.ExecutionException; | ||
| 32 | import java.util.concurrent.FutureTask; | ||
| 33 | |||
| 30 | import java.util.concurrent.atomic.AtomicInteger; | 34 | import java.util.concurrent.atomic.AtomicInteger; |
| 31 | 35 | ||
| 32 | import android.database.Cursor; | 36 | import 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 | ||