@Override
 public void receiveCommand(ReactViewGroup root, int commandId, @Nullable ReadableArray args) {
   switch (commandId) {
     case CMD_HOTSPOT_UPDATE:
       {
         if (args == null || args.size() != 2) {
           throw new JSApplicationIllegalArgumentException(
               "Illegal number of arguments for 'updateHotspot' command");
         }
         if (Build.VERSION.SDK_INT >= 21) {
           root.getLocationOnScreen(sLocationBuf);
           float x = PixelUtil.toPixelFromDIP(args.getDouble(0)) - sLocationBuf[0];
           float y = PixelUtil.toPixelFromDIP(args.getDouble(1)) - sLocationBuf[1];
           root.drawableHotspotChanged(x, y);
         }
         break;
       }
     case CMD_SET_PRESSED:
       {
         if (args == null || args.size() != 1) {
           throw new JSApplicationIllegalArgumentException(
               "Illegal number of arguments for 'setPressed' command");
         }
         root.setPressed(args.getBoolean(0));
         break;
       }
   }
 }
  public void updateDataSetOptions(DataSetClass dataSet, ReadableMap map) {
    if (map.hasKey("values")) {
      ReadableArray valueArray = map.getArray("values");
      for (int j = 0; j < valueArray.size(); j++) {
        Entry entry;
        try {
          entry = entryConstructor.newInstance((float) valueArray.getDouble(j), j);
        } catch (Exception e) {
          throw new Error("Entry failed to instantiate");
        }

        dataSet.addEntry(entry);
      }
    }

    if (map.hasKey("colors")) {
      ReadableArray colorsArray = map.getArray("colors");
      ArrayList<Integer> colors = new ArrayList<>();

      for (int c = 0; c < colorsArray.size(); c++) {
        colors.add(Color.parseColor(colorsArray.getString(c)));
      }

      dataSet.setColors(colors);
    }

    if (map.hasKey("drawValues")) {
      dataSet.setDrawValues(map.getBoolean("drawValues"));
    }

    // TODO: add other properties to dataSet here

  }
 @ReactProp(name = "colors")
 public void setColors(ReactSwipeRefreshLayout view, @Nullable ReadableArray colors) {
   if (colors != null) {
     int[] colorValues = new int[colors.size()];
     for (int i = 0; i < colors.size(); i++) {
       colorValues[i] = colors.getInt(i);
     }
     view.setColorSchemeColors(colorValues);
   } else {
     view.setColorSchemeColors();
   }
 }
  private Boolean updateMarkers(ReadableArray markerArray) {
    try {
      // First clear all markers from the map
      for (Marker marker : mapMarkers) {
        marker.remove();
      }
      mapMarkers.clear();
      markerLookup.clear();

      // All markers to map
      for (int i = 0; i < markerArray.size(); i++) {
        ReadableMap markerJson = markerArray.getMap(i);
        if (markerJson.hasKey("coordinates")) {
          Marker marker = map.addMarker(createMarker(markerJson));

          if (markerJson.hasKey("id")) {
            // As we have to lookup it either way, switch it around
            markerLookup.put(marker.getId(), markerJson.getString("id"));
            markerLookup.put(markerJson.getString("id"), marker.getId().replace("m", ""));
          }

          mapMarkers.add(marker);

        } else break;
      }

      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }
  @ReactProp(name = "data")
  public void setData(ChartClass chart, ReadableMap map) {
    ChartData chartData;
    try {
      chartData = chartDataConstructor.newInstance();
    } catch (Exception e) {
      throw new RuntimeException("ChartData failed to instantiate");
    }

    if (map.hasKey("xValues")) {
      ReadableArray xValuesArray = map.getArray("xValues");
      for (int k = 0; k < xValuesArray.size(); k++) {
        chartData.addXValue(xValuesArray.getString(k));
      }
    }

    if (map.hasKey("dataSets")) {
      ReadableArray dataSetsArray = map.getArray("dataSets");
      for (int i = 0; i < dataSetsArray.size(); i++) {

        ReadableMap dataSetMap = dataSetsArray.getMap(i);
        DataSetClass dataSet;

        try {
          dataSet =
              (DataSetClass)
                  this.dataSetConstructor.newInstance(new ArrayList<>(), "Data Set " + i);
        } catch (Exception e) {
          throw new RuntimeException("DataSet failed to instantiate");
        }

        updateDataSetOptions(dataSet, dataSetMap);
        chartData.addDataSet(dataSet);
      }

      chart.notifyDataSetChanged();
    }

    // TODO: add other properties to data here

    chart.setData(chartData);
    chart.invalidate();
  }
  public ShowOptions(@Nullable ReadableMap options) {
    if (options == null) {
      return;
    }

    if (options.hasKey(CLOSABLE_KEY)) {
      closable = options.getBoolean(CLOSABLE_KEY);
      Log.d(TAG, CLOSABLE_KEY + closable);
    }

    if (options.hasKey(USE_MAGIC_LINK_KEY)) {
      useMagicLink = options.getBoolean(USE_MAGIC_LINK_KEY);
      Log.d(TAG, USE_MAGIC_LINK_KEY + useMagicLink);
    }

    if (options.hasKey(AUTH_PARAMS_KEY)) {
      ReadableMap reactMap = options.getMap(AUTH_PARAMS_KEY);
      authParams = OptionsHelper.convertReadableMapToMap(reactMap);
      Log.d(TAG, AUTH_PARAMS_KEY + authParams);
    }

    if (options.hasKey(CONNECTIONS_KEY)) {
      ReadableArray connections = options.getArray(CONNECTIONS_KEY);
      List<String> list = new ArrayList<>(connections.size());
      for (int i = 0; i < connections.size(); i++) {
        String connectionName = connections.getString(i);
        switch (connectionName) {
          case LockReactModule.CONNECTION_EMAIL:
            connectionType = LockReactModule.CONNECTION_EMAIL;
            break;
          case LockReactModule.CONNECTION_SMS:
            connectionType = LockReactModule.CONNECTION_SMS;
            break;
        }
        list.add(connectionName);
      }
      this.connections = new String[list.size()];
      this.connections = list.toArray(this.connections);
      Log.d(TAG, CONNECTIONS_KEY + list);
    }
  }
  /**
   * Show a {@link 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 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 success) {
    UiThreadUtil.assertOnUiThread();
    View anchor = mTagsToViews.get(reactTag);
    if (anchor == null) {
      throw new JSApplicationIllegalArgumentException("Could not find view with tag " + reactTag);
    }
    PopupMenu popupMenu = new PopupMenu(getReactContextForView(reactTag), anchor);

    Menu menu = popupMenu.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);

    popupMenu.show();
  }
  /**
   * Invoked when there is a mutation in a node tree.
   *
   * @param tag react tag of the node we want to manage
   * @param indicesToRemove ordered (asc) list of indicies at which view should be removed
   * @param viewsToAdd ordered (asc based on mIndex property) list of tag-index pairs that represent
   *     a view which should be added at the specified index
   * @param tagsToDelete list of tags corresponding to views that should be removed
   */
  public void manageChildren(
      int viewTag,
      @Nullable ReadableArray moveFrom,
      @Nullable ReadableArray moveTo,
      @Nullable ReadableArray addChildTags,
      @Nullable ReadableArray addAtIndices,
      @Nullable ReadableArray removeFrom) {
    ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag);

    int numToMove = moveFrom == null ? 0 : moveFrom.size();
    int numToAdd = addChildTags == null ? 0 : addChildTags.size();
    int numToRemove = removeFrom == null ? 0 : removeFrom.size();

    if (numToMove != 0 && (moveTo == null || numToMove != moveTo.size())) {
      throw new IllegalViewOperationException("Size of moveFrom != size of moveTo!");
    }

    if (numToAdd != 0 && (addAtIndices == null || numToAdd != addAtIndices.size())) {
      throw new IllegalViewOperationException("Size of addChildTags != size of addAtIndices!");
    }

    // We treat moves as an add and a delete
    ViewAtIndex[] viewsToAdd = new ViewAtIndex[numToMove + numToAdd];
    int[] indicesToRemove = new int[numToMove + numToRemove];
    int[] tagsToRemove = new int[indicesToRemove.length];
    int[] tagsToDelete = new int[numToRemove];

    if (numToMove > 0) {
      Assertions.assertNotNull(moveFrom);
      Assertions.assertNotNull(moveTo);
      for (int i = 0; i < numToMove; i++) {
        int moveFromIndex = moveFrom.getInt(i);
        int tagToMove = cssNodeToManage.getChildAt(moveFromIndex).getReactTag();
        viewsToAdd[i] = new ViewAtIndex(tagToMove, moveTo.getInt(i));
        indicesToRemove[i] = moveFromIndex;
        tagsToRemove[i] = tagToMove;
      }
    }

    if (numToAdd > 0) {
      Assertions.assertNotNull(addChildTags);
      Assertions.assertNotNull(addAtIndices);
      for (int i = 0; i < numToAdd; i++) {
        int viewTagToAdd = addChildTags.getInt(i);
        int indexToAddAt = addAtIndices.getInt(i);
        viewsToAdd[numToMove + i] = new ViewAtIndex(viewTagToAdd, indexToAddAt);
      }
    }

    if (numToRemove > 0) {
      Assertions.assertNotNull(removeFrom);
      for (int i = 0; i < numToRemove; i++) {
        int indexToRemove = removeFrom.getInt(i);
        int tagToRemove = cssNodeToManage.getChildAt(indexToRemove).getReactTag();
        indicesToRemove[numToMove + i] = indexToRemove;
        tagsToRemove[numToMove + i] = tagToRemove;
        tagsToDelete[i] = tagToRemove;
      }
    }

    // NB: moveFrom and removeFrom are both relative to the starting state of the View's children.
    // moveTo and addAt are both relative to the final state of the View's children.
    //
    // 1) Sort the views to add and indices to remove by index
    // 2) Iterate the indices being removed from high to low and remove them. Going high to low
    //    makes sure we remove the correct index when there are multiple to remove.
    // 3) Iterate the views being added by index low to high and add them. Like the view removal,
    //    iteration direction is important to preserve the correct index.

    Arrays.sort(viewsToAdd, ViewAtIndex.COMPARATOR);
    Arrays.sort(indicesToRemove);

    // Apply changes to CSSNode hierarchy
    int lastIndexRemoved = -1;
    for (int i = indicesToRemove.length - 1; i >= 0; i--) {
      int indexToRemove = indicesToRemove[i];
      if (indexToRemove == lastIndexRemoved) {
        throw new IllegalViewOperationException(
            "Repeated indices in Removal list for view tag: " + viewTag);
      }
      cssNodeToManage.removeChildAt(indicesToRemove[i]);
      lastIndexRemoved = indicesToRemove[i];
    }

    for (int i = 0; i < viewsToAdd.length; i++) {
      ViewAtIndex viewAtIndex = viewsToAdd[i];
      ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(viewAtIndex.mTag);
      if (cssNodeToAdd == null) {
        throw new IllegalViewOperationException(
            "Trying to add unknown view tag: " + viewAtIndex.mTag);
      }
      cssNodeToManage.addChildAt(cssNodeToAdd, viewAtIndex.mIndex);
    }

    if (!cssNodeToManage.isVirtual() && !cssNodeToManage.isVirtualAnchor()) {
      mNativeViewHierarchyOptimizer.handleManageChildren(
          cssNodeToManage, indicesToRemove, tagsToRemove, viewsToAdd, tagsToDelete);
    }

    for (int i = 0; i < tagsToDelete.length; i++) {
      removeShadowNode(mShadowNodeRegistry.getNode(tagsToDelete[i]));
    }
  }