/**
   * Handles an updateView call. If a view transitions from being layout-only to not (or vice-versa)
   * this could result in some number of additional createView and manageChildren calls. If the view
   * is layout only, no updateView call will be dispatched to the native hierarchy.
   */
  public void handleUpdateView(
      ReactShadowNode node, String className, CatalystStylesDiffMap props) {
    if (!ENABLED) {
      mUIViewOperationQueue.enqueueUpdateProperties(node.getReactTag(), className, props);
      return;
    }

    boolean needsToLeaveLayoutOnly = node.isLayoutOnly() && !isLayoutOnlyAndCollapsable(props);
    if (needsToLeaveLayoutOnly) {
      transitionLayoutOnlyViewToNativeView(node, props);
    } else if (!node.isLayoutOnly()) {
      mUIViewOperationQueue.enqueueUpdateProperties(node.getReactTag(), className, props);
    }
  }
 /**
  * Determines the location on screen, width, and height of the given view and returns the values
  * via an async callback.
  */
 public void measure(int reactTag, Callback callback) {
   // This method is called by the implementation of JS touchable interface (see Touchable.js for
   // more details) at the moment of touch activation. That is after user starts the gesture from
   // a touchable view with a given reactTag, or when user drag finger back into the press
   // activation area of a touchable view that have been activated before.
   mOperationsQueue.enqueueMeasure(reactTag, callback);
 }
  private void applyLayoutRecursive(ReactShadowNode toUpdate, int x, int y) {
    if (!toUpdate.isLayoutOnly() && toUpdate.getNativeParent() != null) {
      int tag = toUpdate.getReactTag();
      mUIViewOperationQueue.enqueueUpdateLayout(
          toUpdate.getNativeParent().getReactTag(),
          tag,
          x,
          y,
          toUpdate.getScreenWidth(),
          toUpdate.getScreenHeight());
      return;
    }

    for (int i = 0; i < toUpdate.getChildCount(); i++) {
      ReactShadowNode child = toUpdate.getChildAt(i);
      int childTag = child.getReactTag();
      if (mTagsWithLayoutVisited.get(childTag)) {
        continue;
      }
      mTagsWithLayoutVisited.put(childTag, true);

      int childX = child.getScreenX();
      int childY = child.getScreenY();

      childX += x;
      childY += y;

      applyLayoutRecursive(child, childX, childY);
    }
  }
  /**
   * Handles a manageChildren call. This may translate into multiple manageChildren calls for
   * multiple other views.
   *
   * <p>NB: the assumption for calling this method is that all corresponding ReactShadowNodes have
   * been updated **but tagsToDelete have NOT been deleted yet**. This is because we need to use the
   * metadata from those nodes to figure out the correct commands to dispatch. This is unlike all
   * other calls on this class where we assume all operations on the shadow hierarchy have already
   * completed by the time a corresponding method here is called.
   */
  public void handleManageChildren(
      ReactShadowNode nodeToManage,
      int[] indicesToRemove,
      int[] tagsToRemove,
      ViewAtIndex[] viewsToAdd,
      int[] tagsToDelete) {
    if (!ENABLED) {
      mUIViewOperationQueue.enqueueManageChildren(
          nodeToManage.getReactTag(), indicesToRemove, viewsToAdd, tagsToDelete);
      return;
    }

    // We operate on tagsToRemove instead of indicesToRemove because by the time this method is
    // called, these views have already been removed from the shadow hierarchy and the indices are
    // no longer useful to operate on
    for (int i = 0; i < tagsToRemove.length; i++) {
      int tagToRemove = tagsToRemove[i];
      boolean delete = false;
      for (int j = 0; j < tagsToDelete.length; j++) {
        if (tagsToDelete[j] == tagToRemove) {
          delete = true;
          break;
        }
      }
      ReactShadowNode nodeToRemove = mShadowNodeRegistry.getNode(tagToRemove);
      removeNodeFromParent(nodeToRemove, delete);
    }

    for (int i = 0; i < viewsToAdd.length; i++) {
      ViewAtIndex toAdd = viewsToAdd[i];
      ReactShadowNode nodeToAdd = mShadowNodeRegistry.getNode(toAdd.mTag);
      addNodeToNode(nodeToManage, nodeToAdd, toAdd.mIndex);
    }
  }
 public void setJSResponder(int reactTag, boolean blockNativeResponder) {
   assertViewExists(reactTag, "setJSResponder");
   ReactShadowNode node = mShadowNodeRegistry.getNode(reactTag);
   while (node.isVirtual() || node.isLayoutOnly()) {
     node = node.getParent();
   }
   mOperationsQueue.enqueueSetJSResponder(node.getReactTag(), reactTag, blockNativeResponder);
 }
  /** Handles a createView call. May or may not actually create a native view. */
  public void handleCreateView(
      ReactShadowNode node, int rootViewTag, @Nullable CatalystStylesDiffMap initialProps) {
    if (!ENABLED) {
      int tag = node.getReactTag();
      mUIViewOperationQueue.enqueueCreateView(rootViewTag, tag, node.getViewClass(), initialProps);
      return;
    }

    boolean isLayoutOnly =
        node.getViewClass().equals(ViewProps.VIEW_CLASS_NAME)
            && isLayoutOnlyAndCollapsable(initialProps);
    node.setIsLayoutOnly(isLayoutOnly);

    if (!isLayoutOnly) {
      mUIViewOperationQueue.enqueueCreateView(
          rootViewTag, node.getReactTag(), node.getViewClass(), initialProps);
    }
  }
 private void addNonLayoutNodeToNonLayoutNode(
     ReactShadowNode parent, ReactShadowNode child, int index) {
   parent.addNativeChildAt(child, index);
   mUIViewOperationQueue.enqueueManageChildren(
       parent.getReactTag(),
       null,
       new ViewAtIndex[] {new ViewAtIndex(child.getReactTag(), index)},
       null);
 }
  /** Invoked when native view that corresponds to a root node has its size changed. */
  public void updateRootNodeSize(
      int rootViewTag, int newWidth, int newHeight, EventDispatcher eventDispatcher) {
    ReactShadowNode rootCSSNode = mShadowNodeRegistry.getNode(rootViewTag);
    rootCSSNode.setStyleWidth(newWidth);
    rootCSSNode.setStyleHeight(newHeight);

    // If we're in the middle of a batch, the change will automatically be dispatched at the end of
    // the batch. As all batches are executed as a single runnable on the event queue this should
    // always be empty, but that calling architecture is an implementation detail.
    if (mOperationsQueue.isEmpty()) {
      dispatchViewUpdates(eventDispatcher, -1); // -1 = no associated batch id
    }
  }
  /**
   * Handles an updateLayout call. All updateLayout calls are collected and dispatched at the end of
   * a batch because updateLayout calls to layout-only nodes can necessitate multiple updateLayout
   * calls for all its children.
   */
  public void handleUpdateLayout(ReactShadowNode node) {
    if (!ENABLED) {
      mUIViewOperationQueue.enqueueUpdateLayout(
          Assertions.assertNotNull(node.getParent()).getReactTag(),
          node.getReactTag(),
          node.getScreenX(),
          node.getScreenY(),
          node.getScreenWidth(),
          node.getScreenHeight());
      return;
    }

    applyLayoutBase(node);
  }
  /**
   * Registers a root node with a given tag, size and ThemedReactContext and adds it to a node
   * registry.
   */
  public void registerRootView(
      SizeMonitoringFrameLayout rootView,
      int tag,
      int width,
      int height,
      ThemedReactContext context) {
    final ReactShadowNode rootCSSNode = new ReactShadowNode();
    rootCSSNode.setReactTag(tag);
    rootCSSNode.setThemedContext(context);
    rootCSSNode.setStyleWidth(width);
    rootCSSNode.setStyleHeight(height);
    rootCSSNode.setViewClassName("Root");
    mShadowNodeRegistry.addRootNode(rootCSSNode);

    // register it within NativeViewHierarchyManager
    mOperationsQueue.addRootView(tag, rootView, context);
  }
  /**
   * For handling node removal from manageChildren. In the case of removing a layout-only node, we
   * need to instead recursively remove all its children from their native parents.
   */
  private void removeNodeFromParent(ReactShadowNode nodeToRemove, boolean shouldDelete) {
    ReactShadowNode nativeNodeToRemoveFrom = nodeToRemove.getNativeParent();

    if (nativeNodeToRemoveFrom != null) {
      int index = nativeNodeToRemoveFrom.indexOfNativeChild(nodeToRemove);
      nativeNodeToRemoveFrom.removeNativeChildAt(index);

      mUIViewOperationQueue.enqueueManageChildren(
          nativeNodeToRemoveFrom.getReactTag(),
          new int[] {index},
          null,
          shouldDelete ? new int[] {nodeToRemove.getReactTag()} : null);
    } else {
      for (int i = nodeToRemove.getChildCount() - 1; i >= 0; i--) {
        removeNodeFromParent(nodeToRemove.getChildAt(i), shouldDelete);
      }
    }
  }
  /** Invoked at the end of the transaction to commit any updates to the node hierarchy. */
  public void dispatchViewUpdates(EventDispatcher eventDispatcher, int batchId) {
    for (int i = 0; i < mShadowNodeRegistry.getRootNodeCount(); i++) {
      int tag = mShadowNodeRegistry.getRootTag(i);
      ReactShadowNode cssRoot = mShadowNodeRegistry.getNode(tag);
      notifyOnBeforeLayoutRecursive(cssRoot);

      SystraceMessage.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "cssRoot.calculateLayout")
          .arg("rootTag", tag)
          .flush();
      try {
        cssRoot.calculateLayout(mLayoutContext);
      } finally {
        Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
      }
      applyUpdatesRecursive(cssRoot, 0f, 0f, eventDispatcher);
    }

    mNativeViewHierarchyOptimizer.onBatchComplete();
    mOperationsQueue.dispatchViewUpdates(batchId);
  }
  private void transitionLayoutOnlyViewToNativeView(
      ReactShadowNode node, @Nullable CatalystStylesDiffMap props) {
    ReactShadowNode parent = node.getParent();
    if (parent == null) {
      node.setIsLayoutOnly(false);
      return;
    }

    // First, remove the node from its parent. This causes the parent to update its native children
    // count. The removeNodeFromParent call will cause all the view's children to be detached from
    // their native parent.
    int childIndex = parent.indexOf(node);
    parent.removeChildAt(childIndex);
    removeNodeFromParent(node, false);

    node.setIsLayoutOnly(false);

    // Create the view since it doesn't exist in the native hierarchy yet
    mUIViewOperationQueue.enqueueCreateView(
        node.getRootNode().getReactTag(), node.getReactTag(), node.getViewClass(), props);

    // Add the node and all its children as if we are adding a new nodes
    parent.addChildAt(node, childIndex);
    addNodeToNode(parent, node, childIndex);
    for (int i = 0; i < node.getChildCount(); i++) {
      addNodeToNode(node, node.getChildAt(i), i);
    }

    // Update layouts since the children of the node were offset by its x/y position previously.
    // Bit of a hack: we need to update the layout of this node's children now that it's no longer
    // layout-only, but we may still receive more layout updates at the end of this batch that we
    // don't want to ignore.
    Assertions.assertCondition(mTagsWithLayoutVisited.size() == 0);
    applyLayoutBase(node);
    for (int i = 0; i < node.getChildCount(); i++) {
      applyLayoutBase(node.getChildAt(i));
    }
    mTagsWithLayoutVisited.clear();
  }
 public void setViewHierarchyUpdateDebugListener(
     @Nullable NotThreadSafeViewHierarchyUpdateDebugListener listener) {
   mOperationsQueue.setViewHierarchyUpdateDebugListener(listener);
 }
 public void onHostPause() {
   mOperationsQueue.pauseFrameCallback();
 }
 public void onHostResume() {
   mOperationsQueue.resumeFrameCallback();
 }
 public void sendAccessibilityEvent(int tag, int eventType) {
   mOperationsQueue.enqueueSendAccessibilityEvent(tag, eventType);
 }
 /**
  * Show a PopupMenu.
  *
  * @param reactTag the tag of the anchor view (the PopupMenu is displayed next to this view); this
  *     needs to be the tag of a native view (shadow views can not be anchors)
  * @param items the menu items as an array of strings
  * @param error will be called if there is an error displaying the menu
  * @param success will be called with the position of the selected item as the first argument, or
  *     no arguments if the menu is dismissed
  */
 public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Callback success) {
   assertViewExists(reactTag, "showPopupMenu");
   mOperationsQueue.enqueueShowPopupMenu(reactTag, items, error, success);
 }
 public void clearJSResponder() {
   mOperationsQueue.enqueueClearJSResponder();
 }
 /** Removes an existing Animation, canceling it if it was in progress. */
 public void removeAnimation(int reactTag, int animationID) {
   assertViewExists(reactTag, "removeAnimation");
   mOperationsQueue.enqueueRemoveAnimation(animationID);
 }
 /** Registers a new Animation that can then be added to a View using {@link #addAnimation}. */
 public void registerAnimation(Animation animation) {
   mOperationsQueue.enqueueRegisterAnimation(animation);
 }
 /** Unregisters a root node with a given tag. */
 public void removeRootView(int rootViewTag) {
   mShadowNodeRegistry.removeRootNode(rootViewTag);
   mOperationsQueue.enqueueRemoveRootView(rootViewTag);
 }
 public void dispatchViewManagerCommand(int reactTag, int commandId, ReadableArray commandArgs) {
   assertViewExists(reactTag, "dispatchViewManagerCommand");
   mOperationsQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs);
 }
 /**
  * Adds an Animation previously registered with {@link #registerAnimation} to a View and starts it
  */
 public void addAnimation(int reactTag, int animationID, Callback onSuccess) {
   assertViewExists(reactTag, "addAnimation");
   mOperationsQueue.enqueueAddAnimation(reactTag, animationID, onSuccess);
 }
 /**
  * Find the touch target child native view in the supplied root view hierarchy, given a react
  * target location.
  *
  * <p>This method is currently used only by Element Inspector DevTool.
  *
  * @param reactTag the tag of the root view to traverse
  * @param targetX target X location
  * @param targetY target Y location
  * @param callback will be called if with the identified child view react ID, and measurement
  *     info. If no view was found, callback will be invoked with no data.
  */
 public void findSubviewIn(int reactTag, float targetX, float targetY, Callback callback) {
   mOperationsQueue.enqueueFindTargetForTouch(reactTag, targetX, targetY, callback);
 }