]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve context menus on old versions of Android
authorPo Lu <luangruo@yahoo.com>
Sat, 4 Mar 2023 07:55:09 +0000 (15:55 +0800)
committerPo Lu <luangruo@yahoo.com>
Sat, 4 Mar 2023 07:55:09 +0000 (15:55 +0800)
* 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.

java/org/gnu/emacs/EmacsActivity.java
java/org/gnu/emacs/EmacsContextMenu.java
java/org/gnu/emacs/EmacsView.java

index 62bef33fab33c428c0f9c8ccb31900169d6882a0..13002d5d0e5db08670ac7b122fa9b5c9bffbad8a 100644 (file)
@@ -65,6 +65,9 @@ public class EmacsActivity extends Activity
   /* Whether or not this activity is fullscreen.  */
   private boolean isFullscreen;
 
+  /* The last context menu to be closed.  */
+  private Menu lastClosedMenu;
+
   static
   {
     focusedActivities = new ArrayList<EmacsActivity> ();
@@ -308,9 +311,19 @@ public class EmacsActivity extends Activity
     Log.d (TAG, "onContextMenuClosed: " + menu);
 
     /* See the comment inside onMenuItemClick.  */
+
     if (EmacsContextMenu.wasSubmenuSelected
-       && menu.toString ().contains ("ContextMenuBuilder"))
-      return;
+       || menu == lastClosedMenu)
+      {
+       EmacsContextMenu.wasSubmenuSelected = false;
+       lastClosedMenu = menu;
+       return;
+      }
+
+    /* lastClosedMenu is set because Android apparently calls this
+       function twice.  */
+
+    lastClosedMenu = null;
 
     /* Send a context menu event given that no menu item has already
        been selected.  */
index a1bca98daa047bac08298affd5996d8ef5385ecd..d1a624e68d9d451af5f07a07b3cb6361c31ab630 100644 (file)
@@ -58,6 +58,7 @@ public final class EmacsContextMenu
     public String itemName, tooltip;
     public EmacsContextMenu subMenu;
     public boolean isEnabled, isCheckable, isChecked;
+    public EmacsView inflatedView;
 
     @Override
     public boolean
@@ -67,6 +68,34 @@ public final class EmacsContextMenu
 
       if (subMenu != null)
        {
+         /* Android 6.0 and earlier don't support nested submenus
+            properly, so display the submenu popup by hand.  */
+
+         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+           {
+             Log.d (TAG, "onMenuItemClick: displaying submenu " + subMenu);
+
+             /* Still set wasSubmenuSelected -- if not set, the
+                dismissal of this context menu will result in a
+                context menu event being sent.  */
+             wasSubmenuSelected = true;
+
+             /* Running a popup menu from inside a click handler
+                doesn't work, so make sure it is displayed
+                outside.  */
+
+             inflatedView.post (new Runnable () {
+                 @Override
+                 public void
+                 run ()
+                 {
+                   inflatedView.popupMenu (subMenu, 0, 0, true);
+                 }
+               });
+
+             return true;
+           }
+
          /* After opening a submenu within a submenu, Android will
             send onContextMenuClosed for a ContextMenuBuilder.  This
             will normally confuse Emacs into thinking that the
@@ -164,10 +193,11 @@ public final class EmacsContextMenu
     return item.subMenu;
   }
 
-  /* Add the contents of this menu to MENU.  */
+  /* Add the contents of this menu to MENU.  Assume MENU will be
+     displayed in INFLATEDVIEW.  */
 
   private void
-  inflateMenuItems (Menu menu)
+  inflateMenuItems (Menu menu, EmacsView inflatedView)
   {
     Intent intent;
     MenuItem menuItem;
@@ -177,26 +207,26 @@ public final class EmacsContextMenu
       {
        if (item.subMenu != null)
          {
-           try
+           /* This is a submenu.  On versions of Android which
+              support doing so, create the submenu and add the
+              contents of the menu to it.
+
+              Note that Android 4.0 and later technically supports
+              having multiple layers of nested submenus, but if they
+              are used, onContextMenuClosed becomes unreliable.  */
+
+           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
              {
-               /* This is a submenu.  On versions of Android which
-                  support doing so, create the submenu and add the
-                  contents of the menu to it.  */
                submenu = menu.addSubMenu (item.itemName);
-               item.subMenu.inflateMenuItems (submenu);
-             }
-           catch (UnsupportedOperationException exception)
-             {
-               /* This version of Android has a restriction
-                  preventing submenus from being added to submenus.
-                  Inflate everything into the parent menu
-                  instead.  */
-               item.subMenu.inflateMenuItems (menu);
-               continue;
+               item.subMenu.inflateMenuItems (submenu, inflatedView);
+
+               /* This is still needed to set wasSubmenuSelected.  */
+               menuItem = submenu.getItem ();
              }
+           else
+             menuItem = menu.add (item.itemName);
 
-           /* This is still needed to set wasSubmenuSelected.  */
-           menuItem = submenu.getItem ();
+           item.inflatedView = inflatedView;
            menuItem.setOnMenuItemClickListener (item);
          }
        else
@@ -227,16 +257,14 @@ public final class EmacsContextMenu
       }
   }
 
-  /* Enter the items in this context menu to MENU.  Create each menu
-     item with an Intent containing a Bundle, where the key
-     "emacs:menu_item_hi" maps to the high 16 bits of the
-     corresponding item ID, and the key "emacs:menu_item_low" maps to
-     the low 16 bits of the item ID.  */
+  /* Enter the items in this context menu to MENU.
+     Assume that MENU will be displayed in VIEW; this may lead to
+     popupMenu being called on VIEW if a submenu is selected.  */
 
   public void
-  expandTo (Menu menu)
+  expandTo (Menu menu, EmacsView view)
   {
-    inflateMenuItems (menu);
+    inflateMenuItems (menu, view);
   }
 
   /* Return the parent or NULL.  */
@@ -260,7 +288,8 @@ public final class EmacsContextMenu
     /* No submenu has been selected yet.  */
     wasSubmenuSelected = false;
 
-    return window.view.popupMenu (this, xPosition, yPosition);
+    return window.view.popupMenu (this, xPosition, yPosition,
+                                 false);
   }
 
   /* Display this context menu on WINDOW, at xPosition and
index d2330494bc7ab2b3264fbb95323c28ee7fe9b5ed..617836d88118eca9ec8c438c492b5d4d5b977076 100644 (file)
@@ -464,19 +464,22 @@ public final class EmacsView extends ViewGroup
     if (contextMenu == null)
       return;
 
-    contextMenu.expandTo (menu);
+    contextMenu.expandTo (menu, this);
   }
 
   public boolean
   popupMenu (EmacsContextMenu menu, int xPosition,
-            int yPosition)
+            int yPosition, boolean force)
   {
-    if (popupActive)
+    if (popupActive && !force)
       return false;
 
     contextMenu = menu;
     popupActive = true;
 
+    Log.d (TAG, "popupMenu: " + menu + " @" + xPosition
+          + ", " + yPosition + " " + force);
+
     /* Use showContextMenu (float, float) on N to get actual popup
        behavior.  */
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)