Skip to content

Commit 353c070

Browse files
miguelsmfacebook-github-bot
authored andcommitted
Add a way to dismiss PopupMenu elements
Summary: In native Android apps, like the YouTube app, context menus are closed when the device orientation changes. In React Native apps instead, when having a [PopupMenu](https://developer.android.com/reference/android/widget/PopupMenu.html) open and rotating the device, the PopupMenu is not dismissed and appears in a wrong position on the screen. This PR exposes a `dismissPopupMenu` method to allow the application to dismiss any open PopupMenu: ```(javascript) UIManager.dismissPopupMenu() ``` Closes #15636 Differential Revision: D6837663 Pulled By: hramos fbshipit-source-id: 7b0f4f04341129ad45c703a50897e17d93651974
1 parent 6426735 commit 353c070

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed

ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java

+15-5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class NativeViewHierarchyManager {
7474
private final LayoutAnimationController mLayoutAnimator = new LayoutAnimationController();
7575

7676
private boolean mLayoutAnimationEnabled;
77+
private PopupMenu mPopupMenu;
7778

7879
public NativeViewHierarchyManager(ViewManagerRegistry viewManagers) {
7980
this(viewManagers, new RootViewManager());
@@ -731,18 +732,27 @@ public synchronized void showPopupMenu(int reactTag, ReadableArray items, Callba
731732
error.invoke("Can't display popup. Could not find view with tag " + reactTag);
732733
return;
733734
}
734-
PopupMenu popupMenu = new PopupMenu(getReactContextForView(reactTag), anchor);
735+
mPopupMenu = new PopupMenu(getReactContextForView(reactTag), anchor);
735736

736-
Menu menu = popupMenu.getMenu();
737+
Menu menu = mPopupMenu.getMenu();
737738
for (int i = 0; i < items.size(); i++) {
738739
menu.add(Menu.NONE, Menu.NONE, i, items.getString(i));
739740
}
740741

741742
PopupMenuCallbackHandler handler = new PopupMenuCallbackHandler(success);
742-
popupMenu.setOnMenuItemClickListener(handler);
743-
popupMenu.setOnDismissListener(handler);
743+
mPopupMenu.setOnMenuItemClickListener(handler);
744+
mPopupMenu.setOnDismissListener(handler);
744745

745-
popupMenu.show();
746+
mPopupMenu.show();
747+
}
748+
749+
/**
750+
* Dismiss the last opened PopupMenu {@link PopupMenu}.
751+
*/
752+
public void dismissPopupMenu() {
753+
if (mPopupMenu != null) {
754+
mPopupMenu.dismiss();
755+
}
746756
}
747757

748758
private static class PopupMenuCallbackHandler implements PopupMenu.OnMenuItemClickListener,

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java

+4
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,10 @@ public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Cal
802802
mOperationsQueue.enqueueShowPopupMenu(reactTag, items, error, success);
803803
}
804804

805+
public void dismissPopupMenu() {
806+
mOperationsQueue.enqueueDismissPopupMenu();
807+
}
808+
805809
public void sendAccessibilityEvent(int tag, int eventType) {
806810
mOperationsQueue.enqueueSendAccessibilityEvent(tag, eventType);
807811
}

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java

+5
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,11 @@ public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Cal
600600
mUIImplementation.showPopupMenu(reactTag, items, error, success);
601601
}
602602

603+
@ReactMethod
604+
public void dismissPopupMenu() {
605+
mUIImplementation.dismissPopupMenu();
606+
}
607+
603608
/**
604609
* LayoutAnimation API on Android is currently experimental. Therefore, it needs to be enabled
605610
* explicitly in order to avoid regression in existing application written for iOS using this API.

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java

+11
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,13 @@ public void execute() {
285285
}
286286
}
287287

288+
private final class DismissPopupMenuOperation implements UIOperation {
289+
@Override
290+
public void execute() {
291+
mNativeViewHierarchyManager.dismissPopupMenu();
292+
}
293+
}
294+
288295
/**
289296
* A spec for animation operations (add/remove)
290297
*/
@@ -656,6 +663,10 @@ public void enqueueShowPopupMenu(
656663
mOperations.add(new ShowPopupMenuOperation(reactTag, items, error, success));
657664
}
658665

666+
public void enqueueDismissPopupMenu() {
667+
mOperations.add(new DismissPopupMenuOperation());
668+
}
669+
659670
public void enqueueCreateView(
660671
ThemedReactContext themedContext,
661672
int viewReactTag,

ReactAndroid/src/main/java/com/facebook/react/views/toolbar/ReactToolbarManager.java

+22
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
public class ReactToolbarManager extends ViewGroupManager<ReactToolbar> {
3434

3535
private static final String REACT_CLASS = "ToolbarAndroid";
36+
private static final int COMMAND_DISMISS_POPUP_MENUS = 1;
3637

3738
@Override
3839
public String getName() {
@@ -157,6 +158,27 @@ public boolean needsCustomLayoutForChildren() {
157158
return true;
158159
}
159160

161+
@Nullable
162+
@Override
163+
public Map<String, Integer> getCommandsMap() {
164+
return MapBuilder.of("dismissPopupMenus", COMMAND_DISMISS_POPUP_MENUS);
165+
}
166+
167+
@Override
168+
public void receiveCommand(ReactToolbar view, int commandType, @Nullable ReadableArray args) {
169+
switch (commandType) {
170+
case COMMAND_DISMISS_POPUP_MENUS: {
171+
view.dismissPopupMenus();
172+
return;
173+
}
174+
default:
175+
throw new IllegalArgumentException(String.format(
176+
"Unsupported command %d received by %s.",
177+
commandType,
178+
getClass().getSimpleName()));
179+
}
180+
}
181+
160182
private int[] getDefaultContentInsets(Context context) {
161183
Resources.Theme theme = context.getTheme();
162184
TypedArray toolbarStyle = null;

0 commit comments

Comments
 (0)