/** * 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.testing; import java.util.ArrayList; import java.util.List; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.support.annotation.AttrRes; import android.support.annotation.StyleRes; import android.support.v4.util.Pools; import com.facebook.litho.Component; import com.facebook.litho.ComponentLayout; import com.facebook.litho.ComponentLifecycle; import com.facebook.litho.ComponentContext; import com.facebook.litho.Size; import com.facebook.litho.SizeSpec; public class TestDrawableComponent extends ComponentLifecycle { private static final List<TestDrawableComponent> sInstances = new ArrayList<>(); private static final Pools.SynchronizedPool<Builder> mBuilderPool = new Pools.SynchronizedPool<>(2); private static final long CALLS_SHOULD_UPDATE_ON_MOUNT = 1L << 0; private static final long IS_PURE_RENDER = 1L << 1; private static final long CAN_MEASURE = 1L << 2; private static final long USES_DISPLAY_LIST = 1L << 3; private static final long IMPLEMENTS_ACCESSIBILITY = 1L << 4; private static final long IS_MOUNT_SIZE_DEPENDENT = 1L << 5; private long mProperties; private synchronized static TestDrawableComponent get(long properties) { for (TestDrawableComponent lifecycle : sInstances) { if (lifecycle.mProperties == properties) { return lifecycle; } } final TestDrawableComponent lifecycle = new TestDrawableComponent(properties); sInstances.add(lifecycle); return lifecycle; } private TestDrawableComponent(long properties) { super(); mProperties = properties; } @Override public boolean shouldUpdate(Component previous, Component next) { return !next.equals(previous); } @Override protected boolean callsShouldUpdateOnMount() { return (mProperties & CALLS_SHOULD_UPDATE_ON_MOUNT) != 0; } @Override protected boolean isPureRender() { return (mProperties & IS_PURE_RENDER) != 0; } @Override protected boolean implementsAccessibility() { return (mProperties & IMPLEMENTS_ACCESSIBILITY) != 0; } @Override protected boolean shouldUseDisplayList() { return (mProperties & USES_DISPLAY_LIST) != 0; } @Override public boolean isMountSizeDependent() { return (mProperties & IS_MOUNT_SIZE_DEPENDENT) != 0; } @Override protected Object onCreateMountContent(ComponentContext c) { return new ColorDrawable(); } @Override protected void onMount(ComponentContext c, Object convertDrawable, Component _stateObject) { State state = (State) _stateObject; ((ColorDrawable) convertDrawable).setColor(state.color); state.onMountCalled(); } @Override protected void onUnmount(ComponentContext c, Object mountedContent, Component<?> component) { State state = (State) component; state.onUnmountCalled(); } @Override protected boolean canMeasure() { return (mProperties & CAN_MEASURE) != 0; } @Override protected void onMeasure( ComponentContext c, ComponentLayout layout, int widthSpec, int heightSpec, Size size, Component<?> component) { int width = SizeSpec.getSize(widthSpec); int height = SizeSpec.getSize(heightSpec); State state = (State) component; state.onMeasureCalled(); size.width = (state.measuredWidth != -1) ? SizeSpec.resolveSize(widthSpec, state.measuredWidth) : width; size.height = (state.measuredHeight != -1) ? SizeSpec.resolveSize(heightSpec, state.measuredHeight) : height; } @Override protected void onBoundsDefined( ComponentContext c, ComponentLayout layout, Component<?> component) { State state = (State) component; state.onDefineBoundsCalled(); } @Override protected void onBind(ComponentContext c, Object mountedContent, Component<?> component) { State state = (State) component; state.onBindCalled(); } @Override protected void onUnbind(ComponentContext c, Object mountedContent, Component<?> component) { State state = (State) component; state.onUnbindCalled(); } @Override public MountType getMountType() { return MountType.DRAWABLE; } public static Builder create( ComponentContext context, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { return create(context, defStyleAttr, defStyleRes, true, true, true, false, false); } public static Builder create( ComponentContext context, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, boolean callsShouldUpdateOnMount, boolean isPureRender, boolean canMeasure, boolean implementsAccessibility, boolean usesDisplayList) { return create( context, defStyleAttr, defStyleRes, callsShouldUpdateOnMount, isPureRender, canMeasure, implementsAccessibility, usesDisplayList, false); } public static Builder create( ComponentContext context, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, boolean callsShouldUpdateOnMount, boolean isPureRender, boolean canMeasure, boolean implementsAccessibility, boolean usesDisplayList, boolean isMountSizeDependent) { long properties = 0; if (callsShouldUpdateOnMount) { properties |= CALLS_SHOULD_UPDATE_ON_MOUNT; } if (isPureRender) { properties |= IS_PURE_RENDER; } if (canMeasure) { properties |= CAN_MEASURE; } if (implementsAccessibility) { properties |= IMPLEMENTS_ACCESSIBILITY; } if (usesDisplayList) { properties |= USES_DISPLAY_LIST; } if (isMountSizeDependent) { properties |= IS_MOUNT_SIZE_DEPENDENT; } return newBuilder(context, defStyleAttr, defStyleRes, new State(properties)); } public static Builder create(ComponentContext context) { return create(context, 0, 0, true, true, true, false, false, false); } public static Builder create( ComponentContext context, boolean callsShouldUpdateOnMount, boolean isPureRender, boolean canMeasure, boolean implementsAccessibility, boolean usesDisplayList) { return create( context, 0, 0, callsShouldUpdateOnMount, isPureRender, canMeasure, implementsAccessibility, usesDisplayList); } private static Builder newBuilder( ComponentContext context, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, State state) { Builder builder = mBuilderPool.acquire(); if (builder == null) { builder = new Builder(); } builder.init(context, defStyleAttr, defStyleRes, state); return builder; } public static class State extends TestComponent<TestDrawableComponent> implements Cloneable { int color = Color.BLACK; int measuredWidth = -1; int measuredHeight = -1; private State(long properties) { super(get(properties)); } @Override public int hashCode() { return super.hashCode() + color; } @Override public boolean equals(Object o) { if (o == null) { return false; } if (!super.equals(o)) { return false; } if (o instanceof State) { State s = (State) o; return color == s.color; } return false; } } public static class Builder extends com.facebook.litho.Component.Builder<TestDrawableComponent> { State mState; private void init( ComponentContext context, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, State state) { super.init(context, defStyleAttr, defStyleRes, state); mState = state; } public Builder measuredWidth(int width) { mState.measuredWidth = width; return this; } public Builder measuredHeight(int height) { mState.measuredHeight = height; return this; } public Builder color(int color) { mState.color = color; return this; } public Builder unique() { mState.mIsUnique = true; return this; } @Override public TestComponent<TestDrawableComponent> build() { State state = mState; release(); return state; } @Override protected void release() { super.release(); mState = null; mBuilderPool.release(this); } public Builder key(String key) { super.setKey(key); return this; } } }