override fun generateFor()

in artist-traits/src/main/kotlin/com/uber/artist/traits/JavaForegroundTrait.kt [46:322]


  override fun generateFor(
      type: TypeSpec.Builder,
      initMethod: MethodSpec.Builder,
      rClass: ClassName,
      sourceType: String) {

    val isLayout = sourceType.endsWith("Layout")

    // The field
    type.addField(TypeNames.Android.Drawable, "foreground", Modifier.PRIVATE)

    if (isLayout) {
      type.addField(FieldSpec.builder(TypeNames.Android.Rect, "selfBounds", Modifier.PRIVATE, Modifier.FINAL)
          .initializer("new \$T()", TypeNames.Android.Rect)
          .build())
      type.addField(FieldSpec.builder(TypeNames.Android.Rect, "overlayBounds", Modifier.PRIVATE, Modifier.FINAL)
          .initializer("new \$T()", TypeNames.Android.Rect)
          .build())
      type.addField(FieldSpec.builder(TypeName.BOOLEAN, "foregroundInPadding", Modifier.PRIVATE)
          .initializer("true")
          .build())
      type.addField(FieldSpec.builder(TypeName.BOOLEAN, "foregroundBoundsChanged", Modifier.PRIVATE)
          .initializer("false")
          .build())
      type.addField(FieldSpec.builder(TypeName.INT, "foregroundGravity", Modifier.PRIVATE)
          .initializer("\$T.FILL", TypeNames.Android.Gravity)
          .build())
    }

    // Pull out the value
    initMethod.addStatement(
        "\$T foregroundTA = context.obtainStyledAttributes(attrs, \$T.styleable.ForegroundView)",
        TypeNames.Android.TypedArray,
        rClass)
    initMethod.addStatement(
        "final \$T localForeground = foregroundTA.getDrawable(\$T.styleable.ForegroundView_android_foreground)",
        TypeNames.Android.Drawable,
        rClass)

    initMethod.beginControlFlow("if (localForeground != null)")
    initMethod.addCode("//noinspection AndroidLintNewApi\n")
    initMethod.addStatement("setForeground(localForeground)")
    initMethod.endControlFlow()

    if (isLayout) {
      initMethod.addStatement(
          "foregroundGravity = foregroundTA.getInt(\$T.styleable.ForegroundView_android_foregroundGravity, " +
              "foregroundGravity)", rClass)
      initMethod.addStatement(
          "foregroundInPadding = foregroundTA.getBoolean(" +
              "\$T.styleable.ForegroundView_foregroundInsidePadding, " +
              "true)", rClass)
    }

    initMethod.addStatement("foregroundTA.recycle()")

    val onSizeChangedMethod = MethodSpec.methodBuilder("onSizeChanged")
        .addAnnotation(Override::class.java)
        .addModifiers(Modifier.PROTECTED)
        .addParameter(TypeName.INT, "w")
        .addParameter(TypeName.INT, "h")
        .addParameter(TypeName.INT, "oldw")
        .addParameter(TypeName.INT, "oldh")
        .addStatement("super.onSizeChanged(w, h, oldw, oldh)")

    if (isLayout) {
      onSizeChangedMethod.addStatement("foregroundBoundsChanged = true")
    } else {
      onSizeChangedMethod.beginControlFlow("if (foreground != null)")
          .addStatement("foreground.setBounds(0, 0, w, h)")
          .endControlFlow()
    }

    type.addMethod(onSizeChangedMethod.build())

    if (sourceType.endsWith("ImageView")) {
      type.addMethod(MethodSpec.methodBuilder("hasOverlappingRendering")
          .addAnnotation(Override::class.java)
          .addModifiers(Modifier.PUBLIC)
          .returns(TypeName.BOOLEAN)
          .addStatement("return false")
          .build())
    }

    if (isLayout) {
      type.addMethod(MethodSpec.methodBuilder("getForegroundGravity")
          .addJavadoc("""Describes how the foreground is positioned.

    @return foreground gravity.
    @see #setForegroundGravity(int)
    """)
          .addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
          .addModifiers(Modifier.PUBLIC)
          .returns(TypeName.INT)
          .addStatement("return foregroundGravity")
          .build())

      type.addMethod(MethodSpec.methodBuilder("setForegroundGravity")
          .addJavadoc("""Describes how the foreground is positioned. Defaults to START and TOP.

    @param foregroundGravity See {@link android.view.Gravity}
    @see #getForegroundGravity()
    """)
          .addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
          .addModifiers(Modifier.PUBLIC)
          .addParameter(TypeName.INT, "foregroundGravity")
          .beginControlFlow("if (this.foregroundGravity != foregroundGravity)")
          .beginControlFlow("if ((foregroundGravity & \$T.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0)",
              TypeNames.Android.Gravity)
          .addStatement("foregroundGravity |= \$T.START", TypeNames.Android.GravityCompat)
          .endControlFlow()
          .beginControlFlow("if ((foregroundGravity & \$T.VERTICAL_GRAVITY_MASK) == 0)",
              TypeNames.Android.Gravity)
          .addStatement("foregroundGravity |= \$T.TOP", TypeNames.Android.Gravity)
          .endControlFlow()
          .addStatement("this.foregroundGravity = foregroundGravity")
          .beginControlFlow("if (this.foregroundGravity == \$T.FILL && foreground != null)",
              TypeNames.Android.Gravity)
          .addStatement("\$T padding = new \$T()", TypeNames.Android.Rect, TypeNames.Android.Rect)
          .addStatement("foreground.getPadding(padding)")
          .endControlFlow()
          .addStatement("requestLayout()")
          .endControlFlow()
          .build())
    }

    type.addMethod(MethodSpec.methodBuilder("verifyDrawable")
        .addAnnotation(Override::class.java)
        .addModifiers(Modifier.PROTECTED)
        .returns(TypeName.BOOLEAN)
        .addParameter(TypeNames.Android.Drawable, "who")
        .addStatement("return super.verifyDrawable(who) || (who == foreground)")
        .build())

    type.addMethod(MethodSpec.methodBuilder("jumpDrawablesToCurrentState")
        .addAnnotation(Override::class.java)
        .addModifiers(Modifier.PUBLIC)
        .addStatement("super.jumpDrawablesToCurrentState()")
        .beginControlFlow("if (foreground != null)")
        .addStatement("foreground.jumpToCurrentState()")
        .endControlFlow()
        .build())

    type.addMethod(MethodSpec.methodBuilder("drawableStateChanged")
        .addAnnotation(Override::class.java)
        .addModifiers(Modifier.PROTECTED)
        .addStatement("super.drawableStateChanged()")
        .beginControlFlow("if (foreground != null && foreground.isStateful())")
        .addStatement("foreground.setState(getDrawableState())")
        .endControlFlow()
        .build())

    type.addMethod(MethodSpec.methodBuilder("getForeground")
        .addJavadoc("""Returns the drawable used as the foreground of this view. The
    foreground drawable, if non-null, is always drawn on top of the children.

    @return A Drawable or null if no foreground was set.
    """)
        .addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
        .addModifiers(Modifier.PUBLIC)
        .returns(TypeNames.Android.Drawable)
        .addStatement("return foreground")
        .build())

    val setForegroundMethod = MethodSpec.methodBuilder("setForeground")
        .addJavadoc("""Supply a Drawable that is to be rendered on top of all of the child
    views in this layout.  Any padding in the Drawable will be taken
    into account by ensuring that the children are inset to be placed
    inside of the padding area.

    @param drawable The Drawable to be drawn on top of the children.
    """)
        .addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
        .addAnnotation(AnnotationSpec.builder(ClassName.get("android.annotation", "SuppressLint"))
            .addMember("value", "\"NewApi\"")
            .build())
        .addModifiers(Modifier.PUBLIC)
        .addParameter(TypeNames.Android.Drawable, "drawable")
        .beginControlFlow("if (foreground != drawable)")
        .beginControlFlow("if (foreground != null)")
        .addStatement("foreground.setCallback(null)")
        .addStatement("unscheduleDrawable(foreground)")
        .endControlFlow()
        .addStatement("foreground = drawable")
        .beginControlFlow("if (drawable != null)")

    if (!isLayout) {
      setForegroundMethod.addStatement("foreground.setBounds(0, 0, getWidth(), getHeight())")
    }

    setForegroundMethod.addStatement("setWillNotDraw(false)")
        .addStatement("drawable.setCallback(this)")
        .beginControlFlow("if (drawable.isStateful())")
        .addStatement("drawable.setState(getDrawableState())")
        .endControlFlow()

    if (isLayout) {
      setForegroundMethod.beginControlFlow("if (foregroundGravity == \$T.FILL)", TypeNames.Android.Gravity)
      setForegroundMethod.addStatement("\$T padding = new \$T()", TypeNames.Android.Rect, TypeNames.Android.Rect)
      setForegroundMethod.addStatement("drawable.getPadding(padding)")
      setForegroundMethod.endControlFlow()
    }

    setForegroundMethod.nextControlFlow("else")
        .addStatement("setWillNotDraw(true)")
        .endControlFlow()

    if (isLayout) {
      setForegroundMethod.addStatement("requestLayout()")
    }
    setForegroundMethod.addStatement("invalidate()")
        .endControlFlow()

    type.addMethod(setForegroundMethod.build())

    if (isLayout) {
      type.addMethod(MethodSpec.methodBuilder("onLayout")
          .addAnnotation(Override::class.java)
          .addModifiers(Modifier.PROTECTED)
          .addParameter(TypeName.BOOLEAN, "changed")
          .addParameter(TypeName.INT, "left")
          .addParameter(TypeName.INT, "top")
          .addParameter(TypeName.INT, "right")
          .addParameter(TypeName.INT, "bottom")
          .addStatement("super.onLayout(changed, left, top, right, bottom)")
          .beginControlFlow("if (changed)")
          .addStatement("foregroundBoundsChanged = true")
          .endControlFlow()
          .build())
    }

    val drawMethod = MethodSpec.methodBuilder("draw")
        .addAnnotation(Override::class.java)
        .addModifiers(Modifier.PUBLIC)
        .addParameter(TypeNames.Android.Canvas, "canvas")
        .addStatement("super.draw(canvas)")
        .beginControlFlow("if (foreground != null)")

    if (isLayout) {
      drawMethod.addStatement("final \$T localForeground = foreground", TypeNames.Android.Drawable)
          .beginControlFlow("if (foregroundBoundsChanged)")
          .addStatement("foregroundBoundsChanged = false")
          .addStatement("final \$T localSelfBounds = selfBounds", TypeNames.Android.Rect)
          .addStatement("final \$T localOverlayBounds = overlayBounds", TypeNames.Android.Rect)
          .addStatement("final int w = getRight() - getLeft()")
          .addStatement("final int h = getBottom() - getTop()")
          .beginControlFlow("if (foregroundInPadding)")
          .addStatement("localSelfBounds.set(0, 0, w, h)")
          .nextControlFlow("else")
          .addStatement("localSelfBounds.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - " +
              "getPaddingBottom())")
          .endControlFlow()
          .addStatement("\$T.apply(foregroundGravity, localForeground.getIntrinsicWidth(), localForeground" +
              ".getIntrinsicHeight(), localSelfBounds, localOverlayBounds)", TypeNames.Android.Gravity)
          .addStatement("localForeground.setBounds(localOverlayBounds)")
          .endControlFlow()
          .addStatement("localForeground.draw(canvas)")
    } else {
      drawMethod.addStatement("foreground.draw(canvas)")
    }
    drawMethod.endControlFlow()
    type.addMethod(drawMethod.build())

    type.addMethod(MethodSpec.methodBuilder("drawableHotspotChanged")
        .addAnnotation(AnnotationSpec.builder(TypeNames.Annotations.TargetApi)
            .addMember("value", "\$L", "android.os.Build.VERSION_CODES.LOLLIPOP")
            .build())
        .addAnnotation(Override::class.java)
        .addModifiers(Modifier.PUBLIC)
        .addParameter(TypeName.FLOAT, "x")
        .addParameter(TypeName.FLOAT, "y")
        .addStatement("super.drawableHotspotChanged(x, y)")
        .beginControlFlow("if (foreground != null)")
        .addStatement("foreground.setHotspot(x, y)")
        .endControlFlow()
        .build())
  }