/** * 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 javax.annotation.Nullable; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.AttrRes; import android.support.annotation.ColorInt; import android.support.annotation.DimenRes; import android.support.annotation.Dimension; import android.support.annotation.DrawableRes; import android.support.annotation.Px; import android.support.annotation.StringRes; import android.support.v4.view.ViewCompat; import android.text.TextUtils; import android.util.SparseArray; import com.facebook.infer.annotation.ReturnsOwnership; import com.facebook.infer.annotation.ThreadConfined; import com.facebook.litho.config.ComponentsConfiguration; import com.facebook.litho.reference.DrawableReference; import com.facebook.litho.reference.Reference; import com.facebook.yoga.YogaAlign; import com.facebook.yoga.YogaBaselineFunction; import com.facebook.yoga.YogaConstants; import com.facebook.yoga.YogaDirection; import com.facebook.yoga.YogaEdge; import com.facebook.yoga.YogaFlexDirection; import com.facebook.yoga.YogaJustify; import com.facebook.yoga.YogaMeasureFunction; import com.facebook.yoga.YogaNode; import com.facebook.yoga.YogaPositionType; import com.facebook.yoga.YogaWrap; import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; import static android.os.Build.VERSION_CODES.JELLY_BEAN; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; import static android.support.annotation.Dimension.DP; import static com.facebook.litho.ComponentContext.NULL_LAYOUT; import static com.facebook.yoga.YogaEdge.ALL; import static com.facebook.yoga.YogaEdge.BOTTOM; import static com.facebook.yoga.YogaEdge.END; import static com.facebook.yoga.YogaEdge.HORIZONTAL; import static com.facebook.yoga.YogaEdge.LEFT; import static com.facebook.yoga.YogaEdge.RIGHT; import static com.facebook.yoga.YogaEdge.START; import static com.facebook.yoga.YogaEdge.TOP; import static com.facebook.yoga.YogaEdge.VERTICAL; /** * Internal class representing both a {@link ComponentLayout} and a * {@link com.facebook.litho.ComponentLayout.ContainerBuilder}. */ @ThreadConfined(ThreadConfined.ANY) class InternalNode implements ComponentLayout, ComponentLayout.ContainerBuilder { // Used to check whether or not the framework can use style IDs for // paddingStart/paddingEnd due to a bug in some Android devices. private static final boolean SUPPORTS_RTL = (SDK_INT >= JELLY_BEAN_MR1); // When this flag is set, layoutDirection style was explicitly set on this node. private static final long PFLAG_LAYOUT_DIRECTION_IS_SET = 1L << 0; // When this flag is set, alignSelf was explicitly set on this node. private static final long PFLAG_ALIGN_SELF_IS_SET = 1L << 1; // When this flag is set, position type was explicitly set on this node. private static final long PFLAG_POSITION_TYPE_IS_SET = 1L << 2; // When this flag is set, flex was explicitly set on this node. private static final long PFLAG_FLEX_IS_SET = 1L << 3; // When this flag is set, flex grow was explicitly set on this node. private static final long PFLAG_FLEX_GROW_IS_SET = 1L << 4; // When this flag is set, flex shrink was explicitly set on this node. private static final long PFLAG_FLEX_SHRINK_IS_SET = 1L << 5; // When this flag is set, flex basis was explicitly set on this node. private static final long PFLAG_FLEX_BASIS_IS_SET = 1L << 6; // When this flag is set, importantForAccessibility was explicitly set on this node. private static final long PFLAG_IMPORTANT_FOR_ACCESSIBILITY_IS_SET = 1L << 7; // When this flag is set, duplicateParentState was explicitly set on this node. private static final long PFLAG_DUPLICATE_PARENT_STATE_IS_SET = 1L << 8; // When this flag is set, margin was explicitly set on this node. private static final long PFLAG_MARGIN_IS_SET = 1L << 9; // When this flag is set, padding was explicitly set on this node. private static final long PFLAG_PADDING_IS_SET = 1L << 10; // When this flag is set, position was explicitly set on this node. private static final long PFLAG_POSITION_IS_SET = 1L << 11; // When this flag is set, width was explicitly set on this node. private static final long PFLAG_WIDTH_IS_SET = 1L << 12; // When this flag is set, minWidth was explicitly set on this node. private static final long PFLAG_MIN_WIDTH_IS_SET = 1L << 13; // When this flag is set, maxWidth was explicitly set on this node. private static final long PFLAG_MAX_WIDTH_IS_SET = 1L << 14; // When this flag is set, height was explicitly set on this node. private static final long PFLAG_HEIGHT_IS_SET = 1L << 15; // When this flag is set, minHeight was explicitly set on this node. private static final long PFLAG_MIN_HEIGHT_IS_SET = 1L << 16; // When this flag is set, maxHeight was explicitly set on this node. private static final long PFLAG_MAX_HEIGHT_IS_SET = 1L << 17; // When this flag is set, background was explicitly set on this node. private static final long PFLAG_BACKGROUND_IS_SET = 1L << 18; // When this flag is set, foreground was explicitly set on this node. private static final long PFLAG_FOREGROUND_IS_SET = 1L << 19; // When this flag is set, visibleHandler was explicitly set on this node. private static final long PFLAG_VISIBLE_HANDLER_IS_SET = 1L << 20; // When this flag is set, focusedHandler was explicitly set on this node. private static final long PFLAG_FOCUSED_HANDLER_IS_SET = 1L << 21; // When this flag is set, fullImpressionHandler was explicitly set on this node. private static final long PFLAG_FULL_IMPRESSION_HANDLER_IS_SET = 1L << 22; // When this flag is set, invisibleHandler was explicitly set on this node. private static final long PFLAG_INVISIBLE_HANDLER_IS_SET = 1L << 23; // When this flag is set, unfocusedHandler was explicitly set on this node. private static final long PFLAG_UNFOCUSED_HANDLER_IS_SET = 1L << 24; // When this flag is set, touch expansion was explicitly set on this node. private static final long PFLAG_TOUCH_EXPANSION_IS_SET = 1L << 25; // When this flag is set, border width was explicitly set on this node. private static final long PFLAG_BORDER_WIDTH_IS_SET = 1L << 26; // When this flag is set, aspectRatio was explicitly set on this node. private static final long PFLAG_ASPECT_RATIO_IS_SET = 1L << 27; // When this flag is set, transitionKey was explicitly set on this node. private static final long PFLAG_TRANSITION_KEY_IS_SET = 1L << 28; // When this flag is set, border color was explicitly set on this node. private static final long PFLAG_BORDER_COLOR_IS_SET = 1L << 29; private final ResourceResolver mResourceResolver = new ResourceResolver(); YogaNode mYogaNode; private ComponentContext mComponentContext; private Resources mResources; @ThreadConfined(ThreadConfined.ANY) private List<Component> mComponents = new ArrayList(1); private int mImportantForAccessibility = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO; private boolean mDuplicateParentState; private boolean mIsNestedTreeHolder; private InternalNode mNestedTree; private InternalNode mNestedTreeHolder; private long mPrivateFlags; private Reference<? extends Drawable> mBackground; private Drawable mForeground; private int mBorderColor = Color.TRANSPARENT; private NodeInfo mNodeInfo; private boolean mForceViewWrapping; private String mTransitionKey; private float mVisibleRatio; private EventHandler<VisibleEvent> mVisibleHandler; private EventHandler<FocusedVisibleEvent> mFocusedHandler; private EventHandler<UnfocusedVisibleEvent> mUnfocusedHandler; private EventHandler<FullImpressionVisibleEvent> mFullImpressionHandler; private EventHandler<InvisibleEvent> mInvisibleHandler; private String mTestKey; private Edges mTouchExpansion; private Edges mNestedTreePadding; private Edges mNestedTreeBorderWidth; private boolean[] mIsPaddingPercent; private float mResolvedTouchExpansionLeft = YogaConstants.UNDEFINED; private float mResolvedTouchExpansionRight = YogaConstants.UNDEFINED; private float mResolvedX = YogaConstants.UNDEFINED; private float mResolvedY = YogaConstants.UNDEFINED; private float mResolvedWidth = YogaConstants.UNDEFINED; private float mResolvedHeight = YogaConstants.UNDEFINED; private int mLastWidthSpec = DiffNode.UNSPECIFIED; private int mLastHeightSpec = DiffNode.UNSPECIFIED; private float mLastMeasuredWidth = DiffNode.UNSPECIFIED; private float mLastMeasuredHeight = DiffNode.UNSPECIFIED; private DiffNode mDiffNode; private boolean mCachedMeasuresValid; private TreeProps mPendingTreeProps; void init(YogaNode yogaNode, ComponentContext componentContext, Resources resources) { yogaNode.setData(this); mYogaNode = yogaNode; mComponentContext = componentContext; mResources = resources; mResourceResolver.init( mComponentContext, componentContext.getResourceCache()); } @Px @Override public int getX() { if (YogaConstants.isUndefined(mResolvedX)) { mResolvedX = mYogaNode.getLayoutX(); } return (int) mResolvedX; } @Px @Override public int getY() { if (YogaConstants.isUndefined(mResolvedY)) { mResolvedY = mYogaNode.getLayoutY(); } return (int) mResolvedY; } @Px @Override public int getWidth() { if (YogaConstants.isUndefined(mResolvedWidth)) { mResolvedWidth = mYogaNode.getLayoutWidth(); } return (int) mResolvedWidth; } @Px @Override public int getHeight() { if (YogaConstants.isUndefined(mResolvedHeight)) { mResolvedHeight = mYogaNode.getLayoutHeight(); } return (int) mResolvedHeight; } @Px @Override public int getPaddingLeft() { return FastMath.round(mYogaNode.getLayoutPadding(LEFT)); } @Px @Override public int getPaddingTop() { return FastMath.round(mYogaNode.getLayoutPadding(TOP)); } @Px @Override public int getPaddingRight() { return FastMath.round(mYogaNode.getLayoutPadding(RIGHT)); } @Px @Override public int getPaddingBottom() { return FastMath.round(mYogaNode.getLayoutPadding(BOTTOM)); } public Reference<? extends Drawable> getBackground() { return mBackground; } public Drawable getForeground() { return mForeground; } public void setCachedMeasuresValid(boolean valid) { mCachedMeasuresValid = valid; } public int getLastWidthSpec() { return mLastWidthSpec; } public void setLastWidthSpec(int widthSpec) { mLastWidthSpec = widthSpec; } public int getLastHeightSpec() { return mLastHeightSpec; } public void setLastHeightSpec(int heightSpec) { mLastHeightSpec = heightSpec; } public boolean hasVisibilityHandlers() { return mVisibleHandler != null || mFocusedHandler != null || mUnfocusedHandler != null || mFullImpressionHandler != null || mInvisibleHandler != null; } /** * The last value the measure funcion associated with this node {@link Component} returned * for the width. This is used together with {@link InternalNode#getLastWidthSpec()} * to implement measure caching. */ float getLastMeasuredWidth() { return mLastMeasuredWidth; } /** * Sets the last value the measure funcion associated with this node {@link Component} returned * for the width. */ void setLastMeasuredWidth(float lastMeasuredWidth) { mLastMeasuredWidth = lastMeasuredWidth; } /** * The last value the measure funcion associated with this node {@link Component} returned * for the height. This is used together with {@link InternalNode#getLastHeightSpec()} * to implement measure caching. */ float getLastMeasuredHeight() { return mLastMeasuredHeight; } /** * Sets the last value the measure funcion associated with this node {@link Component} returned * for the height. */ void setLastMeasuredHeight(float lastMeasuredHeight) { mLastMeasuredHeight = lastMeasuredHeight; } DiffNode getDiffNode() { return mDiffNode; } boolean areCachedMeasuresValid() { return mCachedMeasuresValid; } void setDiffNode(DiffNode diffNode) { mDiffNode = diffNode; } /** * Mark this node as a nested tree root holder. */ void markIsNestedTreeHolder(TreeProps currentTreeProps) { mIsNestedTreeHolder = true; mPendingTreeProps = TreeProps.copy(currentTreeProps); } /** * @return Whether this node is holding a nested tree or not. The decision was made during * tree creation {@link ComponentLifecycle#createLayout(ComponentContext, Component, boolean)}. */ boolean isNestedTreeHolder() { return mIsNestedTreeHolder; } @Override public YogaDirection getResolvedLayoutDirection() { return mYogaNode.getLayoutDirection(); } @Override public InternalNode layoutDirection(YogaDirection direction) { mPrivateFlags |= PFLAG_LAYOUT_DIRECTION_IS_SET; mYogaNode.setDirection(direction); return this; } InternalNode flexDirection(YogaFlexDirection direction) { mYogaNode.setFlexDirection(direction); return this; } @Override public InternalNode wrap(YogaWrap wrap) { mYogaNode.setWrap(wrap); return this; } @Override public InternalNode justifyContent(YogaJustify justifyContent) { mYogaNode.setJustifyContent(justifyContent); return this; } @Override public InternalNode alignItems(YogaAlign alignItems) { mYogaNode.setAlignItems(alignItems); return this; } @Override public InternalNode alignContent(YogaAlign alignContent) { mYogaNode.setAlignContent(alignContent); return this; } @Override public InternalNode alignSelf(YogaAlign alignSelf) { mPrivateFlags |= PFLAG_ALIGN_SELF_IS_SET; mYogaNode.setAlignSelf(alignSelf); return this; } @Override public InternalNode positionType(YogaPositionType positionType) { mPrivateFlags |= PFLAG_POSITION_TYPE_IS_SET; mYogaNode.setPositionType(positionType); return this; } @Override public InternalNode flex(float flex) { mPrivateFlags |= PFLAG_FLEX_IS_SET; mYogaNode.setFlex(flex); return this; } @Override public InternalNode flexGrow(float flexGrow) { mPrivateFlags |= PFLAG_FLEX_GROW_IS_SET; mYogaNode.setFlexGrow(flexGrow); return this; } @Override public InternalNode flexShrink(float flexShrink) { mPrivateFlags |= PFLAG_FLEX_SHRINK_IS_SET; mYogaNode.setFlexShrink(flexShrink); return this; } @Override public InternalNode flexBasisPx(@Px int flexBasis) { mPrivateFlags |= PFLAG_FLEX_BASIS_IS_SET; mYogaNode.setFlexBasis(flexBasis); return this; } // Used by stetho to re-set auto value InternalNode flexBasisAuto() { mYogaNode.setFlexBasisAuto(); return this; } @Override public InternalNode flexBasisPercent(float percent) { mPrivateFlags |= PFLAG_FLEX_BASIS_IS_SET; mYogaNode.setFlexBasisPercent(percent); return this; } @Override public InternalNode flexBasisAttr(@AttrRes int resId, @DimenRes int defaultResId) { return flexBasisPx(mResourceResolver.resolveDimenOffsetAttr(resId, defaultResId)); } @Override public InternalNode flexBasisAttr(@AttrRes int resId) { return flexBasisAttr(resId, 0); } @Override public InternalNode flexBasisRes(@DimenRes int resId) { return flexBasisPx(mResourceResolver.resolveDimenOffsetRes(resId)); } @Override public InternalNode flexBasisDip(@Dimension(unit = DP) int flexBasis) { return flexBasisPx(mResourceResolver.dipsToPixels(flexBasis)); } @Override public InternalNode importantForAccessibility(int importantForAccessibility) { mPrivateFlags |= PFLAG_IMPORTANT_FOR_ACCESSIBILITY_IS_SET; mImportantForAccessibility = importantForAccessibility; return this; } @Override public InternalNode duplicateParentState(boolean duplicateParentState) { mPrivateFlags |= PFLAG_DUPLICATE_PARENT_STATE_IS_SET; mDuplicateParentState = duplicateParentState; return this; } @Override public InternalNode marginPx(YogaEdge edge, @Px int margin) { mPrivateFlags |= PFLAG_MARGIN_IS_SET; mYogaNode.setMargin(edge, margin); return this; } @Override public InternalNode marginPercent(YogaEdge edge, float percent) { mPrivateFlags |= PFLAG_MARGIN_IS_SET; mYogaNode.setMarginPercent(edge, percent); return this; } @Override public InternalNode marginAuto(YogaEdge edge) { mPrivateFlags |= PFLAG_MARGIN_IS_SET; mYogaNode.setMarginAuto(edge); return this; } @Override public InternalNode marginAttr( YogaEdge edge, @AttrRes int resId, @DimenRes int defaultResId) { return marginPx(edge, mResourceResolver.resolveDimenOffsetAttr(resId, defaultResId)); } @Override public InternalNode marginAttr( YogaEdge edge, @AttrRes int resId) { return marginAttr(edge, resId, 0); } @Override public InternalNode marginRes(YogaEdge edge, @DimenRes int resId) { return marginPx(edge, mResourceResolver.resolveDimenOffsetRes(resId)); } @Override public InternalNode marginDip(YogaEdge edge, @Dimension(unit = DP) int margin) { return marginPx(edge, mResourceResolver.dipsToPixels(margin)); } @ReturnsOwnership private Edges getNestedTreePadding() { if (mNestedTreePadding == null) { mNestedTreePadding = ComponentsPools.acquireEdges(); } return mNestedTreePadding; } @Override public InternalNode paddingPx(YogaEdge edge, @Px int padding) { mPrivateFlags |= PFLAG_PADDING_IS_SET; if (mIsNestedTreeHolder) { getNestedTreePadding().set(edge, padding); setIsPaddingPercent(edge, false); } else { mYogaNode.setPadding(edge, padding); } return this; } @Override public InternalNode paddingPercent(YogaEdge edge, float percent) { mPrivateFlags |= PFLAG_PADDING_IS_SET; if (mIsNestedTreeHolder) { getNestedTreePadding().set(edge, percent); setIsPaddingPercent(edge, true); } else { mYogaNode.setPaddingPercent(edge, percent); } return this; } @Override public InternalNode paddingAttr( YogaEdge edge, @AttrRes int resId, @DimenRes int defaultResId) { return paddingPx(edge, mResourceResolver.resolveDimenOffsetAttr(resId, defaultResId)); } @Override public InternalNode paddingAttr( YogaEdge edge, @AttrRes int resId) { return paddingAttr(edge, resId, 0); } @Override public InternalNode paddingRes(YogaEdge edge, @DimenRes int resId) { return paddingPx(edge, mResourceResolver.resolveDimenOffsetRes(resId)); } @Override public InternalNode paddingDip(YogaEdge edge, @Dimension(unit = DP) int padding) { return paddingPx(edge, mResourceResolver.dipsToPixels(padding)); } @Override public InternalNode borderWidthPx(YogaEdge edge, @Px int borderWidth) { mPrivateFlags |= PFLAG_BORDER_WIDTH_IS_SET; if (mIsNestedTreeHolder) { if (mNestedTreeBorderWidth == null) { mNestedTreeBorderWidth = ComponentsPools.acquireEdges(); } mNestedTreeBorderWidth.set(edge, borderWidth); } else { mYogaNode.setBorder(edge, borderWidth); } return this; } @Override public InternalNode borderWidthAttr( YogaEdge edge, @AttrRes int resId, @DimenRes int defaultResId) { return borderWidthPx(edge, mResourceResolver.resolveDimenOffsetAttr(resId, defaultResId)); } @Override public InternalNode borderWidthAttr( YogaEdge edge, @AttrRes int resId) { return borderWidthAttr(edge, resId, 0); } @Override public InternalNode borderWidthRes(YogaEdge edge, @DimenRes int resId) { return borderWidthPx(edge, mResourceResolver.resolveDimenOffsetRes(resId)); } @Override public InternalNode borderWidthDip( YogaEdge edge, @Dimension(unit = DP) int borderWidth) { return borderWidthPx(edge, mResourceResolver.dipsToPixels(borderWidth)); } @Override public Builder borderColor(@ColorInt int borderColor) { mPrivateFlags |= PFLAG_BORDER_COLOR_IS_SET; mBorderColor = borderColor; return this; } @Override public InternalNode positionPx(YogaEdge edge, @Px int position) { mPrivateFlags |= PFLAG_POSITION_IS_SET; mYogaNode.setPosition(edge, position); return this; } @Override public InternalNode positionPercent(YogaEdge edge, float percent) { mPrivateFlags |= PFLAG_POSITION_IS_SET; mYogaNode.setPositionPercent(edge, percent); return this; } @Override public InternalNode positionAttr( YogaEdge edge, @AttrRes int resId, @DimenRes int defaultResId) { return positionPx(edge, mResourceResolver.resolveDimenOffsetAttr(resId, defaultResId)); } @Override public InternalNode positionAttr(YogaEdge edge, @AttrRes int resId) { return positionAttr(edge, resId, 0); } @Override public InternalNode positionRes(YogaEdge edge, @DimenRes int resId) { return positionPx(edge, mResourceResolver.resolveDimenOffsetRes(resId)); } @Override public InternalNode positionDip( YogaEdge edge, @Dimension(unit = DP) int position) { return positionPx(edge, mResourceResolver.dipsToPixels(position)); } @Override public InternalNode widthPx(@Px int width) { mPrivateFlags |= PFLAG_WIDTH_IS_SET; mYogaNode.setWidth(width); return this; } // Used by stetho to re-set auto value InternalNode widthAuto() { mYogaNode.setWidthAuto(); return this; } @Override public InternalNode widthPercent(float percent) { mPrivateFlags |= PFLAG_WIDTH_IS_SET; mYogaNode.setWidthPercent(percent); return this; } @Override public InternalNode widthRes(@DimenRes int resId) { return widthPx(mResourceResolver.resolveDimenSizeRes(resId)); } @Override public InternalNode widthAttr(@AttrRes int resId, @DimenRes int defaultResId) { return widthPx(mResourceResolver.resolveDimenSizeAttr(resId, defaultResId)); } @Override public InternalNode widthAttr(@AttrRes int resId) { return widthAttr(resId, 0); } @Override public InternalNode widthDip(@Dimension(unit = DP) int width) { return widthPx(mResourceResolver.dipsToPixels(width)); } @Override public InternalNode minWidthPx(@Px int minWidth) { mPrivateFlags |= PFLAG_MIN_WIDTH_IS_SET; mYogaNode.setMinWidth(minWidth); return this; } @Override public InternalNode minWidthPercent(float percent) { mPrivateFlags |= PFLAG_MIN_WIDTH_IS_SET; mYogaNode.setMinWidthPercent(percent); return this; } @Override public InternalNode minWidthAttr(@AttrRes int resId, @DimenRes int defaultResId) { return minWidthPx(mResourceResolver.resolveDimenSizeAttr(resId, defaultResId)); } @Override public InternalNode minWidthAttr(@AttrRes int resId) { return minWidthAttr(resId, 0); } @Override public InternalNode minWidthRes(@DimenRes int resId) { return minWidthPx(mResourceResolver.resolveDimenSizeRes(resId)); } @Override public InternalNode minWidthDip(@Dimension(unit = DP) int minWidth) { return minWidthPx(mResourceResolver.dipsToPixels(minWidth)); } @Override public InternalNode maxWidthPx(@Px int maxWidth) { mPrivateFlags |= PFLAG_MAX_WIDTH_IS_SET; mYogaNode.setMaxWidth(maxWidth); return this; } @Override public InternalNode maxWidthPercent(float percent) { mPrivateFlags |= PFLAG_MAX_WIDTH_IS_SET; mYogaNode.setMaxWidthPercent(percent); return this; } @Override public InternalNode maxWidthAttr(@AttrRes int resId, @DimenRes int defaultResId) { return maxWidthPx(mResourceResolver.resolveDimenSizeAttr(resId, defaultResId)); } @Override public InternalNode maxWidthAttr(@AttrRes int resId) { return maxWidthAttr(resId, 0); } @Override public InternalNode maxWidthRes(@DimenRes int resId) { return maxWidthPx(mResourceResolver.resolveDimenSizeRes(resId)); } @Override public InternalNode maxWidthDip(@Dimension(unit = DP) int maxWidth) { return maxWidthPx(mResourceResolver.dipsToPixels(maxWidth)); } @Override public InternalNode heightPx(@Px int height) { mPrivateFlags |= PFLAG_HEIGHT_IS_SET; mYogaNode.setHeight(height); return this; } // Used by stetho to re-set auto value InternalNode heightAuto() { mYogaNode.setHeightAuto(); return this; } @Override public InternalNode heightPercent(float percent) { mPrivateFlags |= PFLAG_HEIGHT_IS_SET; mYogaNode.setHeightPercent(percent); return this; } @Override public InternalNode heightRes(@DimenRes int resId) { return heightPx(mResourceResolver.resolveDimenSizeRes(resId)); } @Override public InternalNode heightAttr(@AttrRes int resId, @DimenRes int defaultResId) { return heightPx(mResourceResolver.resolveDimenSizeAttr(resId, defaultResId)); } @Override public InternalNode heightAttr(@AttrRes int resId) { return heightAttr(resId, 0); } @Override public InternalNode heightDip(@Dimension(unit = DP) int height) { return heightPx(mResourceResolver.dipsToPixels(height)); } @Override public InternalNode minHeightPx(@Px int minHeight) { mPrivateFlags |= PFLAG_MIN_HEIGHT_IS_SET; mYogaNode.setMinHeight(minHeight); return this; } @Override public InternalNode minHeightPercent(float percent) { mPrivateFlags |= PFLAG_MIN_HEIGHT_IS_SET; mYogaNode.setMinHeightPercent(percent); return this; } @Override public InternalNode minHeightAttr(@AttrRes int resId, @DimenRes int defaultResId) { return minHeightPx(mResourceResolver.resolveDimenSizeAttr(resId, defaultResId)); } @Override public InternalNode minHeightAttr(@AttrRes int resId) { return minHeightAttr(resId, 0); } @Override public InternalNode minHeightRes(@DimenRes int resId) { return minHeightPx(mResourceResolver.resolveDimenSizeRes(resId)); } @Override public InternalNode minHeightDip(@Dimension(unit = DP) int minHeight) { return minHeightPx(mResourceResolver.dipsToPixels(minHeight)); } @Override public InternalNode maxHeightPx(@Px int maxHeight) { mPrivateFlags |= PFLAG_MAX_HEIGHT_IS_SET; mYogaNode.setMaxHeight(maxHeight); return this; } @Override public InternalNode maxHeightPercent(float percent) { mPrivateFlags |= PFLAG_MAX_HEIGHT_IS_SET; mYogaNode.setMaxHeightPercent(percent); return this; } @Override public InternalNode maxHeightAttr(@AttrRes int resId, @DimenRes int defaultResId) { return maxHeightPx(mResourceResolver.resolveDimenSizeAttr(resId, defaultResId)); } @Override public InternalNode maxHeightAttr(@AttrRes int resId) { return maxHeightAttr(resId, 0); } @Override public InternalNode maxHeightRes(@DimenRes int resId) { return maxHeightPx(mResourceResolver.resolveDimenSizeRes(resId)); } @Override public InternalNode maxHeightDip(@Dimension(unit = DP) int maxHeight) { return maxHeightPx(mResourceResolver.dipsToPixels(maxHeight)); } @Override public InternalNode aspectRatio(float aspectRatio) { mPrivateFlags |= PFLAG_ASPECT_RATIO_IS_SET; mYogaNode.setAspectRatio(aspectRatio); return this; } private boolean shouldApplyTouchExpansion() { return mTouchExpansion != null && mNodeInfo != null && mNodeInfo.hasTouchEventHandlers(); } boolean hasTouchExpansion() { return ((mPrivateFlags & PFLAG_TOUCH_EXPANSION_IS_SET) != 0L); } Edges getTouchExpansion() { return mTouchExpansion; } int getTouchExpansionLeft() { if (!shouldApplyTouchExpansion()) { return 0; } if (YogaConstants.isUndefined(mResolvedTouchExpansionLeft)) { mResolvedTouchExpansionLeft = resolveHorizontalEdges(mTouchExpansion, YogaEdge.LEFT); } return FastMath.round(mResolvedTouchExpansionLeft); } int getTouchExpansionTop() { if (!shouldApplyTouchExpansion()) { return 0; } return FastMath.round(mTouchExpansion.get(YogaEdge.TOP)); } int getTouchExpansionRight() { if (!shouldApplyTouchExpansion()) { return 0; } if (YogaConstants.isUndefined(mResolvedTouchExpansionRight)) { mResolvedTouchExpansionRight = resolveHorizontalEdges(mTouchExpansion, YogaEdge.RIGHT); } return FastMath.round(mResolvedTouchExpansionRight); } int getTouchExpansionBottom() { if (!shouldApplyTouchExpansion()) { return 0; } return FastMath.round(mTouchExpansion.get(YogaEdge.BOTTOM)); } @Override public InternalNode touchExpansionPx(YogaEdge edge, @Px int touchExpansion) { if (mTouchExpansion == null) { mTouchExpansion = ComponentsPools.acquireEdges(); } mPrivateFlags |= PFLAG_TOUCH_EXPANSION_IS_SET; mTouchExpansion.set(edge, touchExpansion); return this; } @Override public InternalNode touchExpansionAttr( YogaEdge edge, @AttrRes int resId, @DimenRes int defaultResId) { return touchExpansionPx( edge, mResourceResolver.resolveDimenOffsetAttr(resId, defaultResId)); } @Override public InternalNode touchExpansionAttr( YogaEdge edge, @AttrRes int resId) { return touchExpansionAttr(edge, resId, 0); } @Override public InternalNode touchExpansionRes(YogaEdge edge, @DimenRes int resId) { return touchExpansionPx(edge, mResourceResolver.resolveDimenOffsetRes(resId)); } @Override public InternalNode touchExpansionDip( YogaEdge edge, @Dimension(unit = DP) int touchExpansion) { return touchExpansionPx(edge, mResourceResolver.dipsToPixels(touchExpansion)); } @Override public InternalNode child(ComponentLayout child) { if (child != null && child != NULL_LAYOUT) { addChildAt((InternalNode) child, mYogaNode.getChildCount()); } return this; } @Override public InternalNode child(ComponentLayout.Builder child) { if (child != null && child != NULL_LAYOUT) { child(child.build()); } return this; } @Override public InternalNode child(Component<?> child) { if (child != null) { child(Layout.create(mComponentContext, child)); } return this; } @Override public InternalNode child(Component.Builder<?> child) { if (child != null) { child(child.build()); } return this; } @Override public InternalNode background(Reference<? extends Drawable> background) { mPrivateFlags |= PFLAG_BACKGROUND_IS_SET; mBackground = background; setPaddingFromDrawableReference(background); return this; } @Override public InternalNode background(Reference.Builder<? extends Drawable> builder) { return background(builder.build()); } @Override public InternalNode background(Drawable background) { return background(DrawableReference.create().drawable(background)); } @Override public InternalNode backgroundAttr(@AttrRes int resId, @DrawableRes int defaultResId) { return backgroundRes(mResourceResolver.resolveResIdAttr(resId, defaultResId)); } @Override public InternalNode backgroundAttr(@AttrRes int resId) { return backgroundAttr(resId, 0); } @Override public InternalNode backgroundRes(@DrawableRes int resId) { if (resId == 0) { return background((Drawable) null); } return background(mComponentContext.getResources().getDrawable(resId)); } @Override public InternalNode backgroundColor(@ColorInt int backgroundColor) { return background(new ColorDrawable(backgroundColor)); } @Override public InternalNode foreground(Drawable foreground) { mPrivateFlags |= PFLAG_FOREGROUND_IS_SET; mForeground = foreground; return this; } @Override public InternalNode foregroundAttr(@AttrRes int resId, @DrawableRes int defaultResId) { return foregroundRes(mResourceResolver.resolveResIdAttr(resId, defaultResId)); } @Override public InternalNode foregroundAttr(@AttrRes int resId) { return foregroundAttr(resId, 0); } @Override public InternalNode foregroundRes(@DrawableRes int resId) { if (resId == 0) { return foreground(null); } return foreground(mResources.getDrawable(resId)); } @Override public InternalNode foregroundColor(@ColorInt int foregroundColor) { return foreground(new ColorDrawable(foregroundColor)); } @Override public InternalNode wrapInView() { mForceViewWrapping = true; return this; } boolean isForceViewWrapping() { return mForceViewWrapping; } @Override public InternalNode clickHandler(EventHandler<ClickEvent> clickHandler) { getOrCreateNodeInfo().setClickHandler(clickHandler); return this; } @Override public InternalNode longClickHandler(EventHandler<LongClickEvent> longClickHandler) { getOrCreateNodeInfo().setLongClickHandler(longClickHandler); return this; } @Override public InternalNode touchHandler(EventHandler<TouchEvent> touchHandler) { getOrCreateNodeInfo().setTouchHandler(touchHandler); return this; } @Override public InternalNode interceptTouchHandler(EventHandler interceptTouchHandler) { getOrCreateNodeInfo().setInterceptTouchHandler(interceptTouchHandler); return this; } @Override public ContainerBuilder focusable(boolean isFocusable) { getOrCreateNodeInfo().setFocusable(isFocusable); return this; } @Override public ContainerBuilder visibleRatio(float visibleRatio) { mVisibleRatio = visibleRatio; return this; } float getVisibleRatio() { return mVisibleRatio; } @Override public InternalNode visibleHandler(EventHandler<VisibleEvent> visibleHandler) { mPrivateFlags |= PFLAG_VISIBLE_HANDLER_IS_SET; mVisibleHandler = visibleHandler; return this; } EventHandler<VisibleEvent> getVisibleHandler() { return mVisibleHandler; } @Override public InternalNode focusedHandler(EventHandler<FocusedVisibleEvent> focusedHandler) { mPrivateFlags |= PFLAG_FOCUSED_HANDLER_IS_SET; mFocusedHandler = focusedHandler; return this; } EventHandler<FocusedVisibleEvent> getFocusedHandler() { return mFocusedHandler; } @Override public InternalNode unfocusedHandler(EventHandler<UnfocusedVisibleEvent> unfocusedHandler) { mPrivateFlags |= PFLAG_UNFOCUSED_HANDLER_IS_SET; mUnfocusedHandler = unfocusedHandler; return this; } EventHandler<UnfocusedVisibleEvent> getUnfocusedHandler() { return mUnfocusedHandler; } @Override public InternalNode fullImpressionHandler( EventHandler<FullImpressionVisibleEvent> fullImpressionHandler) { mPrivateFlags |= PFLAG_FULL_IMPRESSION_HANDLER_IS_SET; mFullImpressionHandler = fullImpressionHandler; return this; } EventHandler<FullImpressionVisibleEvent> getFullImpressionHandler() { return mFullImpressionHandler; } @Override public InternalNode invisibleHandler(EventHandler<InvisibleEvent> invisibleHandler) { mPrivateFlags |= PFLAG_INVISIBLE_HANDLER_IS_SET; mInvisibleHandler = invisibleHandler; return this; } EventHandler<InvisibleEvent> getInvisibleHandler() { return mInvisibleHandler; } @Override public InternalNode contentDescription(CharSequence contentDescription) { getOrCreateNodeInfo().setContentDescription(contentDescription); return this; } @Override public InternalNode contentDescription(@StringRes int stringId) { return contentDescription(mResources.getString(stringId)); } @Override public InternalNode contentDescription(@StringRes int stringId, Object... formatArgs) { return contentDescription(mResources.getString(stringId, formatArgs)); } @Override public InternalNode viewTag(Object viewTag) { getOrCreateNodeInfo().setViewTag(viewTag); return this; } @Override public InternalNode viewTags(SparseArray<Object> viewTags) { getOrCreateNodeInfo().setViewTags(viewTags); return this; } @Override public InternalNode testKey(String testKey) { mTestKey = testKey; return this; } @Override public InternalNode dispatchPopulateAccessibilityEventHandler( EventHandler<DispatchPopulateAccessibilityEventEvent> dispatchPopulateAccessibilityEventHandler) { getOrCreateNodeInfo().setDispatchPopulateAccessibilityEventHandler( dispatchPopulateAccessibilityEventHandler); return this; } @Override public InternalNode onInitializeAccessibilityEventHandler( EventHandler<OnInitializeAccessibilityEventEvent> onInitializeAccessibilityEventHandler) { getOrCreateNodeInfo().setOnInitializeAccessibilityEventHandler( onInitializeAccessibilityEventHandler); return this; } @Override public InternalNode onInitializeAccessibilityNodeInfoHandler( EventHandler<OnInitializeAccessibilityNodeInfoEvent> onInitializeAccessibilityNodeInfoHandler) { getOrCreateNodeInfo().setOnInitializeAccessibilityNodeInfoHandler( onInitializeAccessibilityNodeInfoHandler); return this; } @Override public InternalNode onPopulateAccessibilityEventHandler( EventHandler<OnPopulateAccessibilityEventEvent> onPopulateAccessibilityEventHandler) { getOrCreateNodeInfo().setOnPopulateAccessibilityEventHandler( onPopulateAccessibilityEventHandler); return this; } @Override public InternalNode onRequestSendAccessibilityEventHandler( EventHandler<OnRequestSendAccessibilityEventEvent> onRequestSendAccessibilityEventHandler) { getOrCreateNodeInfo().setOnRequestSendAccessibilityEventHandler( onRequestSendAccessibilityEventHandler); return this; } @Override public InternalNode performAccessibilityActionHandler( EventHandler<PerformAccessibilityActionEvent> performAccessibilityActionHandler) { getOrCreateNodeInfo().setPerformAccessibilityActionHandler(performAccessibilityActionHandler); return this; } @Override public InternalNode sendAccessibilityEventHandler( EventHandler<SendAccessibilityEventEvent> sendAccessibilityEventHandler) { getOrCreateNodeInfo().setSendAccessibilityEventHandler(sendAccessibilityEventHandler); return this; } @Override public InternalNode sendAccessibilityEventUncheckedHandler( EventHandler<SendAccessibilityEventUncheckedEvent> sendAccessibilityEventUncheckedHandler) { getOrCreateNodeInfo().setSendAccessibilityEventUncheckedHandler( sendAccessibilityEventUncheckedHandler); return this; } @Override public ContainerBuilder transitionKey(String key) { if (SDK_INT >= ICE_CREAM_SANDWICH && !TextUtils.isEmpty(key)) { mPrivateFlags |= PFLAG_TRANSITION_KEY_IS_SET; mTransitionKey = key; wrapInView(); } return this; } String getTransitionKey() { return mTransitionKey; } /** * A unique identifier which may be set for retrieving a component and its bounds when testing. */ String getTestKey() { return mTestKey; } void setMeasureFunction(YogaMeasureFunction measureFunction) { mYogaNode.setMeasureFunction(measureFunction); } void setBaselineFunction(YogaBaselineFunction baselineFunction) { mYogaNode.setBaselineFunction(baselineFunction); } boolean hasNewLayout() { return mYogaNode.hasNewLayout(); } void markLayoutSeen() { mYogaNode.markLayoutSeen(); } float getStyleWidth() { return mYogaNode.getWidth().value; } float getMinWidth() { return mYogaNode.getMinWidth().value; } float getMaxWidth() { return mYogaNode.getMaxWidth().value; } float getStyleHeight() { return mYogaNode.getHeight().value; } float getMinHeight() { return mYogaNode.getMinHeight().value; } float getMaxHeight() { return mYogaNode.getMaxHeight().value; } void calculateLayout(float width, float height) { if (ComponentsConfiguration.isDebugModeEnabled) { applyOverridesRecursive(this); } mYogaNode.calculateLayout(width, height); } private static void applyOverridesRecursive(InternalNode node) { DebugComponent.getInstance(node, 0).applyOverrides(); for (int i = 0, count = node.getChildCount(); i < count; i++) { applyOverridesRecursive(node.getChildAt(i)); } if (node.hasNestedTree()) { applyOverridesRecursive(node.getNestedTree()); } } void calculateLayout() { calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED); } int getChildCount() { return mYogaNode.getChildCount(); } com.facebook.yoga.YogaDirection getStyleDirection() { return mYogaNode.getStyleDirection(); } InternalNode getChildAt(int index) { if (mYogaNode.getChildAt(index) == null) { return null; } return (InternalNode) mYogaNode.getChildAt(index).getData(); } int getChildIndex(InternalNode child) { for (int i = 0, count = mYogaNode.getChildCount(); i < count; i++) { if (mYogaNode.getChildAt(i) == child.mYogaNode) { return i; } } return -1; } InternalNode getParent() { if (mYogaNode == null || mYogaNode.getParent() == null) { return null; } return (InternalNode) mYogaNode.getParent().getData(); } void addChildAt(InternalNode child, int index) { mYogaNode.addChildAt(child.mYogaNode, index); } InternalNode removeChildAt(int index) { return (InternalNode) mYogaNode.removeChildAt(index).getData(); } @Override public ComponentLayout build() { return this; } private float resolveHorizontalEdges(Edges spacing, YogaEdge edge) { final boolean isRtl = (mYogaNode.getLayoutDirection() == YogaDirection.RTL); final YogaEdge resolvedEdge; switch (edge) { case LEFT: resolvedEdge = (isRtl ? YogaEdge.END : YogaEdge.START); break; case RIGHT: resolvedEdge = (isRtl ? YogaEdge.START : YogaEdge.END); break; default: throw new IllegalArgumentException("Not an horizontal padding edge: " + edge); } float result = spacing.getRaw(resolvedEdge); if (YogaConstants.isUndefined(result)) { result = spacing.get(edge); } return result; } ComponentContext getContext() { return mComponentContext; } /** * Return the list of components contributing to this InternalNode. We have no need for this * in production but it is useful information to have while debugging. Therefor this list * will only container the root component if running in production mode. */ List<Component> getComponents() { return mComponents; } Component getRootComponent() { return mComponents.size() == 0 ? null : mComponents.get(0); } int getBorderColor() { return mBorderColor; } boolean shouldDrawBorders() { return mBorderColor != Color.TRANSPARENT && (mYogaNode.getLayoutBorder(LEFT) != 0 || mYogaNode.getLayoutBorder(TOP) != 0 || mYogaNode.getLayoutBorder(RIGHT) != 0 || mYogaNode.getLayoutBorder(BOTTOM) != 0); } /** * Set the root component associated with this internal node. This is the component which created * this internal node. If we are in debug mode we also keep track of any delegate components * which may have altered anything about this internal node. This is useful when understanding * the hierarchy of components in the debugger as well as in stetho. */ void appendComponent(Component component) { if (mComponents.size() == 0 || ComponentsConfiguration.isDebugModeEnabled) { mComponents.add(component); } } boolean hasNestedTree() { return mNestedTree != null; } @Nullable InternalNode getNestedTree() { return mNestedTree; } InternalNode getNestedTreeHolder() { return mNestedTreeHolder; } /** * Set the nested tree before measuring it in order to transfer over important information * such as layout direction needed during measurement. */ void setNestedTree(InternalNode nestedTree) { nestedTree.mNestedTreeHolder = this; mNestedTree = nestedTree; } NodeInfo getNodeInfo() { return mNodeInfo; } void copyInto(InternalNode node) { if (mNodeInfo != null) { if (node.mNodeInfo == null) { node.mNodeInfo = mNodeInfo.acquireRef(); } else { node.mNodeInfo.updateWith(mNodeInfo); } } if ((node.mPrivateFlags & PFLAG_LAYOUT_DIRECTION_IS_SET) == 0L || node.getResolvedLayoutDirection() == YogaDirection.INHERIT) { node.layoutDirection(getResolvedLayoutDirection()); } if ((node.mPrivateFlags & PFLAG_IMPORTANT_FOR_ACCESSIBILITY_IS_SET) == 0L || node.mImportantForAccessibility == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { node.mImportantForAccessibility = mImportantForAccessibility; } if ((mPrivateFlags & PFLAG_DUPLICATE_PARENT_STATE_IS_SET) != 0L) { node.mDuplicateParentState = mDuplicateParentState; } if ((mPrivateFlags & PFLAG_BACKGROUND_IS_SET) != 0L) { node.mBackground = mBackground; } if ((mPrivateFlags & PFLAG_FOREGROUND_IS_SET) != 0L) { node.mForeground = mForeground; } if (mForceViewWrapping) { node.mForceViewWrapping = true; } if ((mPrivateFlags & PFLAG_VISIBLE_HANDLER_IS_SET) != 0L) { node.mVisibleHandler = mVisibleHandler; } if ((mPrivateFlags & PFLAG_FOCUSED_HANDLER_IS_SET) != 0L) { node.mFocusedHandler = mFocusedHandler; } if ((mPrivateFlags & PFLAG_FULL_IMPRESSION_HANDLER_IS_SET) != 0L) { node.mFullImpressionHandler = mFullImpressionHandler; } if ((mPrivateFlags & PFLAG_INVISIBLE_HANDLER_IS_SET) != 0L) { node.mInvisibleHandler = mInvisibleHandler; } if ((mPrivateFlags & PFLAG_UNFOCUSED_HANDLER_IS_SET) != 0L) { node.mUnfocusedHandler = mUnfocusedHandler; } if (mTestKey != null) { node.mTestKey = mTestKey; } if ((mPrivateFlags & PFLAG_PADDING_IS_SET) != 0L) { if (mNestedTreePadding == null) { throw new IllegalStateException("copyInto() must be used when resolving a nestedTree. " + "If padding was set on the holder node, we must have a mNestedTreePadding instance"); } final YogaNode yogaNode = node.mYogaNode; node.mPrivateFlags |= PFLAG_PADDING_IS_SET; if (isPaddingPercent(LEFT)) { yogaNode.setPaddingPercent(LEFT, mNestedTreePadding.getRaw(YogaEdge.LEFT)); } else { yogaNode.setPadding(LEFT, mNestedTreePadding.getRaw(YogaEdge.LEFT)); } if (isPaddingPercent(TOP)) { yogaNode.setPaddingPercent(TOP, mNestedTreePadding.getRaw(YogaEdge.TOP)); } else { yogaNode.setPadding(TOP, mNestedTreePadding.getRaw(YogaEdge.TOP)); } if (isPaddingPercent(RIGHT)) { yogaNode.setPaddingPercent(RIGHT, mNestedTreePadding.getRaw(YogaEdge.RIGHT)); } else { yogaNode.setPadding(RIGHT, mNestedTreePadding.getRaw(YogaEdge.RIGHT)); } if (isPaddingPercent(BOTTOM)) { yogaNode.setPaddingPercent(BOTTOM, mNestedTreePadding.getRaw(YogaEdge.BOTTOM)); } else { yogaNode.setPadding(BOTTOM, mNestedTreePadding.getRaw(YogaEdge.BOTTOM)); } if (isPaddingPercent(VERTICAL)) { yogaNode.setPaddingPercent(VERTICAL, mNestedTreePadding.getRaw(YogaEdge.VERTICAL)); } else { yogaNode.setPadding(VERTICAL, mNestedTreePadding.getRaw(YogaEdge.VERTICAL)); } if (isPaddingPercent(HORIZONTAL)) { yogaNode.setPaddingPercent(HORIZONTAL, mNestedTreePadding.getRaw(YogaEdge.HORIZONTAL)); } else { yogaNode.setPadding(HORIZONTAL, mNestedTreePadding.getRaw(YogaEdge.HORIZONTAL)); } if (isPaddingPercent(START)) { yogaNode.setPaddingPercent(START, mNestedTreePadding.getRaw(YogaEdge.START)); } else { yogaNode.setPadding(START, mNestedTreePadding.getRaw(YogaEdge.START)); } if (isPaddingPercent(END)) { yogaNode.setPaddingPercent(END, mNestedTreePadding.getRaw(YogaEdge.END)); } else { yogaNode.setPadding(END, mNestedTreePadding.getRaw(YogaEdge.END)); } if (isPaddingPercent(ALL)) { yogaNode.setPaddingPercent(ALL, mNestedTreePadding.getRaw(YogaEdge.ALL)); } else { yogaNode.setPadding(ALL, mNestedTreePadding.getRaw(YogaEdge.ALL)); } } if ((mPrivateFlags & PFLAG_BORDER_WIDTH_IS_SET) != 0L) { if (mNestedTreeBorderWidth == null) { throw new IllegalStateException("copyInto() must be used when resolving a nestedTree. " + "If border width was set on the holder node, we must have a mNestedTreeBorderWidth " + "instance"); } final YogaNode yogaNode = node.mYogaNode; node.mPrivateFlags |= PFLAG_BORDER_WIDTH_IS_SET; yogaNode.setBorder(LEFT, mNestedTreeBorderWidth.getRaw(YogaEdge.LEFT)); yogaNode.setBorder(TOP, mNestedTreeBorderWidth.getRaw(YogaEdge.TOP)); yogaNode.setBorder(RIGHT, mNestedTreeBorderWidth.getRaw(YogaEdge.RIGHT)); yogaNode.setBorder(BOTTOM, mNestedTreeBorderWidth.getRaw(YogaEdge.BOTTOM)); yogaNode.setBorder(VERTICAL, mNestedTreeBorderWidth.getRaw(YogaEdge.VERTICAL)); yogaNode.setBorder(HORIZONTAL, mNestedTreeBorderWidth.getRaw(YogaEdge.HORIZONTAL)); yogaNode.setBorder(START, mNestedTreeBorderWidth.getRaw(YogaEdge.START)); yogaNode.setBorder(END, mNestedTreeBorderWidth.getRaw(YogaEdge.END)); yogaNode.setBorder(ALL, mNestedTreeBorderWidth.getRaw(YogaEdge.ALL)); } if ((mPrivateFlags & PFLAG_TRANSITION_KEY_IS_SET) != 0L) { node.mTransitionKey = mTransitionKey; } if ((mPrivateFlags & PFLAG_BORDER_COLOR_IS_SET) != 0L) { node.mBorderColor = mBorderColor; } if (mVisibleRatio != 0) { node.mVisibleRatio = mVisibleRatio; } } void setStyleWidthFromSpec(int widthSpec) { switch (SizeSpec.getMode(widthSpec)) { case SizeSpec.UNSPECIFIED: mYogaNode.setWidth(YogaConstants.UNDEFINED); break; case SizeSpec.AT_MOST: mYogaNode.setMaxWidth(SizeSpec.getSize(widthSpec)); break; case SizeSpec.EXACTLY: mYogaNode.setWidth(SizeSpec.getSize(widthSpec)); break; } } void setStyleHeightFromSpec(int heightSpec) { switch (SizeSpec.getMode(heightSpec)) { case SizeSpec.UNSPECIFIED: mYogaNode.setHeight(YogaConstants.UNDEFINED); break; case SizeSpec.AT_MOST: mYogaNode.setMaxHeight(SizeSpec.getSize(heightSpec)); break; case SizeSpec.EXACTLY: mYogaNode.setHeight(SizeSpec.getSize(heightSpec)); break; } } int getImportantForAccessibility() { return mImportantForAccessibility; } boolean isDuplicateParentStateEnabled() { return mDuplicateParentState; } void applyAttributes(TypedArray a) { for (int i = 0, size = a.getIndexCount(); i < size; i++) { final int attr = a.getIndex(i); if (attr == R.styleable.ComponentLayout_android_layout_width) { int width = a.getLayoutDimension(attr, -1); // We don't support WRAP_CONTENT or MATCH_PARENT so no-op for them if (width >= 0) { widthPx(width); } } else if (attr == R.styleable.ComponentLayout_android_layout_height) { int height = a.getLayoutDimension(attr, -1); // We don't support WRAP_CONTENT or MATCH_PARENT so no-op for them if (height >= 0) { heightPx(height); } } else if (attr == R.styleable.ComponentLayout_android_paddingLeft) { paddingPx(LEFT, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_paddingTop) { paddingPx(TOP, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_paddingRight) { paddingPx(RIGHT, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_paddingBottom) { paddingPx(BOTTOM, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_paddingStart && SUPPORTS_RTL) { paddingPx(START, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_paddingEnd && SUPPORTS_RTL) { paddingPx(END, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_padding) { paddingPx(ALL, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_layout_marginLeft) { marginPx(LEFT, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_layout_marginTop) { marginPx(TOP, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_layout_marginRight) { marginPx(RIGHT, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_layout_marginBottom) { marginPx(BOTTOM, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_layout_marginStart && SUPPORTS_RTL) { marginPx(START, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_layout_marginEnd && SUPPORTS_RTL) { marginPx(END, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_layout_margin) { marginPx(ALL, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_importantForAccessibility && SDK_INT >= JELLY_BEAN) { importantForAccessibility(a.getInt(attr, 0)); } else if (attr == R.styleable.ComponentLayout_android_duplicateParentState) { duplicateParentState(a.getBoolean(attr, false)); } else if (attr == R.styleable.ComponentLayout_android_background) { if (TypedArrayUtils.isColorAttribute(a, R.styleable.ComponentLayout_android_background)) { backgroundColor(a.getColor(attr, 0)); } else { backgroundRes(a.getResourceId(attr, -1)); } } else if (attr == R.styleable.ComponentLayout_android_foreground) { if (TypedArrayUtils.isColorAttribute(a, R.styleable.ComponentLayout_android_foreground)) { foregroundColor(a.getColor(attr, 0)); } else { foregroundRes(a.getResourceId(attr, -1)); } } else if (attr == R.styleable.ComponentLayout_android_contentDescription) { contentDescription(a.getString(attr)); } else if (attr == R.styleable.ComponentLayout_flex_direction) { flexDirection(YogaFlexDirection.fromInt(a.getInteger(attr, 0))); } else if (attr == R.styleable.ComponentLayout_flex_wrap) { wrap(YogaWrap.fromInt(a.getInteger(attr, 0))); } else if (attr == R.styleable.ComponentLayout_flex_justifyContent) { justifyContent(YogaJustify.fromInt(a.getInteger(attr, 0))); } else if (attr == R.styleable.ComponentLayout_flex_alignItems) { alignItems(YogaAlign.fromInt(a.getInteger(attr, 0))); } else if (attr == R.styleable.ComponentLayout_flex_alignSelf) { alignSelf(YogaAlign.fromInt(a.getInteger(attr, 0))); } else if (attr == R.styleable.ComponentLayout_flex_positionType) { positionType(YogaPositionType.fromInt(a.getInteger(attr, 0))); } else if (attr == R.styleable.ComponentLayout_flex) { final float flex = a.getFloat(attr, -1); if (flex >= 0f) { flex(flex); } } else if (attr == R.styleable.ComponentLayout_flex_left) { positionPx(LEFT, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_flex_top) { positionPx(TOP, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_flex_right) { positionPx(RIGHT, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_flex_bottom) { positionPx(BOTTOM, a.getDimensionPixelOffset(attr, 0)); } else if (attr == R.styleable.ComponentLayout_flex_layoutDirection) { final int layoutDirection = a.getInteger(attr, -1); layoutDirection(YogaDirection.fromInt(layoutDirection)); } } } /** * Reset all attributes to default values. Intended to facilitate recycling. */ void release() { if (mYogaNode.getParent() != null || mYogaNode.getChildCount() > 0) { throw new IllegalStateException("You should not free an attached Internalnode"); } ComponentsPools.release(mYogaNode); mYogaNode = null; mResourceResolver.internalRelease(); mResolvedTouchExpansionLeft = YogaConstants.UNDEFINED; mResolvedTouchExpansionRight = YogaConstants.UNDEFINED; mResolvedX = YogaConstants.UNDEFINED; mResolvedY = YogaConstants.UNDEFINED; mResolvedWidth = YogaConstants.UNDEFINED; mResolvedHeight = YogaConstants.UNDEFINED; mComponentContext = null; mResources = null; mComponents.clear(); mNestedTree = null; mNestedTreeHolder = null; if (mNodeInfo != null) { mNodeInfo.release(); mNodeInfo = null; } mImportantForAccessibility = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO; mDuplicateParentState = false; mBackground = null; mForeground = null; mForceViewWrapping = false; mVisibleRatio = 0; mVisibleHandler = null; mFocusedHandler = null; mUnfocusedHandler = null; mFullImpressionHandler = null; mInvisibleHandler = null; mPrivateFlags = 0L; mTransitionKey = null; mBorderColor = Color.TRANSPARENT; mIsPaddingPercent = null; if (mTouchExpansion != null) { ComponentsPools.release(mTouchExpansion); mTouchExpansion = null; } if (mNestedTreePadding != null) { ComponentsPools.release(mNestedTreePadding); mNestedTreePadding = null; } if (mNestedTreeBorderWidth != null) { ComponentsPools.release(mNestedTreeBorderWidth); mNestedTreeBorderWidth = null; } mLastWidthSpec = DiffNode.UNSPECIFIED; mLastHeightSpec = DiffNode.UNSPECIFIED; mLastMeasuredHeight = DiffNode.UNSPECIFIED; mLastMeasuredWidth = DiffNode.UNSPECIFIED; mDiffNode = null; mCachedMeasuresValid = false; mIsNestedTreeHolder = false; mTestKey = null; if (mPendingTreeProps != null) { mPendingTreeProps.reset(); ComponentsPools.release(mPendingTreeProps); mPendingTreeProps = null; } } private NodeInfo getOrCreateNodeInfo() { if (mNodeInfo == null) { mNodeInfo = NodeInfo.acquire(); } return mNodeInfo; } /** * Check that the root of the nested tree we are going to use, has valid layout directions * with its main tree holder node. */ static boolean hasValidLayoutDirectionInNestedTree( InternalNode nestedTreeHolder, InternalNode nestedTree) { final boolean nestedTreeHasExplicitDirection = ((nestedTree.mPrivateFlags & PFLAG_LAYOUT_DIRECTION_IS_SET) != 0L); final boolean hasSameLayoutDirection = (nestedTree.getResolvedLayoutDirection() == nestedTreeHolder.getResolvedLayoutDirection()); return nestedTreeHasExplicitDirection || hasSameLayoutDirection; } /** * Adds an item to a possibly nulled list to defer the allocation as long as possible. */ private static <A> List<A> addOrCreateList(@Nullable List<A> list, A item) { if (list == null) { list = new LinkedList<>(); } list.add(item); return list; } private void setIsPaddingPercent(YogaEdge edge, boolean isPaddingPercent) { if (mIsPaddingPercent == null && isPaddingPercent) { mIsPaddingPercent = new boolean[YogaEdge.ALL.intValue() + 1]; } if (mIsPaddingPercent != null) { mIsPaddingPercent[edge.intValue()] = isPaddingPercent; } } private boolean isPaddingPercent(YogaEdge edge) { return (mIsPaddingPercent == null) ? false : mIsPaddingPercent[edge.intValue()]; } /** * Crash if the given node has context specific style set. */ static void assertContextSpecificStyleNotSet(InternalNode node) { List<CharSequence> errorTypes = null; if ((node.mPrivateFlags & PFLAG_ALIGN_SELF_IS_SET) != 0L) { errorTypes = addOrCreateList(errorTypes, "alignSelf"); } if ((node.mPrivateFlags & PFLAG_POSITION_TYPE_IS_SET) != 0L) { errorTypes = addOrCreateList(errorTypes, "positionType"); } if ((node.mPrivateFlags & PFLAG_FLEX_IS_SET) != 0L) { errorTypes = addOrCreateList(errorTypes, "flex"); } if ((node.mPrivateFlags & PFLAG_FLEX_GROW_IS_SET) != 0L) { errorTypes = addOrCreateList(errorTypes, "flexGrow"); } if ((node.mPrivateFlags & PFLAG_MARGIN_IS_SET) != 0L) { errorTypes = addOrCreateList(errorTypes, "margin"); } if (errorTypes != null) { final CharSequence errorStr = TextUtils.join(", ", errorTypes); throw new IllegalStateException("You should not set " + errorStr + " to a root layout in " + node.getRootComponent().getLifecycle()); } } public TreeProps getPendingTreeProps() { return mPendingTreeProps; } private <T extends Drawable> void setPaddingFromDrawableReference(Reference<T> ref) { if (ref == null) { return; } final T drawable = Reference.acquire(mComponentContext,ref); if (drawable != null) { final Rect backgroundPadding = ComponentsPools.acquireRect(); if (drawable.getPadding(backgroundPadding)) { paddingPx(LEFT, backgroundPadding.left); paddingPx(TOP, backgroundPadding.top); paddingPx(RIGHT, backgroundPadding.right); paddingPx(BOTTOM, backgroundPadding.bottom); } Reference.release(mComponentContext, drawable, ref); ComponentsPools.release(backgroundPadding); } } }