override fun generateFor()

in buildSrc/artist-traits/src/main/kotlin/com/uber/artist/traits/KotlinForegroundTrait.kt [48:307]


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

    val isLayout = sourceType.endsWith("Layout")

    // The field
    type.addProperty(PropertySpec.builder("foreground", KotlinTypeNames.Android.Drawable.copy(nullable = true), KModifier.PRIVATE)
        .initializer("null")
        .mutable()
        .build())

    if (isLayout) {
      type.addProperty(PropertySpec.builder("selfBounds", KotlinTypeNames.Android.Rect, KModifier.PRIVATE, KModifier.FINAL)
          .initializer("%T()", KotlinTypeNames.Android.Rect)
          .build())
      type.addProperty(PropertySpec.builder("overlayBounds", KotlinTypeNames.Android.Rect, KModifier.PRIVATE, KModifier.FINAL)
          .initializer("%T()", KotlinTypeNames.Android.Rect)
          .build())
      type.addProperty(PropertySpec.builder("foregroundInPadding", BOOLEAN, KModifier.PRIVATE)
          .initializer("true")
          .mutable()
          .build())
      type.addProperty(PropertySpec.builder("foregroundBoundsChanged", BOOLEAN, KModifier.PRIVATE)
          .initializer("false")
          .mutable()
          .build())
      type.addProperty(PropertySpec.builder("foregroundGravity", INT, KModifier.PRIVATE)
          .initializer("%T.FILL", KotlinTypeNames.Android.Gravity)
          .mutable()
          .build())
    }

    // Pull out the value
    initMethod.apply {
      addStatement("val foregroundTA = context.obtainStyledAttributes(attrs, %T.styleable.ForegroundView)", rClass)
      beginControlFlow("foregroundTA.getDrawable(%T.styleable.ForegroundView_android_foreground)?.let", rClass)
      addCode("//noinspection AndroidLintNewApi\n")
      addStatement("setForeground(it)")
      endControlFlow()

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

      addStatement("foregroundTA.recycle()")
    }

    val onSizeChangedMethod = FunSpec.builder("onSizeChanged")
        .addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
        .addParameter("w", INT)
        .addParameter("h", INT)
        .addParameter("oldw", INT)
        .addParameter("oldh", INT)
        .addStatement("super.onSizeChanged(w, h, oldw, oldh)")

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

    type.addFunction(onSizeChangedMethod.build())

    if (sourceType.endsWith("ImageView")) {
      type.addFunction(FunSpec.builder("hasOverlappingRendering")
          .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
          .returns(BOOLEAN)
          .addStatement("return false")
          .build())
    }

    if (isLayout) {
      type.addFunction(FunSpec.builder("getForegroundGravity")
          .addKdoc("""Describes how the foreground is positioned.

    @return foreground gravity.
    @see #setForegroundGravity(int)
    """)
          .addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("%S", "MissingOverride").build())
          .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
          .returns(INT)
          .addStatement("return foregroundGravity")
          .build())

      type.addFunction(FunSpec.builder("setForegroundGravity")
          .addKdoc("""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("%S", "MissingOverride").build())
          .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
          .addParameter("foregroundGravity", INT)
          .beginControlFlow("if (this.foregroundGravity != foregroundGravity)")
          .beginControlFlow("if ((foregroundGravity and %T.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0)",
              KotlinTypeNames.Android.Gravity)
          .addStatement("this.foregroundGravity = foregroundGravity.or(%T.START)", KotlinTypeNames.Android.GravityCompat)
          .endControlFlow()
          .beginControlFlow("if ((foregroundGravity and %T.VERTICAL_GRAVITY_MASK) == 0)",
              KotlinTypeNames.Android.Gravity)
          .addStatement("this.foregroundGravity = foregroundGravity.or(%T.TOP)", KotlinTypeNames.Android.Gravity)
          .endControlFlow()
          .beginControlFlow("if (this.foregroundGravity == %T.FILL && foreground != null)",
              KotlinTypeNames.Android.Gravity)
          .addStatement("val padding = %T()", KotlinTypeNames.Android.Rect)
          .addStatement("foreground?.getPadding(padding)")
          .endControlFlow()
          .addStatement("requestLayout()")
          .endControlFlow()
          .build())
    }

    type.addFunction(FunSpec.builder("verifyDrawable")
        .addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
        .returns(BOOLEAN)
        .addParameter("who", KotlinTypeNames.Android.Drawable)
        .addStatement("return super.verifyDrawable(who) || (who == foreground)")
        .build())

    type.addFunction(FunSpec.builder("jumpDrawablesToCurrentState")
        .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
        .addStatement("super.jumpDrawablesToCurrentState()")
        .addStatement("foreground?.jumpToCurrentState()")
        .build())

    type.addFunction(FunSpec.builder("drawableStateChanged")
        .addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
        .addStatement("super.drawableStateChanged()")
        .beginControlFlow("if (foreground?.isStateful() ?: false)")
        .addStatement("foreground?.setState(getDrawableState())")
        .endControlFlow()
        .build())

    type.addFunction(FunSpec.builder("getForeground")
        .addKdoc("""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("%S", "MissingOverride").build())
        .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
        .returns(KotlinTypeNames.Android.Drawable.copy(nullable = true))
        .addStatement("return foreground")
        .build())

    val setForegroundMethod = FunSpec.builder("setForeground")
        .addKdoc("""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("%S", "MissingOverride").build())
        .addAnnotation(AnnotationSpec.builder(ClassName("android.annotation", "SuppressLint"))
            .addMember("%S", "NewApi")
            .build())
        .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
        .addParameter("drawable", KotlinTypeNames.Android.Drawable.copy(nullable = true))
        .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)", KotlinTypeNames.Android.Gravity)
      setForegroundMethod.addStatement("val padding = %T()", KotlinTypeNames.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.addFunction(setForegroundMethod.build())

    if (isLayout) {
      type.addFunction(FunSpec.builder("onLayout")
          .addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
          .addParameter("changed", BOOLEAN)
          .addParameter("left", INT)
          .addParameter("top", INT)
          .addParameter("right", INT)
          .addParameter("bottom", INT)
          .addStatement("super.onLayout(changed, left, top, right, bottom)")
          .beginControlFlow("if (changed)")
          .addStatement("foregroundBoundsChanged = true")
          .endControlFlow()
          .build())
    }

    val drawMethod = FunSpec.builder("draw")
        .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
        .addParameter("canvas", KotlinTypeNames.Android.Canvas)
        .addStatement("super.draw(canvas)")

    if (isLayout) {
      drawMethod
          .beginControlFlow("foreground?.let")
          .addStatement("val localForeground = it")
          .beginControlFlow("if (foregroundBoundsChanged)")
          .addStatement("foregroundBoundsChanged = false")
          .addStatement("val localSelfBounds: %T = selfBounds", KotlinTypeNames.Android.Rect)
          .addStatement("val localOverlayBounds: %T = overlayBounds", KotlinTypeNames.Android.Rect)
          .addStatement("val w: Int = getRight() - getLeft()")
          .addStatement("val h: Int = 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)", KotlinTypeNames.Android.Gravity)
          .addStatement("localForeground.setBounds(localOverlayBounds)")
          .endControlFlow()
          .addStatement("localForeground.draw(canvas)")
          .endControlFlow()
    } else {
      drawMethod.addStatement("foreground?.draw(canvas)")
    }
    type.addFunction(drawMethod.build())

    type.addFunction(FunSpec.builder("drawableHotspotChanged")
        .addAnnotation(AnnotationSpec.builder(KotlinTypeNames.Annotations.TargetApi)
            .addMember("%T.VERSION_CODES.LOLLIPOP", ClassName("android.os", "Build"))
            .build())
        .addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
        .addParameter("x", FLOAT)
        .addParameter("y", FLOAT)
        .addStatement("super.drawableHotspotChanged(x, y)")
        .addStatement("foreground?.setHotspot(x, y)")
        .build())
  }