Permalink
Browse files

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
  • Loading branch information...
miguelsm authored and facebook-github-bot committed Mar 16, 2018
1 parent 6426735 commit 353c070be9e9a5528d2098db4df3f0dc02d758a9
@@ -74,6 +74,7 @@
private final LayoutAnimationController mLayoutAnimator = new LayoutAnimationController();
private boolean mLayoutAnimationEnabled;
private PopupMenu mPopupMenu;
public NativeViewHierarchyManager(ViewManagerRegistry viewManagers) {
this(viewManagers, new RootViewManager());
@@ -731,18 +732,27 @@ public synchronized void showPopupMenu(int reactTag, ReadableArray items, Callba
error.invoke("Can't display popup. Could not find view with tag " + reactTag);
return;
}
PopupMenu popupMenu = new PopupMenu(getReactContextForView(reactTag), anchor);
mPopupMenu = new PopupMenu(getReactContextForView(reactTag), anchor);
Menu menu = popupMenu.getMenu();
Menu menu = mPopupMenu.getMenu();
for (int i = 0; i < items.size(); i++) {
menu.add(Menu.NONE, Menu.NONE, i, items.getString(i));
}
PopupMenuCallbackHandler handler = new PopupMenuCallbackHandler(success);
popupMenu.setOnMenuItemClickListener(handler);
popupMenu.setOnDismissListener(handler);
mPopupMenu.setOnMenuItemClickListener(handler);
mPopupMenu.setOnDismissListener(handler);
popupMenu.show();
mPopupMenu.show();
}
/**
* Dismiss the last opened PopupMenu {@link PopupMenu}.
*/
public void dismissPopupMenu() {
if (mPopupMenu != null) {
mPopupMenu.dismiss();
}
}
private static class PopupMenuCallbackHandler implements PopupMenu.OnMenuItemClickListener,
@@ -802,6 +802,10 @@ public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Cal
mOperationsQueue.enqueueShowPopupMenu(reactTag, items, error, success);
}
public void dismissPopupMenu() {
mOperationsQueue.enqueueDismissPopupMenu();
}
public void sendAccessibilityEvent(int tag, int eventType) {
mOperationsQueue.enqueueSendAccessibilityEvent(tag, eventType);
}
@@ -600,6 +600,11 @@ public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Cal
mUIImplementation.showPopupMenu(reactTag, items, error, success);
}
@ReactMethod
public void dismissPopupMenu() {
mUIImplementation.dismissPopupMenu();
}
/**
* LayoutAnimation API on Android is currently experimental. Therefore, it needs to be enabled
* explicitly in order to avoid regression in existing application written for iOS using this API.
@@ -285,6 +285,13 @@ public void execute() {
}
}
private final class DismissPopupMenuOperation implements UIOperation {
@Override
public void execute() {
mNativeViewHierarchyManager.dismissPopupMenu();
}
}
/**
* A spec for animation operations (add/remove)
*/
@@ -656,6 +663,10 @@ public void enqueueShowPopupMenu(
mOperations.add(new ShowPopupMenuOperation(reactTag, items, error, success));
}
public void enqueueDismissPopupMenu() {
mOperations.add(new DismissPopupMenuOperation());
}
public void enqueueCreateView(
ThemedReactContext themedContext,
int viewReactTag,
@@ -33,6 +33,7 @@
public class ReactToolbarManager extends ViewGroupManager<ReactToolbar> {
private static final String REACT_CLASS = "ToolbarAndroid";
private static final int COMMAND_DISMISS_POPUP_MENUS = 1;
@Override
public String getName() {
@@ -157,6 +158,27 @@ public boolean needsCustomLayoutForChildren() {
return true;
}
@Nullable
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.of("dismissPopupMenus", COMMAND_DISMISS_POPUP_MENUS);
}
@Override
public void receiveCommand(ReactToolbar view, int commandType, @Nullable ReadableArray args) {
switch (commandType) {
case COMMAND_DISMISS_POPUP_MENUS: {
view.dismissPopupMenus();
return;
}
default:
throw new IllegalArgumentException(String.format(
"Unsupported command %d received by %s.",
commandType,
getClass().getSimpleName()));
}
}
private int[] getDefaultContentInsets(Context context) {
Resources.Theme theme = context.getTheme();
TypedArray toolbarStyle = null;

0 comments on commit 353c070

Please sign in to comment.