public void send(Object userData) throws MethodNotSupportedException {
    // TODO consider using task manager
    final TiHTTPClient me = this;
    double totalLength = 0;
    needMultipart = false;

    if (userData != null) {
      if (userData instanceof TiDict) {
        TiDict data = (TiDict) userData;

        // first time through check if we need multipart for POST
        for (String key : data.keySet()) {
          Object value = data.get(key);
          if (value instanceof TiBaseFile || value instanceof TiBlob) {
            needMultipart = true;
          }
        }

        for (String key : data.keySet()) {
          Object value = data.get(key);

          if (method.equals("POST") || method.equals("PUT")) {
            if (value instanceof TiBaseFile || value instanceof TiBlob) {
              totalLength += addTitaniumFileAsPostData(key, value);
            } else {
              String str = TiConvert.toString(value);
              addPostData(key, str);
              totalLength += str.length();
            }
          } else if (method.equals("GET")) {
            uri = uri.buildUpon().appendQueryParameter(key, TiConvert.toString(value)).build();
          }
        }
      } else {
        addStringData(TiConvert.toString(userData));
      }
    }

    request = new DefaultHttpRequestFactory().newHttpRequest(method, uri.toString());
    for (String header : headers.keySet()) {
      request.setHeader(header, headers.get(header));
    }

    clientThread =
        new Thread(
            new ClientRunnable(totalLength),
            "TiHttpClient-" + httpClientThreadCounter.incrementAndGet());
    clientThread.setPriority(Thread.MIN_PRIORITY);
    clientThread.start();
    if (DBG) {
      Log.d(LCAT, "Leaving send()");
    }
  }
  @Override
  public void processProperties(TiDict d) {
    TiImageView view = getView();

    if (d.containsKey("images")) {
      Object o = d.get("images");
      if (o instanceof Object[]) {
        setImages((Object[]) o);
      }
    } else if (d.containsKey("url")) {
      Log.w(LCAT, "The url property of ImageView is deprecated, use image instead.");
      if (!d.containsKey("image")) {
        d.put("image", d.get("url"));
      }
    }
    if (d.containsKey("canScale")) {
      view.setCanScaleImage(TiConvert.toBoolean(d, "canScale"));
    }
    if (d.containsKey("enableZoomControls")) {
      view.setEnableZoomControls(TiConvert.toBoolean(d, "enableZoomControls"));
    }
    if (d.containsKey("image")) {
      Object image = d.get("image");
      if (image instanceof String) {
        String imageURL = TiConvert.toString(d, "image");
        if (URLUtil.isNetworkUrl(imageURL)) {
          synchronized (imageTokenGenerator) {
            token = imageTokenGenerator.incrementAndGet();
            getView().setImageDrawable(null);
            new BgImageLoader(getProxy().getTiContext(), null, null, token).load(imageURL);
          }
        } else {
          setImage(createBitmap(imageURL));
        }
      } else {
        setImage(createBitmap(image));
      }

    } else {
      getProxy().internalSetDynamicValue("image", null, false);
    }

    super.processProperties(d);
  }
  @Override
  public Object get(String name, Scriptable start) {
    if (this.has(name, start)) {
      return super.get(name, start);
    }
    if (target instanceof TiProxy) {
      TiDict constants = ((TiProxy) target).getConstants();
      if (constants != null) {
        if (constants.containsKey(name)) {
          Object value = constants.get(name);
          super.putConst(name, start, value);
          return value;
        }
      }
    }

    if (DBG) {
      Log.d(LCAT, "get name: " + name);
    }
    Object o = NOT_FOUND;

    // If starts with Capital letter see if there is a module for it.
    if (name.matches("^[A-Z].*") || name.matches("iPhone")) {
      Object p = loadModule(name);
      if (p != null) {
        o = new KrollObject(this, p);
        put(name, this, o);
        ((TiModule) p).postCreate();
      }
    } else {
      if (DBG) {
        Log.d(LCAT, "Start: " + start.getClassName() + " looking for method:" + name);
      }
      o = handleMethodOrProperty(name, start, true, null);
    }

    return o;
  }
  @SuppressWarnings("serial")
  public static Object fromNative(Object value, KrollContext kroll) {
    Object o = value;

    if (DBG) {
      if (value != null) {
        Log.d(LCAT, "Incoming type is " + value.getClass().getCanonicalName());
      }
    }
    if (value == null
        || value instanceof String
        || value instanceof Number
        || value instanceof Boolean
        || value instanceof Scriptable
        || value instanceof Function) {
      o = Context.javaToJS(value, kroll.getScope());
    } else if (value instanceof TiDict) {
      TiDict d = (TiDict) value;
      ScriptableObject so =
          new ScriptableObject(
              kroll.getScope(), ScriptableObject.getObjectPrototype(kroll.getScope())) {
            @Override
            public String getClassName() {
              return "Object";
            }

            public String toString() {
              StringBuilder sb = new StringBuilder();
              sb.append("{ ");

              Object[] ids = (Object[]) getIds();
              String sep = "";

              if (ids != null) {
                for (Object id : ids) {
                  sb.append(" '").append(id).append("' : ");
                  Object o = get(id.toString(), this);
                  if (o == null) {
                    sb.append("null");
                  } else if (o instanceof String) {
                    sb.append(" '").append((String) o).append("' ");
                  } else if (o instanceof Number) {
                    sb.append(o);
                  } else if (o instanceof ScriptableObject) {
                    sb.append(" {").append(o).append("} ");
                  } else {
                    sb.append(o);
                  }

                  sb.append("sep");
                  sep = ",";
                }
              }

              sb.append(" }");

              return sb.toString();
            }
          };
      for (String key : d.keySet()) {
        so.put(key, so, fromNative(d.get(key), kroll));
      }
      o = so;
    } else if (value instanceof Date) {
      Date date = (Date) value;
      o =
          Context.getCurrentContext()
              .newObject(kroll.getScope(), "Date", new Object[] {date.getTime()});
    } else if (value.getClass().isArray()) {
      int length = Array.getLength(value);
      Object[] jsArray = new Object[length];
      for (int i = 0; i < length; i++) {
        jsArray[i] = fromNative(Array.get(value, i), kroll);
      }

      o = Context.getCurrentContext().newObject(kroll.getScope(), "Array", jsArray);
    } else {
      o = new KrollObject(kroll, value);
    }

    return o;
  }
  @Override
  public void processProperties(TiDict d) {
    super.processProperties(d);

    if (d.containsKey("enabled")) {
      tv.setEnabled(d.getBoolean("enabled"));
    }
    if (d.containsKey("value")) {
      tv.setText(d.getString("value"));
    }
    if (d.containsKey("color")) {
      tv.setTextColor(TiConvert.toColor(d, "color"));
    }
    if (d.containsKey("hintText")) {
      tv.setHint(d.getString("hintText"));
    }
    if (d.containsKey("font")) {
      TiUIHelper.styleText(tv, d.getTiDict("font"));
    }
    if (d.containsKey("textAlign") || d.containsKey("verticalAlign")) {
      String textAlign = null;
      String verticalAlign = null;
      if (d.containsKey("textAlign")) {
        textAlign = d.getString("textAlign");
      }
      if (d.containsKey("verticalAlign")) {
        verticalAlign = d.getString("verticalAlign");
      }
      handleTextAlign(textAlign, verticalAlign);
    }
    if (d.containsKey("returnKeyType")) {
      handleReturnKeyType(d.getInt("returnKeyType"));
    }
    if (d.containsKey("keyboardType")) {
      boolean autocorrect = true;
      if (d.containsKey("autocorrect")) {
        autocorrect = d.getBoolean("autocorrect");
      }
      handleKeyboardType(d.getInt("keyboardType"), autocorrect);
    }

    if (d.containsKey("autocapitalization")) {

      Capitalize autoCapValue = null;

      switch (d.getInt("autocapitalization")) {
        case TEXT_AUTOCAPITALIZATION_NONE:
          autoCapValue = Capitalize.NONE;
          break;

        case TEXT_AUTOCAPITALIZATION_ALL:
          autoCapValue = Capitalize.CHARACTERS;
          break;

        case TEXT_AUTOCAPITALIZATION_SENTENCES:
          autoCapValue = Capitalize.SENTENCES;
          break;

        case TEXT_AUTOCAPITALIZATION_WORDS:
          autoCapValue = Capitalize.WORDS;
          break;

        default:
          Log.w(
              LCAT, "Unknown AutoCapitalization Value [" + d.getString("autocapitalization") + "]");
          break;
      }

      if (null != autoCapValue) {
        tv.setKeyListener(TextKeyListener.getInstance(false, autoCapValue));
      }
    }

    if (d.containsKey("passwordMask")) {
      if (TiConvert.toBoolean(d.get("passwordMask"))) {
        // This shouldn't be needed but it's belts & braces
        tv.setKeyListener(TextKeyListener.getInstance(false, Capitalize.NONE));
        // Both setTransform & keyboard type are required
        tv.setTransformationMethod(PasswordTransformationMethod.getInstance());
        // We also need to set the keyboard type - otherwise the password mask won't be applied
        handleKeyboardType(KEYBOARD_PASSWORD, false);
      }
    }
  }