private void addSearchLayout(RelativeLayout layout, TiViewProxy searchView, TiUIView search) {
    RelativeLayout.LayoutParams p = createBasicSearchLayout();
    p.addRule(RelativeLayout.ALIGN_PARENT_TOP);

    TiDimension rawHeight;
    if (searchView.hasProperty(TiC.PROPERTY_HEIGHT)) {
      rawHeight = TiConvert.toTiDimension(searchView.getProperty(TiC.PROPERTY_HEIGHT), 0);
    } else {
      rawHeight = TiConvert.toTiDimension(MIN_SEARCH_HEIGHT, 0);
    }
    p.height = rawHeight.getAsPixels(layout);

    View nativeView = search.getNativeView();
    layout.addView(nativeView, p);

    p = createBasicSearchLayout();
    p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    p.addRule(RelativeLayout.BELOW, nativeView.getId());
    ViewParent parentWrapper = wrapper.getParent();
    if (parentWrapper != null && parentWrapper instanceof ViewGroup) {
      // get the previous layout params so we can reset with new layout
      ViewGroup.LayoutParams lp = wrapper.getLayoutParams();
      ViewGroup parentView = (ViewGroup) parentWrapper;
      // remove view from parent
      parentView.removeView(wrapper);
      // add new layout
      layout.addView(wrapper, p);
      parentView.addView(layout, lp);

    } else {
      layout.addView(wrapper, p);
    }
    this.searchLayout = layout;
  }
 private void setPageOffset(Object value) {
   if (verticalLayout) {
     TiCompositeLayout.LayoutParams params = (LayoutParams) mPager.getLayoutParams();
     params.optionHeight =
         TiConvert.toTiDimension(TiConvert.toString(value), TiDimension.TYPE_TOP);
     mPager.setLayoutParams(params);
   } else {
     TiCompositeLayout.LayoutParams params = (LayoutParams) mPager.getLayoutParams();
     params.optionWidth =
         TiConvert.toTiDimension(TiConvert.toString(value), TiDimension.TYPE_LEFT);
     mPager.setLayoutParams(params);
   }
 }
  @Override
  public void propertyChanged(String key, Object oldValue, Object newValue, KrollProxy proxy) {
    TiImageView view = getView();
    if (view == null) {
      return;
    }
    if (key.equals(TiC.PROPERTY_CAN_SCALE)) {
      view.setCanScaleImage(TiConvert.toBoolean(newValue));
    } else if (key.equals(TiC.PROPERTY_ENABLE_ZOOM_CONTROLS)) {
      view.setEnableZoomControls(TiConvert.toBoolean(newValue));
    } else if (key.equals(TiC.PROPERTY_URL)) {
      Log.w(TAG, "The url property of ImageView is deprecated, use image instead.");
      setImageSource(newValue);
      firedLoad = false;
      setImage(true);
    } else if (key.equals(TiC.PROPERTY_IMAGE)) {
      setImageSource(newValue);
      firedLoad = false;
      setImage(true);
    } else if (key.equals(TiC.PROPERTY_IMAGES)) {
      if (newValue instanceof Object[]) {
        setImageSource(newValue);
        setImages();
      }
    } else {
      // Update requestedWidth / requestedHeight when width / height is changed.
      if (key.equals(TiC.PROPERTY_WIDTH)) {
        View parentView = getParentView();
        if (TiC.LAYOUT_FILL.equals(TiConvert.toString(newValue)) && parentView != null) {
          // Use the parent's width when it's fill
          requestedWidth =
              TiConvert.toTiDimension(parentView.getMeasuredWidth(), TiDimension.TYPE_WIDTH);
        } else {
          requestedWidth = TiConvert.toTiDimension(newValue, TiDimension.TYPE_WIDTH);
        }
      } else if (key.equals(TiC.PROPERTY_HEIGHT)) {
        View parentView = getParentView();
        // Use the parent's height when it's fill
        if (TiC.LAYOUT_FILL.equals(TiConvert.toString(newValue)) && parentView != null) {
          requestedHeight =
              TiConvert.toTiDimension(parentView.getMeasuredHeight(), TiDimension.TYPE_HEIGHT);
        } else {
          requestedHeight = TiConvert.toTiDimension(newValue, TiDimension.TYPE_HEIGHT);
        }
      }

      super.propertyChanged(key, oldValue, newValue, proxy);
    }
  }
  private void setPageWidth(Object value) {
    if (verticalLayout) {
      TiCompositeLayout.LayoutParams params = (LayoutParams) mPager.getLayoutParams();
      params.optionHeight =
          TiConvert.toTiDimension(TiConvert.toString(value), TiDimension.TYPE_HEIGHT);
      mPager.setLayoutParams(params);
    } else {
      TiCompositeLayout.LayoutParams params = (LayoutParams) mPager.getLayoutParams();
      params.optionWidth =
          TiConvert.toTiDimension(TiConvert.toString(value), TiDimension.TYPE_WIDTH);
      mPager.setLayoutParams(params);
    }
    ((ViewGroup) nativeView).setClipChildren(false);

    hardwaredDisabled = true;
    disableHWAcceleration(nativeView); // we ll reenable it later because of a glitch
  }
  public TiGradientDrawable(View view, KrollDict properties) {
    super(new RectShape());

    // Determine which type of gradient is being used.
    // Supported types are 'linear' and 'radial'.
    String type = properties.optString("type", "linear");
    if (type.equals("linear")) {
      gradientType = GradientType.LINEAR_GRADIENT;
    } else if (type.equals("radial")) {
      gradientType = GradientType.RADIAL_GRADIENT;

      // TODO: Add support for radial gradients.
      // Android's RadialGradient only supports a single circle.
      // We need to figure out how to support two circle gradients
      // as specified by the HTML Canvas specification.
      return;

    } else {
      throw new IllegalArgumentException("Invalid gradient type. Must be linear or radial.");
    }

    // Load the 'startPoint' property which defines the start of the gradient.
    Object startPointObject = properties.get("startPoint");
    if (startPointObject instanceof HashMap) {
      startPoint = new TiPoint((HashMap) startPointObject, 0, 0);
    }

    // Load the 'endPoint' property which defines the end of the gradient.
    // Note: this is only used for linear gradient since Android does not
    // support an ending circle for radial gradients.
    Object endPointObject = properties.get("endPoint");
    if (endPointObject instanceof HashMap) {
      endPoint = new TiPoint((HashMap) endPointObject, 0, 1);
    }

    startRadius = TiConvert.toTiDimension(properties, "startRadius", TiDimension.TYPE_UNDEFINED);
    if (startRadius == null) {
      startRadius = DEFAULT_RADIUS;
    }

    Object colors = properties.get("colors");
    if (!(colors instanceof Object[])) {
      Log.w(LCAT, "Android does not support gradients without colors.");
      throw new IllegalArgumentException("Must provide an array of colors.");
    }
    loadColors((Object[]) colors);

    this.view = view;

    setShaderFactory(new GradientShaderFactory());
  }
  @Override
  public void processProperties(KrollDict d) {
    TiImageView view = getView();
    if (view == null) {
      return;
    }

    if (d.containsKey(TiC.PROPERTY_WIDTH)) {
      requestedWidth = TiConvert.toTiDimension(d, TiC.PROPERTY_WIDTH, TiDimension.TYPE_WIDTH);
    }
    if (d.containsKey(TiC.PROPERTY_HEIGHT)) {
      requestedHeight = TiConvert.toTiDimension(d, TiC.PROPERTY_HEIGHT, TiDimension.TYPE_HEIGHT);
    }

    if (d.containsKey(TiC.PROPERTY_IMAGES)) {
      setImageSource(d.get(TiC.PROPERTY_IMAGES));
      setImages();
    } else if (d.containsKey(TiC.PROPERTY_URL)) {
      Log.w(LCAT, "The url property of ImageView is deprecated, use image instead.");
      if (!d.containsKey(TiC.PROPERTY_IMAGE)) {
        d.put(TiC.PROPERTY_IMAGE, d.get(TiC.PROPERTY_URL));
      }
    }
    if (d.containsKey(TiC.PROPERTY_CAN_SCALE)) {
      view.setCanScaleImage(TiConvert.toBoolean(d, TiC.PROPERTY_CAN_SCALE));
    }
    if (d.containsKey(TiC.PROPERTY_ENABLE_ZOOM_CONTROLS)) {
      view.setEnableZoomControls(TiConvert.toBoolean(d, TiC.PROPERTY_ENABLE_ZOOM_CONTROLS));
    }
    if (d.containsKey(TiC.PROPERTY_DEFAULT_IMAGE)) {
      Object defaultImage = d.get(TiC.PROPERTY_DEFAULT_IMAGE);
      try {
        Object image = d.get(TiC.PROPERTY_IMAGE);

        if (image instanceof String) {
          String imageUrl = (String) image;
          URI imageUri = new URI(imageUrl);
          if (URLUtil.isNetworkUrl(imageUrl) && !TiResponseCache.peek(imageUri)) {
            setDefaultImageSource(defaultImage);
          }

        } else if (image == null) {
          setDefaultImageSource(defaultImage);
        }

      } catch (URISyntaxException e) {
        setDefaultImageSource(defaultImage);
      }
    }
    if (d.containsKey(TiC.PROPERTY_IMAGE)) {
      // processProperties is also called from TableView, we need check if we changed before
      // re-creating the
      // bitmap
      boolean changeImage = true;
      Object newImage = d.get(TiC.PROPERTY_IMAGE);
      TiDrawableReference source = makeImageSource(newImage);
      if (imageSources != null && imageSources.size() == 1) {
        if (imageSources.get(0).equals(source)) {
          changeImage = false;
        }
      }
      if (changeImage) {
        setImageSource(source);
        firedLoad = false;
        setImage(false);
      }

    } else {
      if (!d.containsKey(TiC.PROPERTY_IMAGES)) {
        getProxy().setProperty(TiC.PROPERTY_IMAGE, null);
        if (defaultImageSource != null) {
          setDefaultImage();
        }
      }
    }

    super.processProperties(d);
  }
  @Override
  public void processProperties(KrollDict d) {
    TiImageView view = getView();
    View parentView = getParentView();

    if (view == null) {
      return;
    }

    if (d.containsKey(TiC.PROPERTY_WIDTH)) {
      if (TiC.LAYOUT_FILL.equals(d.getString(TiC.PROPERTY_WIDTH)) && parentView != null) {
        // Use the parent's width when it's fill
        requestedWidth =
            TiConvert.toTiDimension(parentView.getMeasuredWidth(), TiDimension.TYPE_WIDTH);
      } else {
        requestedWidth = TiConvert.toTiDimension(d, TiC.PROPERTY_WIDTH, TiDimension.TYPE_WIDTH);
      }
    }
    if (d.containsKey(TiC.PROPERTY_HEIGHT)) {
      // Use the parent's height when it's fill
      if (TiC.LAYOUT_FILL.equals(d.getString(TiC.PROPERTY_HEIGHT)) && parentView != null) {
        requestedHeight =
            TiConvert.toTiDimension(parentView.getMeasuredHeight(), TiDimension.TYPE_HEIGHT);
      } else {
        requestedHeight = TiConvert.toTiDimension(d, TiC.PROPERTY_HEIGHT, TiDimension.TYPE_HEIGHT);
      }
    }

    if (d.containsKey(TiC.PROPERTY_IMAGES)) {
      setImageSource(d.get(TiC.PROPERTY_IMAGES));
      setImages();
    } else if (d.containsKey(TiC.PROPERTY_URL)) {
      Log.w(TAG, "The url property of ImageView is deprecated, use image instead.");
      if (!d.containsKey(TiC.PROPERTY_IMAGE)) {
        d.put(TiC.PROPERTY_IMAGE, d.get(TiC.PROPERTY_URL));
      }
    }
    if (d.containsKey(TiC.PROPERTY_CAN_SCALE)) {
      view.setCanScaleImage(TiConvert.toBoolean(d, TiC.PROPERTY_CAN_SCALE));
    }
    if (d.containsKey(TiC.PROPERTY_ENABLE_ZOOM_CONTROLS)) {
      view.setEnableZoomControls(TiConvert.toBoolean(d, TiC.PROPERTY_ENABLE_ZOOM_CONTROLS));
    }
    if (d.containsKey(TiC.PROPERTY_DEFAULT_IMAGE)) {
      Object defaultImage = d.get(TiC.PROPERTY_DEFAULT_IMAGE);
      try {
        Object image = d.get(TiC.PROPERTY_IMAGE);

        if (image instanceof String) {
          String imageUrl = TiUrl.getCleanUri((String) image).toString();
          URI imageUri = new URI(imageUrl);
          if (URLUtil.isNetworkUrl(imageUrl) && !TiResponseCache.peek(imageUri)) {
            setDefaultImageSource(defaultImage);
          }

        } else if (image == null) {
          setDefaultImageSource(defaultImage);
        }

      } catch (URISyntaxException e) {
        setDefaultImageSource(defaultImage);
      } catch (NullPointerException e) {
        setDefaultImageSource(defaultImage);
      }
    }
    if (d.containsKey(TiC.PROPERTY_IMAGE)) {
      // processProperties is also called from TableView, we need check if we changed before
      // re-creating the
      // bitmap
      boolean changeImage = true;
      Object newImage = d.get(TiC.PROPERTY_IMAGE);
      TiDrawableReference source = makeImageSource(newImage);

      // Check for orientation only if they specified an image
      if (d.containsKey(TiC.PROPERTY_AUTOROTATE)) {
        source.setAutoRotate(d.getBoolean(TiC.PROPERTY_AUTOROTATE));
      }

      if (imageSources != null && imageSources.size() == 1) {
        if (imageSources.get(0).equals(source)) {
          changeImage = false;
        }
      }

      if (changeImage) {
        setImageSource(source);
        firedLoad = false;
        setImage(false);
      }

    } else {
      if (!d.containsKey(TiC.PROPERTY_IMAGES)) {
        getProxy().setProperty(TiC.PROPERTY_IMAGE, null);
        if (defaultImageSource != null) {
          setDefaultImage();
        }
      }
    }

    super.processProperties(d);
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int w = MeasureSpec.getSize(widthMeasureSpec);
    int wMode = MeasureSpec.getMode(widthMeasureSpec);
    int h = MeasureSpec.getSize(heightMeasureSpec);
    int hMode = MeasureSpec.getMode(heightMeasureSpec);
    int imageHMargin = 0;

    int leftImageWidth = 0;
    int leftImageHeight = 0;
    if (leftImage != null && leftImage.getVisibility() != View.GONE) {
      measureChild(leftImage, widthMeasureSpec, heightMeasureSpec);
      leftImageWidth = leftImage.getMeasuredWidth();
      leftImageHeight = leftImage.getMeasuredHeight();
      imageHMargin += LEFT_MARGIN;
    }

    int rightImageWidth = 0;
    int rightImageHeight = 0;
    if (rightImage != null && rightImage.getVisibility() != View.GONE) {
      measureChild(rightImage, widthMeasureSpec, heightMeasureSpec);
      rightImageWidth = rightImage.getMeasuredWidth();
      rightImageHeight = rightImage.getMeasuredHeight();
      imageHMargin += RIGHT_MARGIN;
    }

    int adjustedWidth = w - leftImageWidth - rightImageWidth - imageHMargin;
    // int adjustedWidth = w;

    if (content != null) {
      measureChild(content, MeasureSpec.makeMeasureSpec(adjustedWidth, wMode), heightMeasureSpec);
      if (hMode == MeasureSpec.UNSPECIFIED) {
        TableViewProxy table = ((TableViewRowProxy) item.proxy).getTable();
        int minRowHeight = -1;
        if (table != null && table.hasProperty(TiC.PROPERTY_MIN_ROW_HEIGHT)) {
          minRowHeight =
              TiConvert.toTiDimension(
                      TiConvert.toString(table.getProperty(TiC.PROPERTY_MIN_ROW_HEIGHT)),
                      TiDimension.TYPE_HEIGHT)
                  .getAsPixels(this);
        }

        if (height == null) {
          h =
              Math.max(
                  h,
                  Math.max(
                      content.getMeasuredHeight(), Math.max(leftImageHeight, rightImageHeight)));
          h = Math.max(h, minRowHeight);
        } else {
          h = Math.max(minRowHeight, height.getAsPixels(this));
        }
        if (DBG) {
          Log.d(LCAT, "Row content measure (" + adjustedWidth + "x" + h + ")");
        }
        measureChild(
            content,
            MeasureSpec.makeMeasureSpec(adjustedWidth, wMode),
            MeasureSpec.makeMeasureSpec(h, hMode));
      }
    }

    setMeasuredDimension(w, Math.max(h, Math.max(leftImageHeight, rightImageHeight)));
  }
  public void setRowData(TableViewRowProxy rp) {
    KrollDict props = rp.getProperties();
    hasControls = rp.hasControls();

    Object newSelectorSource = null;
    if (rp.hasProperty(TiC.PROPERTY_BACKGROUND_SELECTED_IMAGE)) {
      newSelectorSource = rp.getProperty(TiC.PROPERTY_BACKGROUND_SELECTED_IMAGE);
    } else if (rp.hasProperty(TiC.PROPERTY_BACKGROUND_SELECTED_COLOR)) {
      newSelectorSource = rp.getProperty(TiC.PROPERTY_BACKGROUND_SELECTED_COLOR);
    }
    if (newSelectorSource == null
        || selectorSource != null && !selectorSource.equals(newSelectorSource)) {
      selectorDrawable = null;
    }
    selectorSource = newSelectorSource;
    if (selectorSource != null) {
      rp.getTable().getTableView().getTableView().enableCustomSelector();
    }

    setBackgroundFromProperties(props);
    // Handle right image
    boolean clearRightImage = true;
    // It's one or the other, check or child.  If you set them both, child's gonna win.
    if (props.containsKey(TiC.PROPERTY_HAS_CHECK)) {
      if (TiConvert.toBoolean(props, TiC.PROPERTY_HAS_CHECK)) {
        if (hasCheckDrawable == null) {
          hasCheckDrawable = createHasCheckDrawable();
        }
        rightImage.setImageDrawable(hasCheckDrawable);
        rightImage.setVisibility(VISIBLE);
        clearRightImage = false;
      }
    }
    if (props.containsKey(TiC.PROPERTY_HAS_CHILD)) {
      if (TiConvert.toBoolean(props, TiC.PROPERTY_HAS_CHILD)) {
        if (hasChildDrawable == null) {
          hasChildDrawable = createHasChildDrawable();
        }
        rightImage.setImageDrawable(hasChildDrawable);
        rightImage.setVisibility(VISIBLE);
        clearRightImage = false;
      }
    }
    if (props.containsKey(TiC.PROPERTY_RIGHT_IMAGE)) {
      String path = TiConvert.toString(props, TiC.PROPERTY_RIGHT_IMAGE);
      String url = tiContext.resolveUrl(null, path);
      Drawable d = loadDrawable(url);
      if (d != null) {
        rightImage.setImageDrawable(d);
        rightImage.setVisibility(VISIBLE);
        clearRightImage = false;
      }
    }

    if (clearRightImage) {
      rightImage.setImageDrawable(null);
      rightImage.setVisibility(GONE);
    }

    // Handle left image
    if (props.containsKey(TiC.PROPERTY_LEFT_IMAGE)) {
      String path = TiConvert.toString(props, TiC.PROPERTY_LEFT_IMAGE);
      String url = tiContext.resolveUrl(null, path);

      Drawable d = loadDrawable(url);
      if (d != null) {
        leftImage.setImageDrawable(d);
        leftImage.setVisibility(VISIBLE);
      }
    } else {
      leftImage.setImageDrawable(null);
      leftImage.setVisibility(GONE);
    }

    if (props.containsKey(TiC.PROPERTY_HEIGHT)) {
      if (!props.get(TiC.PROPERTY_HEIGHT).equals(TiC.SIZE_AUTO)) {
        height =
            TiConvert.toTiDimension(
                TiConvert.toString(props, TiC.PROPERTY_HEIGHT), TiDimension.TYPE_HEIGHT);
      }
    }

    if (props.containsKey(TiC.PROPERTY_LAYOUT)) {
      content.setLayoutArrangement(TiConvert.toString(props, TiC.PROPERTY_LAYOUT));
    }

    if (rp.hasControls()) {
      refreshControls();
    } else {
      refreshOldStyleRow();
    }
  }
  public void propertyChanged(String key, Object oldValue, Object newValue, KrollProxy proxy) {
    if (key.equals(TiC.PROPERTY_LEFT)) {
      if (newValue != null) {
        layoutParams.optionLeft =
            TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_LEFT);
      } else {
        layoutParams.optionLeft = null;
      }
      layoutNativeView();
    } else if (key.equals(TiC.PROPERTY_TOP)) {
      if (newValue != null) {
        layoutParams.optionTop =
            TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_TOP);
      } else {
        layoutParams.optionTop = null;
      }
      layoutNativeView();
    } else if (key.equals(TiC.PROPERTY_CENTER)) {
      TiConvert.updateLayoutCenter(newValue, layoutParams);
      layoutNativeView();
    } else if (key.equals(TiC.PROPERTY_RIGHT)) {
      if (newValue != null) {
        layoutParams.optionRight =
            TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_RIGHT);
      } else {
        layoutParams.optionRight = null;
      }
      layoutNativeView();
    } else if (key.equals(TiC.PROPERTY_BOTTOM)) {
      if (newValue != null) {
        layoutParams.optionBottom =
            TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_BOTTOM);
      } else {
        layoutParams.optionBottom = null;
      }
      layoutNativeView();
    } else if (key.equals(TiC.PROPERTY_SIZE)) {
      if (newValue instanceof HashMap) {
        HashMap<String, Object> d = (HashMap) newValue;
        propertyChanged(TiC.PROPERTY_WIDTH, oldValue, d.get(TiC.PROPERTY_WIDTH), proxy);
        propertyChanged(TiC.PROPERTY_HEIGHT, oldValue, d.get(TiC.PROPERTY_HEIGHT), proxy);
      } else if (newValue != null) {
        Log.w(
            LCAT,
            "Unsupported property type ("
                + (newValue.getClass().getSimpleName())
                + ") for key: "
                + key
                + ". Must be an object/dictionary");
      }
    } else if (key.equals(TiC.PROPERTY_HEIGHT)) {
      if (newValue != null) {
        if (!newValue.equals(TiC.SIZE_AUTO)) {
          layoutParams.optionHeight =
              TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_HEIGHT);
          layoutParams.autoHeight = false;
        } else {
          layoutParams.optionHeight = null;
          layoutParams.autoHeight = true;
        }
      } else {
        layoutParams.optionHeight = null;
      }
      layoutNativeView();
    } else if (key.equals(TiC.PROPERTY_WIDTH)) {
      if (newValue != null) {
        if (!newValue.equals(TiC.SIZE_AUTO)) {
          layoutParams.optionWidth =
              TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_WIDTH);
          layoutParams.autoWidth = false;
        } else {
          layoutParams.optionWidth = null;
          layoutParams.autoWidth = true;
        }
      } else {
        layoutParams.optionWidth = null;
      }
      layoutNativeView();
    } else if (key.equals(TiC.PROPERTY_ZINDEX)) {
      if (newValue != null) {
        layoutParams.optionZIndex = TiConvert.toInt(newValue);
      } else {
        layoutParams.optionZIndex = 0;
      }
      layoutNativeView(true);
    } else if (key.equals(TiC.PROPERTY_FOCUSABLE)) {
      boolean focusable = TiConvert.toBoolean(proxy.getProperty(TiC.PROPERTY_FOCUSABLE));
      nativeView.setFocusable(focusable);
      if (focusable) {
        registerForKeyClick(nativeView);
      } else {
        // nativeView.setOnClickListener(null); // ? mistake? I assume OnKeyListener was meant
        nativeView.setOnKeyListener(null);
      }
    } else if (key.equals(TiC.PROPERTY_TOUCH_ENABLED)) {
      doSetClickable(TiConvert.toBoolean(newValue));
    } else if (key.equals(TiC.PROPERTY_VISIBLE)) {
      nativeView.setVisibility(TiConvert.toBoolean(newValue) ? View.VISIBLE : View.INVISIBLE);
    } else if (key.equals(TiC.PROPERTY_ENABLED)) {
      nativeView.setEnabled(TiConvert.toBoolean(newValue));
    } else if (key.startsWith(TiC.PROPERTY_BACKGROUND_PADDING)) {
      Log.i(LCAT, key + " not yet implemented.");
    } else if (key.equals(TiC.PROPERTY_OPACITY)
        || key.startsWith(TiC.PROPERTY_BACKGROUND_PREFIX)
        || key.startsWith(TiC.PROPERTY_BORDER_PREFIX)) {
      // Update first before querying.
      proxy.setProperty(key, newValue);

      KrollDict d = proxy.getProperties();

      boolean hasImage = hasImage(d);
      boolean hasColorState = hasColorState(d);
      boolean hasBorder = hasBorder(d);

      boolean requiresCustomBackground = hasImage || hasColorState || hasBorder;

      if (!requiresCustomBackground) {
        if (background != null) {
          background.releaseDelegate();
          background.setCallback(null);
          background = null;
        }

        if (d.containsKeyAndNotNull(TiC.PROPERTY_BACKGROUND_COLOR)) {
          Integer bgColor = TiConvert.toColor(d, TiC.PROPERTY_BACKGROUND_COLOR);
          if (nativeView != null) {
            nativeView.setBackgroundColor(bgColor);
            nativeView.postInvalidate();
          }
        } else {
          if (key.equals(TiC.PROPERTY_OPACITY)) {
            setOpacity(TiConvert.toFloat(newValue));
          }
          if (nativeView != null) {
            nativeView.setBackgroundDrawable(null);
            nativeView.postInvalidate();
          }
        }
      } else {
        boolean newBackground = background == null;
        if (newBackground) {
          background = new TiBackgroundDrawable();
        }

        Integer bgColor = null;

        if (!hasColorState) {
          if (d.get(TiC.PROPERTY_BACKGROUND_COLOR) != null) {
            bgColor = TiConvert.toColor(d, TiC.PROPERTY_BACKGROUND_COLOR);
            if (newBackground
                || (key.equals(TiC.PROPERTY_OPACITY)
                    || key.equals(TiC.PROPERTY_BACKGROUND_COLOR))) {
              background.setBackgroundColor(bgColor);
            }
          }
        }

        if (hasImage || hasColorState) {
          if (newBackground || key.startsWith(TiC.PROPERTY_BACKGROUND_PREFIX)) {
            handleBackgroundImage(d);
          }
        }

        if (hasBorder) {
          if (newBackground) {
            initializeBorder(d, bgColor);
          } else if (key.startsWith(TiC.PROPERTY_BORDER_PREFIX)) {
            handleBorderProperty(key, newValue);
          }
        }
        applyCustomBackground();
      }
      if (nativeView != null) {
        nativeView.postInvalidate();
      }
    } else if (key.equals(TiC.PROPERTY_SOFT_KEYBOARD_ON_FOCUS)) {
      Log.w(
          LCAT,
          "Focus state changed to "
              + TiConvert.toString(newValue)
              + " not honored until next focus event.");
    } else if (key.equals(TiC.PROPERTY_TRANSFORM)) {
      if (nativeView != null) {
        applyTransform((Ti2DMatrix) newValue);
      }
    } else if (key.equals(TiC.PROPERTY_KEEP_SCREEN_ON)) {
      if (nativeView != null) {
        nativeView.setKeepScreenOn(TiConvert.toBoolean(newValue));
      }
    } else {
      TiViewProxy viewProxy = getProxy();
      if (viewProxy != null && viewProxy.isLocalizedTextId(key)) {
        viewProxy.setLocalizedText(key, TiConvert.toString(newValue));
      } else {
        if (DBG) {
          Log.d(LCAT, "Unhandled property key: " + key);
        }
      }
    }
  }
  public TiBubbleAndroidView(TiViewProxy proxy) {
    super(proxy);

    prop = proxy.getProperties();

    LayoutArrangement arrangement = LayoutArrangement.DEFAULT;

    if (proxy.hasProperty(TiC.PROPERTY_LAYOUT)) {
      String layoutProperty = TiConvert.toString(proxy.getProperty(TiC.PROPERTY_LAYOUT));
      if (layoutProperty.equals(TiC.LAYOUT_HORIZONTAL)) {
        arrangement = LayoutArrangement.HORIZONTAL;
      } else if (layoutProperty.equals(TiC.LAYOUT_VERTICAL)) {
        arrangement = LayoutArrangement.VERTICAL;
      }
    }

    TiCompositeLayout nativeView = new TiCompositeLayout(proxy.getActivity(), arrangement);
    setNativeView(nativeView);

    if (nativeBubbleView == null) {
      Activity currentActivity = proxy.getActivity();
      if (currentActivity == null) {
        currentActivity = TiApplication.getAppCurrentActivity();
      }
      nativeBubbleView = new NativeBubbleView(currentActivity);

      LayoutParams params = new LayoutParams();
      params.height = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;
      params.width = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;

      ViewGroup savedParent = null;
      int childIndex = -1;

      TiUIView parentView = proxy.getParent().getOrCreateView();
      View parentNativeView = (View) parentView.getNativeView();

      if (parentNativeView != null) {
        ViewParent nativeParent = (ViewParent) parentNativeView;

        if (nativeParent instanceof ViewGroup) {
          savedParent = (ViewGroup) nativeParent;
          childIndex = savedParent.indexOfChild(nativeView);
          savedParent.removeView(nativeView);
        }
      }

      nativeBubbleView.addView(nativeView, params);

      if (savedParent != null) {
        savedParent.addView(nativeBubbleView, childIndex, getLayoutParams());
      }

      if (prop.containsKey("bubbleRadius")) {
        float radius = 0;
        TiDimension radiusDim =
            TiConvert.toTiDimension(prop.get("bubbleRadius"), TiDimension.TYPE_WIDTH);
        if (radiusDim != null) {
          radius = (float) radiusDim.getPixels(getNativeView());
        }
        nativeBubbleView.setBubbleRadius(radius);
      }

      if (prop.containsKey("bubbleColor")) {
        nativeBubbleView.setBubbleColor(TiConvert.toColor(prop, "bubbleColor"));
      } else {
        Integer bgColor = TiConvert.toColor(prop, TiC.PROPERTY_BACKGROUND_COLOR);
        if (bgColor != null) {
          nativeBubbleView.setBubbleColor(bgColor);
        }
      }

      if (prop.containsKey("bubbleBeak")) {
        nativeBubbleView.setBubbleBeak(TiConvert.toInt(prop, "bubbleBeak"));
      }

      if (prop.containsKey("bubbleBeakVertical")) {
        nativeBubbleView.setBubbleBeakVertical(TiConvert.toInt(prop, "bubbleBeakVertical"));
      }

      parentView.remove(this);
      parentView.add(this);
    }
  }
  @Override
  public void processProperties(KrollDict d) {
    tableView = new TiTableView(proxy.getTiContext(), (TableViewProxy) proxy);
    proxy.getTiContext().addOnLifecycleEventListener(this);

    tableView.setOnItemClickListener(this);
    tableView.setOnItemLongClickListener(this);

    if (d.containsKey(TiC.PROPERTY_SEARCH)) {
      RelativeLayout layout = new RelativeLayout(proxy.getTiContext().getActivity());
      layout.setGravity(Gravity.NO_GRAVITY);
      layout.setPadding(0, 0, 0, 0);

      TiViewProxy searchView = (TiViewProxy) d.get(TiC.PROPERTY_SEARCH);
      TiUISearchBar searchBar =
          (TiUISearchBar) searchView.getView(proxy.getTiContext().getActivity());
      searchBar.setOnSearchChangeListener(tableView);
      searchBar.getNativeView().setId(102);

      RelativeLayout.LayoutParams p =
          new RelativeLayout.LayoutParams(
              RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
      p.addRule(RelativeLayout.ALIGN_PARENT_TOP);
      p.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
      p.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);

      TiDimension rawHeight;
      if (searchView.hasProperty("height")) {
        rawHeight = TiConvert.toTiDimension(searchView.getProperty("height"), 0);
      } else {
        rawHeight = TiConvert.toTiDimension("52dp", 0);
      }
      p.height = rawHeight.getAsPixels(layout);

      layout.addView(searchBar.getNativeView(), p);

      p =
          new RelativeLayout.LayoutParams(
              RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
      p.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
      p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
      p.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
      p.addRule(RelativeLayout.BELOW, 102);
      layout.addView(tableView, p);
      setNativeView(layout);
    } else {
      setNativeView(tableView);
    }

    if (d.containsKey(TiC.PROPERTY_FILTER_ATTRIBUTE)) {
      tableView.setFilterAttribute(TiConvert.toString(d, TiC.PROPERTY_FILTER_ATTRIBUTE));
    } else {
      // Default to title to match iPhone default.
      proxy.setProperty(TiC.PROPERTY_FILTER_ATTRIBUTE, TiC.PROPERTY_TITLE, false);
      tableView.setFilterAttribute(TiC.PROPERTY_TITLE);
    }

    boolean filterCaseInsensitive = true;
    if (d.containsKey(TiC.PROPERTY_FILTER_CASE_INSENSITIVE)) {
      filterCaseInsensitive = TiConvert.toBoolean(d, TiC.PROPERTY_FILTER_CASE_INSENSITIVE);
    }
    tableView.setFilterCaseInsensitive(filterCaseInsensitive);
    super.processProperties(d);
  }