private boolean invokeBlinkup(
      final Activity activity, final BlinkupController controller, JSONArray data) {
    int timeoutMs;
    try {
      mApiKey = data.getString(BLINKUP_ARG_API_KEY);
      mDeveloperPlanId = data.getString(BLINKUP_ARG_DEVELOPER_PLAN_ID);
      timeoutMs = data.getInt(BLINKUP_ARG_TIMEOUT_MS);
      mGeneratePlanId = data.getBoolean(BLINKUP_ARG_GENERATE_PLAN_ID);
    } catch (JSONException exc) {
      BlinkUpPluginResult.sendPluginErrorToCallback(ERROR_INVALID_ARGUMENTS);
      return false;
    }

    // if api key not valid, send error message and quit
    if (!apiKeyFormatValid()) {
      BlinkUpPluginResult.sendPluginErrorToCallback(ERROR_INVALID_API_KEY);
      return false;
    }

    Intent blinkupCompleteIntent = new Intent(activity, BlinkUpCompleteActivity.class);
    blinkupCompleteIntent.putExtra(Extras.EXTRA_DEVELOPER_PLAN_ID, mDeveloperPlanId);
    blinkupCompleteIntent.putExtra(Extras.EXTRA_TIMEOUT_MS, timeoutMs);
    controller.intentBlinkupComplete = blinkupCompleteIntent;

    // default is to run on WebCore thread, we have UI so need UI thread
    activity.runOnUiThread(
        new Runnable() {
          @Override
          public void run() {
            presentBlinkUp(activity, controller);
          }
        });
    return true;
  }
  /**
   * ******************************************************** shows BlinkUpPlugin activity and
   * handles appropriate callbacks ********************************************************
   */
  private void presentBlinkUp(Activity activity, BlinkupController controller) {

    // show toast if can't acquire token
    final BlinkupController.TokenAcquireCallback tokenAcquireCallback =
        new BlinkupController.TokenAcquireCallback() {
          @Override
          public void onSuccess(String planId, String id) {}

          @Override
          public void onError(String s) {
            Log.e(TAG, s);
          }
        };

    // send back error if connectivity issue
    BlinkupController.ServerErrorHandler serverErrorHandler =
        new BlinkupController.ServerErrorHandler() {
          @Override
          public void onError(String s) {
            BlinkUpPluginResult.sendPluginErrorToCallback(ERROR_VERIFY_API_KEY_FAIL);
          }
        };

    // load cached planId if available. Otherwise, SDK generates new one automatically
    // see electricimp.com/docs/manufacturing/planids/ for info about planIDs
    if (!mGeneratePlanId) {
      if (org.apache.cordova.BuildConfig.DEBUG && !TextUtils.isEmpty(mDeveloperPlanId)) {
        controller.setPlanID(mDeveloperPlanId);
      } else {
        String planId = PreferencesHelper.getPlanId(activity);
        controller.setPlanID(planId);
      }
    }

    controller.acquireSetupToken(activity, mApiKey, tokenAcquireCallback);
    controller.selectWifiAndSetupDevice(activity, mApiKey, serverErrorHandler);
  }
  private boolean clearBlinkupData(final Activity activity, final BlinkupController controller) {
    PreferencesHelper.setPlanId(activity, null);
    sClearCache = true;
    controller.intentClearComplete = new Intent(activity, ClearCompleteActivity.class);

    // default is to run on WebCore thread, clearing shows UI so needs UI thread
    activity.runOnUiThread(
        new Runnable() {
          @Override
          public void run() {
            controller.clearDevice(activity);
          }
        });
    return true;
  }
  /**
   * ******************************************************** method called by Cordova javascript
   * *******************************************************
   */
  @Override
  public boolean execute(String action, JSONArray data, CallbackContext callbackContext)
      throws JSONException {
    sCallbackContext = callbackContext;
    final Activity activity = cordova.getActivity();
    final BlinkupController controller = BlinkupController.getInstance();

    if (INVOKE_BLINKUP.equalsIgnoreCase(action)) {
      return invokeBlinkup(activity, controller, data);
    } else if (ABORT_BLINKUP.equalsIgnoreCase(action)) {
      return abortBlinkup(controller);
    } else if (CLEAR_BLINKUP_DATA.equalsIgnoreCase(action)) {
      return clearBlinkupData(activity, controller);
    }
    return false;
  }
 private boolean abortBlinkup(BlinkupController controller) {
   controller.cancelTokenStatusPolling();
   BlinkUpPluginResult.sendPluginErrorToCallback(ERROR_CANCELLED_BY_USER);
   return true;
 }