aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorPo Lu2023-03-04 15:55:09 +0800
committerPo Lu2023-03-04 15:55:09 +0800
commit2634765bc382c27e2d11dc14174ca80d9cf41e15 (patch)
tree3ef522e33fec01fbe0447dfc10306e6e58e17d8d /java
parent39a7e6b79fdeafc539a36f6831d922a2622cb679 (diff)
downloademacs-2634765bc382c27e2d11dc14174ca80d9cf41e15.tar.gz
emacs-2634765bc382c27e2d11dc14174ca80d9cf41e15.zip
Improve context menus on old versions of Android
* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New field `lastClosedMenu'. (onContextMenuClosed): Don't send event if a menu is closed twice in a row. Also, clear wasSubmenuSelected immediately. * java/org/gnu/emacs/EmacsContextMenu.java: Display submenus manually in Android 6.0 and earlier. * java/org/gnu/emacs/EmacsView.java (onCreateContextMenu) (popupMenu): Adjust accordingly.
Diffstat (limited to 'java')
-rw-r--r--java/org/gnu/emacs/EmacsActivity.java17
-rw-r--r--java/org/gnu/emacs/EmacsContextMenu.java81
-rw-r--r--java/org/gnu/emacs/EmacsView.java9
3 files changed, 76 insertions, 31 deletions
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java
index 62bef33fab3..13002d5d0e5 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -65,6 +65,9 @@ public class EmacsActivity extends Activity
65 /* Whether or not this activity is fullscreen. */ 65 /* Whether or not this activity is fullscreen. */
66 private boolean isFullscreen; 66 private boolean isFullscreen;
67 67
68 /* The last context menu to be closed. */
69 private Menu lastClosedMenu;
70
68 static 71 static
69 { 72 {
70 focusedActivities = new ArrayList<EmacsActivity> (); 73 focusedActivities = new ArrayList<EmacsActivity> ();
@@ -308,9 +311,19 @@ public class EmacsActivity extends Activity
308 Log.d (TAG, "onContextMenuClosed: " + menu); 311 Log.d (TAG, "onContextMenuClosed: " + menu);
309 312
310 /* See the comment inside onMenuItemClick. */ 313 /* See the comment inside onMenuItemClick. */
314
311 if (EmacsContextMenu.wasSubmenuSelected 315 if (EmacsContextMenu.wasSubmenuSelected
312 && menu.toString ().contains ("ContextMenuBuilder")) 316 || menu == lastClosedMenu)
313 return; 317 {
318 EmacsContextMenu.wasSubmenuSelected = false;
319 lastClosedMenu = menu;
320 return;
321 }
322
323 /* lastClosedMenu is set because Android apparently calls this
324 function twice. */
325
326 lastClosedMenu = null;
314 327
315 /* Send a context menu event given that no menu item has already 328 /* Send a context menu event given that no menu item has already
316 been selected. */ 329 been selected. */
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java
index a1bca98daa0..d1a624e68d9 100644
--- a/java/org/gnu/emacs/EmacsContextMenu.java
+++ b/java/org/gnu/emacs/EmacsContextMenu.java
@@ -58,6 +58,7 @@ public final class EmacsContextMenu
58 public String itemName, tooltip; 58 public String itemName, tooltip;
59 public EmacsContextMenu subMenu; 59 public EmacsContextMenu subMenu;
60 public boolean isEnabled, isCheckable, isChecked; 60 public boolean isEnabled, isCheckable, isChecked;
61 public EmacsView inflatedView;
61 62
62 @Override 63 @Override
63 public boolean 64 public boolean
@@ -67,6 +68,34 @@ public final class EmacsContextMenu
67 68
68 if (subMenu != null) 69 if (subMenu != null)
69 { 70 {
71 /* Android 6.0 and earlier don't support nested submenus
72 properly, so display the submenu popup by hand. */
73
74 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
75 {
76 Log.d (TAG, "onMenuItemClick: displaying submenu " + subMenu);
77
78 /* Still set wasSubmenuSelected -- if not set, the
79 dismissal of this context menu will result in a
80 context menu event being sent. */
81 wasSubmenuSelected = true;
82
83 /* Running a popup menu from inside a click handler
84 doesn't work, so make sure it is displayed
85 outside. */
86
87 inflatedView.post (new Runnable () {
88 @Override
89 public void
90 run ()
91 {
92 inflatedView.popupMenu (subMenu, 0, 0, true);
93 }
94 });
95
96 return true;
97 }
98
70 /* After opening a submenu within a submenu, Android will 99 /* After opening a submenu within a submenu, Android will
71 send onContextMenuClosed for a ContextMenuBuilder. This 100 send onContextMenuClosed for a ContextMenuBuilder. This
72 will normally confuse Emacs into thinking that the 101 will normally confuse Emacs into thinking that the
@@ -164,10 +193,11 @@ public final class EmacsContextMenu
164 return item.subMenu; 193 return item.subMenu;
165 } 194 }
166 195
167 /* Add the contents of this menu to MENU. */ 196 /* Add the contents of this menu to MENU. Assume MENU will be
197 displayed in INFLATEDVIEW. */
168 198
169 private void 199 private void
170 inflateMenuItems (Menu menu) 200 inflateMenuItems (Menu menu, EmacsView inflatedView)
171 { 201 {
172 Intent intent; 202 Intent intent;
173 MenuItem menuItem; 203 MenuItem menuItem;
@@ -177,26 +207,26 @@ public final class EmacsContextMenu
177 { 207 {
178 if (item.subMenu != null) 208 if (item.subMenu != null)
179 { 209 {
180 try 210 /* This is a submenu. On versions of Android which
211 support doing so, create the submenu and add the
212 contents of the menu to it.
213
214 Note that Android 4.0 and later technically supports
215 having multiple layers of nested submenus, but if they
216 are used, onContextMenuClosed becomes unreliable. */
217
218 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
181 { 219 {
182 /* This is a submenu. On versions of Android which
183 support doing so, create the submenu and add the
184 contents of the menu to it. */
185 submenu = menu.addSubMenu (item.itemName); 220 submenu = menu.addSubMenu (item.itemName);
186 item.subMenu.inflateMenuItems (submenu); 221 item.subMenu.inflateMenuItems (submenu, inflatedView);
187 } 222
188 catch (UnsupportedOperationException exception) 223 /* This is still needed to set wasSubmenuSelected. */
189 { 224 menuItem = submenu.getItem ();
190 /* This version of Android has a restriction
191 preventing submenus from being added to submenus.
192 Inflate everything into the parent menu
193 instead. */
194 item.subMenu.inflateMenuItems (menu);
195 continue;
196 } 225 }
226 else
227 menuItem = menu.add (item.itemName);
197 228
198 /* This is still needed to set wasSubmenuSelected. */ 229 item.inflatedView = inflatedView;
199 menuItem = submenu.getItem ();
200 menuItem.setOnMenuItemClickListener (item); 230 menuItem.setOnMenuItemClickListener (item);
201 } 231 }
202 else 232 else
@@ -227,16 +257,14 @@ public final class EmacsContextMenu
227 } 257 }
228 } 258 }
229 259
230 /* Enter the items in this context menu to MENU. Create each menu 260 /* Enter the items in this context menu to MENU.
231 item with an Intent containing a Bundle, where the key 261 Assume that MENU will be displayed in VIEW; this may lead to
232 "emacs:menu_item_hi" maps to the high 16 bits of the 262 popupMenu being called on VIEW if a submenu is selected. */
233 corresponding item ID, and the key "emacs:menu_item_low" maps to
234 the low 16 bits of the item ID. */
235 263
236 public void 264 public void
237 expandTo (Menu menu) 265 expandTo (Menu menu, EmacsView view)
238 { 266 {
239 inflateMenuItems (menu); 267 inflateMenuItems (menu, view);
240 } 268 }
241 269
242 /* Return the parent or NULL. */ 270 /* Return the parent or NULL. */
@@ -260,7 +288,8 @@ public final class EmacsContextMenu
260 /* No submenu has been selected yet. */ 288 /* No submenu has been selected yet. */
261 wasSubmenuSelected = false; 289 wasSubmenuSelected = false;
262 290
263 return window.view.popupMenu (this, xPosition, yPosition); 291 return window.view.popupMenu (this, xPosition, yPosition,
292 false);
264 } 293 }
265 294
266 /* Display this context menu on WINDOW, at xPosition and 295 /* Display this context menu on WINDOW, at xPosition and
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
index d2330494bc7..617836d8811 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -464,19 +464,22 @@ public final class EmacsView extends ViewGroup
464 if (contextMenu == null) 464 if (contextMenu == null)
465 return; 465 return;
466 466
467 contextMenu.expandTo (menu); 467 contextMenu.expandTo (menu, this);
468 } 468 }
469 469
470 public boolean 470 public boolean
471 popupMenu (EmacsContextMenu menu, int xPosition, 471 popupMenu (EmacsContextMenu menu, int xPosition,
472 int yPosition) 472 int yPosition, boolean force)
473 { 473 {
474 if (popupActive) 474 if (popupActive && !force)
475 return false; 475 return false;
476 476
477 contextMenu = menu; 477 contextMenu = menu;
478 popupActive = true; 478 popupActive = true;
479 479
480 Log.d (TAG, "popupMenu: " + menu + " @" + xPosition
481 + ", " + yPosition + " " + force);
482
480 /* Use showContextMenu (float, float) on N to get actual popup 483 /* Use showContextMenu (float, float) on N to get actual popup
481 behavior. */ 484 behavior. */
482 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 485 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)