/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.litho;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import com.facebook.litho.config.ComponentsConfiguration;
import static com.facebook.litho.Component.isHostSpec;
import static com.facebook.litho.Component.isMountViewSpec;
/**
* Draw operations used in developer options.
*/
class DebugDraw {
private static final int INTERACTIVE_VIEW_COLOR = 0x66C29BFF;
private static final int TOUCH_DELEGATE_COLOR = 0x44D3FFCE;
private static final int MOUNT_BORDER_COLOR = 0x99FF0000;
private static final int MOUNT_BORDER_COLOR_HOST = 0x99FF00FF;
private static final int MOUNT_CORNER_COLOR = 0xFF0000FF;
private static final int MOUNT_CORNER_COLOR_HOST = 0xFF00FFFF;
private static Paint sInteractiveViewPaint;
private static Paint sTouchDelegatePaint;
private static Rect sMountBoundsRect;
private static Paint sMountBoundsBorderPaint;
private static Paint sMountBoundsCornerPaint;
static void draw(ComponentHost host, Canvas canvas) {
if (ComponentsConfiguration.debugHighlightInteractiveBounds) {
highlightInteractiveBounds(host, canvas);
}
if (ComponentsConfiguration.debugHighlightMountBounds) {
highlightMountBounds(host, canvas);
}
}
private static void highlightInteractiveBounds(ComponentHost host, Canvas canvas) {
if (sInteractiveViewPaint == null) {
sInteractiveViewPaint = new Paint();
sInteractiveViewPaint.setColor(INTERACTIVE_VIEW_COLOR);
}
if (sTouchDelegatePaint == null) {
sTouchDelegatePaint = new Paint();
sTouchDelegatePaint.setColor(TOUCH_DELEGATE_COLOR);
}
// 1. Highlight root, if applicable.
if (isInteractive(host)) {
canvas.drawRect(0, 0, host.getWidth(), host.getHeight(), sInteractiveViewPaint);
}
// 2. Highlight non-host interactive mounted views.
for (int i = host.getMountItemCount() - 1; i >= 0; i--) {
final MountItem item = host.getMountItemAt(i);
final Component<?> component = item.getComponent();
if (!isMountViewSpec(component) || isHostSpec(component)) {
continue;
}
final View view = (View) item.getContent();
if (!isInteractive(view)) {
continue;
}
canvas.drawRect(
view.getLeft(),
view.getTop(),
view.getRight(),
view.getBottom(),
sTouchDelegatePaint);
}
// 3. Highlight expanded touch bounds.
final TouchExpansionDelegate touchDelegate = host.getTouchExpansionDelegate();
if (touchDelegate != null) {
touchDelegate.draw(canvas, sTouchDelegatePaint);
}
}
private static void highlightMountBounds(ComponentHost host, Canvas canvas) {
final Resources resources = host.getResources();
if (sMountBoundsRect == null) {
sMountBoundsRect = new Rect();
}
if (sMountBoundsBorderPaint == null) {
sMountBoundsBorderPaint = new Paint();
sMountBoundsBorderPaint.setStyle(Style.STROKE);
sMountBoundsBorderPaint.setStrokeWidth(dipToPixels(resources, 1));
}
if (sMountBoundsCornerPaint == null) {
sMountBoundsCornerPaint = new Paint();
sMountBoundsCornerPaint.setStyle(Style.FILL);
sMountBoundsCornerPaint.setStrokeWidth(dipToPixels(resources, 2));
}
for (int i = host.getMountItemCount() - 1; i >= 0; i--) {
final MountItem item = host.getMountItemAt(i);
final Component<?> component = item.getComponent();
final Object content = item.getContent();
if (!shouldHighlight(component)) {
continue;
}
if (content instanceof View) {
final View view = (View) content;
sMountBoundsRect.left = view.getLeft();
sMountBoundsRect.top = view.getTop();
sMountBoundsRect.right = view.getRight();
sMountBoundsRect.bottom = view.getBottom();
} else if (content instanceof Drawable) {
final Drawable drawable = (Drawable) content;
sMountBoundsRect.set(drawable.getBounds());
}
sMountBoundsBorderPaint.setColor(getBorderColor(component));
drawMountBoundsBorder(canvas, sMountBoundsBorderPaint, sMountBoundsRect);
sMountBoundsCornerPaint.setColor(getCornerColor(component));
drawMountBoundsCorners(
canvas,
sMountBoundsCornerPaint,
sMountBoundsRect,
(int) sMountBoundsCornerPaint.getStrokeWidth(),
Math.min(
Math.min(sMountBoundsRect.width(), sMountBoundsRect.height()) / 3,
dipToPixels(resources, 12)));
}
}
private static void drawMountBoundsBorder(Canvas canvas, Paint paint, Rect bounds) {
final int inset = (int) paint.getStrokeWidth() / 2;
canvas.drawRect(
bounds.left + inset,
bounds.top + inset,
bounds.right - inset,
bounds.bottom - inset,
paint);
}
private static void drawMountBoundsCorners(
Canvas canvas,
Paint paint,
Rect bounds,
int cornerLength,
int cornerWidth) {
drawCorner(
canvas,
paint,
bounds.left,
bounds.top,
cornerLength,
cornerLength,
cornerWidth);
drawCorner(
canvas,
paint,
bounds.left,
bounds.bottom,
cornerLength,
-cornerLength,
cornerWidth);
drawCorner(
canvas,
paint,
bounds.right,
bounds.top,
-cornerLength,
cornerLength,
cornerWidth);
drawCorner(
canvas,
paint,
bounds.right,
bounds.bottom,
-cornerLength,
-cornerLength,
cornerWidth);
}
private static boolean shouldHighlight(Component<?> component) {
// Don't highlight bounds of background/foreground components.
return !(component.getLifecycle() instanceof DrawableComponent);
}
private static int dipToPixels(Resources res, int dips) {
float scale = res.getDisplayMetrics().density;
return (int) (dips * scale + 0.5f);
}
private static int getBorderColor(Component<?> component) {
return isHostSpec(component) ? MOUNT_BORDER_COLOR_HOST : MOUNT_BORDER_COLOR;
}
private static int getCornerColor(Component<?> component) {
return isHostSpec(component) ? MOUNT_CORNER_COLOR_HOST : MOUNT_CORNER_COLOR;
}
private static int sign(float x) {
return (x >= 0) ? 1 : -1;
}
private static void drawCorner(
Canvas c,
Paint paint,
int x,
int y,
int dx,
int dy,
int cornerWidth) {
drawCornerLine(c, paint, x, y, x + dx, y + cornerWidth * sign(dy));
drawCornerLine(c, paint, x, y, x + cornerWidth * sign(dx), y + dy);
}
private static void drawCornerLine(
Canvas canvas,
Paint paint,
int left,
int top,
int right,
int bottom) {
if (left > right) {
final int tmp = left;
left = right;
right = tmp;
}
if (top > bottom) {
final int tmp = top;
top = bottom;
bottom = tmp;
}
canvas.drawRect(left, top, right, bottom, paint);
}
private static boolean isInteractive(View view) {
return MountState.getComponentClickListener(view) != null
|| MountState.getComponentLongClickListener(view) != null
|| MountState.getComponentTouchListener(view) != null;
}
}