/** Launch a client android app for the given concert app. */
  private static Result launchAndroidApp(
      final Activity parent,
      final RoconDescription concert,
      final rocon_interaction_msgs.Interaction app) {

    // Create the Intent from rapp's name, pass it parameters and remaps and start it
    String appName = app.getName();
    Intent intent = new Intent(appName);

    // Copy all app data to "extra" data in the intent.
    intent.putExtra(
        Constants.ACTIVITY_SWITCHER_ID + "." + InteractionMode.CONCERT + "_app_name", appName);
    intent.putExtra(RoconDescription.UNIQUE_KEY, concert);
    intent.putExtra("RemoconActivity", Constants.ACTIVITY_ROCON_REMOCON);
    intent.putExtra("Parameters", app.getParameters()); // YAML-formatted string

    // Remappings come as a messages list that make YAML parser crash, so we must digest if for him
    if ((app.getRemappings() != null) && (app.getRemappings().size() > 0)) {
      String remaps = "{";
      for (rocon_std_msgs.Remapping remap : app.getRemappings())
        remaps += remap.getRemapFrom() + ": " + remap.getRemapTo() + ", ";
      remaps = remaps.substring(0, remaps.length() - 2) + "}";
      intent.putExtra("Remappings", remaps);
    }

    //      intent.putExtra("runningNodes", runningNodes);
    //      intent.putExtra("PairedManagerActivity",
    // "com.github.rosjava.android_remocons.rocon_remocon.Remocon");

    try {
      Log.i("AppLaunch", "trying to start activity (action: " + appName + " )");
      parent.startActivity(intent);
      return Result.SUCCESS;
    } catch (ActivityNotFoundException e) {
      Log.i("AppLaunch", "activity not found for action: " + appName);
    }
    return Result.NOT_INSTALLED.withMsg("Android app not installed");
  }
  /** Launch a client web app for the given concert app. */
  private static Result launchWebApp(
      final Activity parent,
      final RoconDescription concert,
      final rocon_interaction_msgs.Interaction app) {
    try {
      // Validate the URL before starting anything
      String app_name = "";
      String app_type = "";

      // Parse the url
      if (checkAppType(app.getName()) == AppType.WEB_URL) {
        app_type = "web_url";
        app_name = app.getName().substring(app_type.length() + 1, app.getName().length() - 1);
      } else if (checkAppType(app.getName()) == AppType.WEB_APP) {
        app_type = "web_app";
        app_name = app.getName().substring(app_type.length() + 1, app.getName().length() - 1);
      } else {
        app_name = app.getName();
      }

      URL appURL = new URL(app_name);

      AsyncTask<URL, Void, String> asyncTask =
          new AsyncTask<URL, Void, String>() {
            @Override
            protected String doInBackground(URL... urls) {
              try {
                HttpURLConnection urlConnection = (HttpURLConnection) urls[0].openConnection();
                int unused_responseCode = urlConnection.getResponseCode();
                urlConnection.disconnect();
                return urlConnection.getResponseMessage();
              } catch (IOException e) {
                return e.getMessage();
              }
            }
          }.execute(appURL);
      String result = asyncTask.get(5, TimeUnit.SECONDS);
      if (result == null
          || (result.startsWith("OK") == false && result.startsWith("ok") == false)) {
        return Result.CANNOT_CONNECT.withMsg(result);
      }

      // We pass concert URL, parameters and remaps as URL parameters
      String appUriStr = app_name;
      String interaction_data = "{";
      // add remap
      String remaps = "\"remappings\": {";
      if ((app.getRemappings() != null) && (app.getRemappings().size() > 0)) {
        for (rocon_std_msgs.Remapping remap : app.getRemappings())
          remaps += "\"" + remap.getRemapFrom() + "\":\"" + remap.getRemapTo() + "\",";
        remaps = remaps.substring(0, remaps.length() - 1) + "}";
      } else {
        remaps += "}";
      }
      remaps += ",";
      interaction_data += remaps;

      // add displayname
      String displayname = "\"display_name\":";
      if ((app.getDisplayName() != null) && (app.getDisplayName().length() > 0)) {
        displayname += "\"" + app.getDisplayName() + "\"";
      }
      displayname += ",";
      interaction_data += displayname;

      // add parameters
      String parameters = "\"parameters\": {";
      if ((app.getParameters() != null) && (app.getParameters().length() > 0)) {
        Yaml yaml = new Yaml();
        Map<String, String> params = (Map<String, String>) yaml.load(app.getParameters());
        for (String key : params.keySet()) {
          parameters += "\"" + key + "\":\"" + String.valueOf(params.get(key)) + "" + "\",";
        }
        parameters = parameters.substring(0, parameters.length() - 1);
      }

      parameters += "}";
      interaction_data += parameters;
      interaction_data += "}";

      if (app_type.equals("web_url")) {
        appUriStr = appUriStr;
      } else if (app_type.equals("web_app")) {
        appUriStr = appUriStr + "?" + "interaction_data=" + URLEncoder.encode(interaction_data);
      } else {
        appUriStr = appUriStr + "?" + "interaction_data=" + URLEncoder.encode(interaction_data);
      }
      appURL.toURI(); // throws URISyntaxException if fails; probably a redundant check
      Uri appURI = Uri.parse(appUriStr);

      // Create an action view intent and pass rapp's name + extra information as URI
      Intent intent = new Intent(Intent.ACTION_VIEW, appURI);

      Log.i("AppLaunch", "trying to start web app (URI: " + appUriStr + ")");
      parent.startActivity(intent);
      return Result.SUCCESS;
    } catch (URISyntaxException e) {
      return Result.MALFORMED_URI.withMsg("Cannot convert URL into URI. " + e.getMessage());
    } catch (MalformedURLException e) {
      return Result.MALFORMED_URI.withMsg("App URL is not valid. " + e.getMessage());
    } catch (ActivityNotFoundException e) {
      // This cannot happen for a web site, right? must mean that I have no web browser!
      return Result.NOT_INSTALLED.withMsg("Activity not found for view action??? muoia???");
    } catch (TimeoutException e) {
      return Result.CONNECT_TIMEOUT.withMsg("Timeout waiting for app");
    } catch (Exception e) {
      return Result.OTHER_ERROR.withMsg(e.getMessage());
    }
  }