diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawBorder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawBorder.java deleted file mode 100644 index 9ebbf1baa..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawBorder.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PathEffect; -import android.graphics.RectF; - -/** - * Base class for border drawing operations (used by DrawImage and DrawBorder). Draws rectangular or - * rounded border along the rectangle, defined by the bounding box of the AbstractDrawCommand. - */ -/* package */ abstract class AbstractDrawBorder extends AbstractDrawCommand { - - private static final Paint PAINT = new Paint(Paint.ANTI_ALIAS_FLAG); - private static final RectF TMP_RECT = new RectF(); - private static final int BORDER_PATH_DIRTY = 1 << 0; - - static { - PAINT.setStyle(Paint.Style.STROKE); - } - - private int mSetPropertiesFlag; - private int mBorderColor = Color.BLACK; - private float mBorderWidth; - private float mBorderRadius; - private @Nullable Path mPathForBorderRadius; - - public final void setBorderWidth(float borderWidth) { - mBorderWidth = borderWidth; - setFlag(BORDER_PATH_DIRTY); - } - - public final float getBorderWidth() { - return mBorderWidth; - } - - public void setBorderRadius(float borderRadius) { - mBorderRadius = borderRadius; - setFlag(BORDER_PATH_DIRTY); - } - - public final float getBorderRadius() { - return mBorderRadius; - } - - public final void setBorderColor(int borderColor) { - mBorderColor = borderColor; - } - - public final int getBorderColor() { - return mBorderColor; - } - - @Override - protected void onBoundsChanged() { - setFlag(BORDER_PATH_DIRTY); - } - - protected final void drawBorders(Canvas canvas) { - if (mBorderWidth < 0.5f) { - return; - } - - if (mBorderColor == 0) { - return; - } - - PAINT.setColor(mBorderColor); - PAINT.setStrokeWidth(mBorderWidth); - PAINT.setPathEffect(getPathEffectForBorderStyle()); - canvas.drawPath(getPathForBorderRadius(), PAINT); - } - - protected final void updatePath(Path path, float correction) { - path.reset(); - - TMP_RECT.set( - getLeft() + correction, - getTop() + correction, - getRight() - correction, - getBottom() - correction); - - path.addRoundRect( - TMP_RECT, - mBorderRadius, - mBorderRadius, - Path.Direction.CW); - } - - protected @Nullable PathEffect getPathEffectForBorderStyle() { - return null; - } - - protected final boolean isFlagSet(int mask) { - return (mSetPropertiesFlag & mask) == mask; - } - - protected final void setFlag(int mask) { - mSetPropertiesFlag |= mask; - } - - protected final void resetFlag(int mask) { - mSetPropertiesFlag &= ~mask; - } - - protected final Path getPathForBorderRadius() { - if (isFlagSet(BORDER_PATH_DIRTY)) { - if (mPathForBorderRadius == null) { - mPathForBorderRadius = new Path(); - } - - updatePath(mPathForBorderRadius, mBorderWidth * 0.5f); - - resetFlag(BORDER_PATH_DIRTY); - } - - return mPathForBorderRadius; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawCommand.java b/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawCommand.java deleted file mode 100644 index fd924b8d1..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawCommand.java +++ /dev/null @@ -1,299 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Typeface; - -/** - * Base class for all DrawCommands. Becomes immutable once it has its bounds set. Until then, a - * subclass is able to mutate any of its properties (e.g. updating Layout in DrawTextLayout). - * - * The idea is to be able to reuse unmodified objects when we build up DrawCommands before we ship - * them to UI thread, but we can only do that if DrawCommands are immutable. - */ -/* package */ abstract class AbstractDrawCommand extends DrawCommand implements Cloneable { - - private float mLeft; - private float mTop; - private float mRight; - private float mBottom; - private boolean mFrozen; - - protected boolean mNeedsClipping; - private float mClipLeft; - private float mClipTop; - private float mClipRight; - private float mClipBottom; - - // Used to draw highlights in debug draw. - private static Paint sDebugHighlightRed; - private static Paint sDebugHighlightYellow; - private static Paint sDebugHighlightOverlayText; - - public final boolean clipBoundsMatch( - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - return mClipLeft == clipLeft && mClipTop == clipTop - && mClipRight == clipRight && mClipBottom == clipBottom; - } - - protected final void setClipBounds( - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - mClipLeft = clipLeft; - mClipTop = clipTop; - mClipRight = clipRight; - mClipBottom = clipBottom; - // We put this check here to not clip when we have the default [-infinity, infinity] bounds, - // since clipRect in those cases is essentially no-op anyway. This is needed to fix a bug that - // shows up during screenshot testing. Note that checking one side is enough, since if one side - // is infinite, all sides will be infinite, since we only set infinite for all sides at the - // same time - conversely, if one side is finite, all sides will be finite. - mNeedsClipping = mClipLeft != Float.NEGATIVE_INFINITY; - } - - public final float getClipLeft() { - return mClipLeft; - } - - public final float getClipTop() { - return mClipTop; - } - - public final float getClipRight() { - return mClipRight; - } - - public final float getClipBottom() { - return mClipBottom; - } - - protected void applyClipping(Canvas canvas) { - canvas.clipRect(mClipLeft, mClipTop, mClipRight, mClipBottom); - } - - /** - * Don't override this unless you need to do custom clipping in a draw command. Otherwise just - * override onPreDraw and onDraw. - */ - @Override - public void draw(FlatViewGroup parent, Canvas canvas) { - onPreDraw(parent, canvas); - if (mNeedsClipping && shouldClip()) { - canvas.save(Canvas.CLIP_SAVE_FLAG); - applyClipping(canvas); - onDraw(canvas); - canvas.restore(); - } else { - onDraw(canvas); - } - } - - protected static int getDebugBorderColor() { - return Color.CYAN; - } - - protected String getDebugName() { - return getClass().getSimpleName().substring(4); - } - - private void initDebugHighlightResources(FlatViewGroup parent) { - if (sDebugHighlightRed == null) { - sDebugHighlightRed = new Paint(); - sDebugHighlightRed.setARGB(75, 255, 0, 0); - } - if (sDebugHighlightYellow == null) { - sDebugHighlightYellow = new Paint(); - sDebugHighlightYellow.setARGB(100, 255, 204, 0); - } - if (sDebugHighlightOverlayText == null) { - sDebugHighlightOverlayText = new Paint(); - sDebugHighlightOverlayText.setAntiAlias(true); - sDebugHighlightOverlayText.setARGB(200, 50, 50, 50); - sDebugHighlightOverlayText.setTextAlign(Paint.Align.RIGHT); - sDebugHighlightOverlayText.setTypeface(Typeface.MONOSPACE); - sDebugHighlightOverlayText.setTextSize(parent.dipsToPixels(9)); - } - } - - private void debugDrawHighlightRect(Canvas canvas, Paint paint, String text) { - canvas.drawRect(getLeft(), getTop(), getRight(), getBottom(), paint); - canvas.drawText(text, getRight() - 5, getBottom() - 5, sDebugHighlightOverlayText); - } - - protected void debugDrawWarningHighlight(Canvas canvas, String text) { - debugDrawHighlightRect(canvas, sDebugHighlightRed, text); - } - - protected void debugDrawCautionHighlight(Canvas canvas, String text) { - debugDrawHighlightRect(canvas, sDebugHighlightYellow, text); - } - - @Override - public final void debugDraw(FlatViewGroup parent, Canvas canvas) { - onDebugDraw(parent, canvas); - if (FlatViewGroup.DEBUG_HIGHLIGHT_PERFORMANCE_ISSUES) { - initDebugHighlightResources(parent); - onDebugDrawHighlight(canvas); - } - } - - protected void onDebugDraw(FlatViewGroup parent, Canvas canvas) { - parent.debugDrawNamedRect( - canvas, - getDebugBorderColor(), - getDebugName(), - mLeft, - mTop, - mRight, - mBottom); - } - - protected void onDebugDrawHighlight(Canvas canvas) { - } - - protected void onPreDraw(FlatViewGroup parent, Canvas canvas) { - } - - /** - * Updates boundaries of the AbstractDrawCommand and freezes it. - * Will return a frozen copy if the current AbstractDrawCommand cannot be mutated. - * - * This should not be called on a DrawView, as the DrawView is modified on UI thread. Use - * DrawView.collectDrawView instead to avoid race conditions. - */ - public AbstractDrawCommand updateBoundsAndFreeze( - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - if (mFrozen) { - // see if we can reuse it - boolean boundsMatch = boundsMatch(left, top, right, bottom); - boolean clipBoundsMatch = clipBoundsMatch(clipLeft, clipTop, clipRight, clipBottom); - if (boundsMatch && clipBoundsMatch) { - return this; - } - - try { - AbstractDrawCommand copy = (AbstractDrawCommand) clone(); - if (!boundsMatch) { - copy.setBounds(left, top, right, bottom); - } - if (!clipBoundsMatch) { - copy.setClipBounds(clipLeft, clipTop, clipRight, clipBottom); - } - return copy; - } catch (CloneNotSupportedException e) { - // This should not happen since AbstractDrawCommand implements Cloneable - throw new RuntimeException(e); - } - } - - setBounds(left, top, right, bottom); - setClipBounds(clipLeft, clipTop, clipRight, clipBottom); - mFrozen = true; - return this; - } - - /** - * Returns a non-frozen shallow copy of AbstractDrawCommand as defined by {@link Object#clone()}. - */ - public final AbstractDrawCommand mutableCopy() { - try { - AbstractDrawCommand copy = (AbstractDrawCommand) super.clone(); - copy.mFrozen = false; - return copy; - } catch (CloneNotSupportedException e) { - // should not happen since we implement Cloneable - throw new RuntimeException(e); - } - } - - /** - * Returns whether this object was frozen and thus cannot be mutated. - */ - public final boolean isFrozen() { - return mFrozen; - } - - /** - * Mark this object as frozen, indicating that it should not be mutated. - */ - public final void freeze() { - mFrozen = true; - } - - /** - * Left position of this DrawCommand relative to the hosting View. - */ - public final float getLeft() { - return mLeft; - } - - /** - * Top position of this DrawCommand relative to the hosting View. - */ - public final float getTop() { - return mTop; - } - - /** - * Right position of this DrawCommand relative to the hosting View. - */ - public final float getRight() { - return mRight; - } - - /** - * Bottom position of this DrawCommand relative to the hosting View. - */ - public final float getBottom() { - return mBottom; - } - - protected abstract void onDraw(Canvas canvas); - - protected boolean shouldClip() { - return mLeft < getClipLeft() || mTop < getClipTop() || - mRight > getClipRight() || mBottom > getClipBottom(); - } - - protected void onBoundsChanged() { - } - - /** - * Updates boundaries of this DrawCommand. - */ - protected final void setBounds(float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - - onBoundsChanged(); - } - - /** - * Returns true if boundaries match and don't need to be updated. False otherwise. - */ - protected final boolean boundsMatch(float left, float top, float right, float bottom) { - return mLeft == left && mTop == top && mRight == right && mBottom == bottom; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/AndroidView.java b/ReactAndroid/src/main/java/com/facebook/react/flat/AndroidView.java deleted file mode 100644 index 9658620ac..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/AndroidView.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -interface AndroidView { - - /** - * Whether or not custom layout is needed for the children - * @return a boolean representing whether custom layout is needed - */ - boolean needsCustomLayoutForChildren(); - - /** - * Did the padding change - * @return a boolean representing whether the padding changed - */ - boolean isPaddingChanged(); - - /** - * Reset the padding changed internal state - */ - void resetPaddingChanged(); - - /** - * Get the padding for a certain spacingType defined in com.facebook.yoga.Spacing - */ - float getPadding(int spacingType); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/AttachDetachListener.java b/ReactAndroid/src/main/java/com/facebook/react/flat/AttachDetachListener.java deleted file mode 100644 index 1498380f3..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/AttachDetachListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -/** - * An interface that DrawCommands need to implement into order to receive - * {@link android.view.View#onAttachedToWindow()} and - * {@link android.view.View#onDetachedFromWindow()} events. - */ -/* package */ interface AttachDetachListener { - public static final AttachDetachListener[] EMPTY_ARRAY = new AttachDetachListener[0]; - - /** - * Called when a DrawCommand is being attached to a visible View hierarchy. - * @param callback a WeakReference to a View that provides invalidate() helper method. - */ - public void onAttached(FlatViewGroup.InvalidateCallback callback); - - /** - * Called when a DrawCommand is being detached from a visible View hierarchy. - */ - public void onDetached(); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/BUCK b/ReactAndroid/src/main/java/com/facebook/react/flat/BUCK deleted file mode 100644 index bff3b9366..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/BUCK +++ /dev/null @@ -1,38 +0,0 @@ -load("//ReactNative:DEFS.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library") - -rn_android_library( - name = "flat", - srcs = glob(["*.java"]), - provided_deps = [ - react_native_dep("third-party/android/support/v4:lib-support-v4"), - ], - visibility = [ - "PUBLIC", - ], - deps = [ - YOGA_TARGET, - react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), - react_native_dep("libraries/fresco/fresco-react-native:fbcore"), - react_native_dep("libraries/fresco/fresco-react-native:fresco-drawee"), - react_native_dep("libraries/fresco/fresco-react-native:fresco-react-native"), - react_native_dep("libraries/fresco/fresco-react-native:imagepipeline"), - react_native_dep("libraries/textlayoutbuilder:textlayoutbuilder"), - react_native_dep("third-party/java/infer-annotations:infer-annotations"), - react_native_dep("third-party/java/jsr-305:jsr-305"), - react_native_target("java/com/facebook/react/bridge:bridge"), - react_native_target("java/com/facebook/react/common:common"), - react_native_target("java/com/facebook/react/modules/fresco:fresco"), - react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"), - react_native_target("java/com/facebook/react/touch:touch"), - react_native_target("java/com/facebook/react/uimanager:uimanager"), - react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), - react_native_target("java/com/facebook/react/views/art:art"), - react_native_target("java/com/facebook/react/views/image:image"), - react_native_target("java/com/facebook/react/views/imagehelper:withmultisource"), - react_native_target("java/com/facebook/react/views/modal:modal"), - react_native_target("java/com/facebook/react/views/text:text"), - react_native_target("java/com/facebook/react/views/textinput:textinput"), - react_native_target("java/com/facebook/react/views/view:view"), - react_native_target("java/com/facebook/react/views/viewpager:viewpager"), - ], -) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/BitmapUpdateListener.java b/ReactAndroid/src/main/java/com/facebook/react/flat/BitmapUpdateListener.java deleted file mode 100644 index 3eeb4888d..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/BitmapUpdateListener.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Bitmap; - -/* package */ interface BitmapUpdateListener { - public void onSecondaryAttach(Bitmap bitmap); - public void onBitmapReady(Bitmap bitmap); - public void onImageLoadEvent(int imageLoadEvent); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/ClippingDrawCommandManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/ClippingDrawCommandManager.java deleted file mode 100644 index 390f8f922..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/ClippingDrawCommandManager.java +++ /dev/null @@ -1,650 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import java.util.ArrayList; -import java.util.Arrays; - -import android.graphics.Canvas; -import android.graphics.Rect; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.View; -import android.view.animation.Animation; - -import com.facebook.common.logging.FLog; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.uimanager.ReactClippingViewGroup; -import com.facebook.react.uimanager.ReactClippingViewGroupHelper; - -/** - * Abstract class for a {@link DrawCommandManager} with directional clipping. Allows support for - * vertical and horizontal clipping by implementing abstract methods. - * - * Uses two dynamic programming arrays to efficiently update which views and commands are onscreen, - * while not having to sort the incoming draw commands. The draw commands are loosely sorted, as - * they represent a flattening of the normal view hierarchy, and we use that information to quickly - * find children that should be considered onscreen. One array keeps track of, for each index, the - * maximum bottom position that occurs at or before that index; the other keeps track of the - * minimum top position that occurs at or after that index. Given the following children: - * - * +---------------------------------+ 0 (Y coordinate) - * | 0 | - * | +-----------+ | 10 - * | | 1 | | - * | | | +--------------+ | 20 - * | | | | 3 | | - * | +-----------+ | | | 30 - * | | | | - * | +-----------+ | | | 40 - * | | 2 | | | | - * | | | +--------------+ | 50 - * | | | | - * | +-----------+ | 60 - * | | - * +---------------------------------+ 70 - * - * +-----------+ 80 - * | 4 | - * | | +--------------+ 90 - * | | | 6 | - * +-----------+ | | 100 - * | | - * +-----------+ | | 110 - * | 5 | | | - * | | +--------------+ 120 - * | | - * +-----------+ 130 - * - * The two arrays are: - * 0 1 2 3 4 5 6 - * Max Bottom: [70, 70, 70, 70, 100, 130, 130] - * Min Top: [ 0, 0, 0, 0, 80, 90, 90] - * - * We can then binary search for the first max bottom that is above our rect, and the first min top - * that is below our rect. - * - * If the top and bottom of the rect are 55 and 85, respectively, we will start drawing at index 0 - * and stop at index 4. - * - * +---------------------------------+ 0 (Y coordinate) - * | 0 | - * | +-----------+ | 10 - * | | 1 | | - * | | | +--------------+ | 20 - * | | | | 3 | | - * | +-----------+ | | | 30 - * | | | | - * | +-----------+ | | | 40 - * | | 2 | | | | - * | | | +--------------+ | 50 - * - -| -| - - - -| - - - - - - |- - - - * | +-----------+ | 60 - * | | - * +---------------------------------+ 70 - * - * +-----------+ 80 - * - - -| 4 - - - | - - - - - - - - - - * | | +--------------+ 90 - * | | | 6 | - * +-----------+ | | 100 - * | | - * +-----------+ | | 110 - * | 5 | | | - * | | +--------------+ 120 - * | | - * +-----------+ 130 - * - * If the top and bottom are 75 and 105 respectively, we will start drawing at index 4 and stop at - * index 6. - * - * +---------------------------------+ 0 (Y coordinate) - * | 0 | - * | +-----------+ | 10 - * | | 1 | | - * | | | +--------------+ | 20 - * | | | | 3 | | - * | +-----------+ | | | 30 - * | | | | - * | +-----------+ | | | 40 - * | | 2 | | | | - * | | | +--------------+ | 50 - * | | | | - * | +-----------+ | 60 - * | | - * +---------------------------------+ 70 - * - - - - - - - - - - - - - - - - - * +-----------+ 80 - * | 4 | - * | | +--------------+ 90 - * | | | 6 | - * +-----------+ | | 100 - * - - - - - - - |- - - - - | - - - - * +-----------+ | | 110 - * | 5 | | | - * | | +--------------+ 120 - * | | - * +-----------+ 130 - * - * While this doesn't map exactly to all of the commands that could be clipped, it means that - * children which contain other children (a pretty common case when flattening views) are clipped - * or unclipped as one logical unit. This has the side effect of minimizing the amount of - * invalidates coming from minor clipping rect adjustments. The underlying dynamic programming - * arrays can be calculated off the UI thread in O(n) time, requiring just two passes through the - * command array. - * - * We do a similar optimization when searching for matching node regions, as node regions are - * loosely sorted as well when clipping. - */ -/* package */ abstract class ClippingDrawCommandManager extends DrawCommandManager { - - private static final String TAG = ClippingDrawCommandManager.class.getSimpleName(); - - private final FlatViewGroup mFlatViewGroup; - private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY; - protected float[] mCommandMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY; - protected float[] mCommandMinTop = StateBuilder.EMPTY_FLOAT_ARRAY; - - private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY; - protected float[] mRegionMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY; - protected float[] mRegionMinTop = StateBuilder.EMPTY_FLOAT_ARRAY; - - // Onscreen bounds of draw command array. - private int mStart; - private int mStop; - - // Mapping of ids to index position within the draw command array. O(log n) lookups should be - // less in our case because of the large constant overhead and auto boxing of the map. - private SparseIntArray mDrawViewIndexMap = StateBuilder.EMPTY_SPARSE_INT; - // Map of views that are currently clipped. - private final SparseArray mClippedSubviews = new SparseArray<>(); - - protected final Rect mClippingRect = new Rect(); - - // Used in updating the clipping rect, as sometimes we want to detach all views, which means we - // need to temporarily store the views we are detaching and removing. These are always of size - // 0, except when used in update clipping rect. - private final SparseArray mViewsToRemove = new SparseArray<>(); - private final ArrayList mViewsToKeep = new ArrayList<>(); - - // Currently clipping ViewGroups - private final ArrayList mClippingViewGroups = new ArrayList<>(); - - ClippingDrawCommandManager(FlatViewGroup flatViewGroup, DrawCommand[] drawCommands) { - mFlatViewGroup = flatViewGroup; - initialSetup(drawCommands); - } - - /** - * Initially setup this instance. Makes sure the draw commands are mounted, and that our - * clipping rect reflects our current bounds. - * - * @param drawCommands The list of current draw commands. In current implementations, this will - * always be DrawCommand.EMPTY_ARRAY - */ - private void initialSetup(DrawCommand[] drawCommands) { - mountDrawCommands( - drawCommands, - mDrawViewIndexMap, - mCommandMaxBottom, - mCommandMinTop, - true); - updateClippingRect(); - } - - /** - * @return index of the first command that could be on the screen. - */ - abstract int commandStartIndex(); - - /** - * @return index of the first command that is guaranteed to be off the screen, starting from the - * given start. - */ - abstract int commandStopIndex(int start); - - /** - * @return index of the first region that is guaranteed to be outside of the bounds for touch. - */ - abstract int regionStopIndex(float touchX, float touchY); - - /** - * Whether an index and all indices before it are guaranteed to be out of bounds for the current - * touch. - * - * @param index The region index to check. - * @param touchX X coordinate. - * @param touchY Y coordinate. - * @return true if the index and all before it are out of bounds. - */ - abstract boolean regionAboveTouch(int index, float touchX, float touchY); - - @Override - public void mountDrawCommands( - DrawCommand[] drawCommands, - SparseIntArray drawViewIndexMap, - float[] maxBottom, - float[] minTop, - boolean willMountViews) { - mDrawCommands = drawCommands; - mCommandMaxBottom = maxBottom; - mCommandMinTop = minTop; - mDrawViewIndexMap = drawViewIndexMap; - if (mClippingRect.bottom != mClippingRect.top) { - mStart = commandStartIndex(); - mStop = commandStopIndex(mStart); - if (!willMountViews) { - // If we are not mounting views, we still need to update view indices and positions. It is - // possible that a child changed size and we still need new clipping even though we are not - // mounting views. - updateClippingToCurrentRect(); - } - } - } - - @Override - public void mountNodeRegions(NodeRegion[] nodeRegions, float[] maxBottom, float[] minTop) { - mNodeRegions = nodeRegions; - mRegionMaxBottom = maxBottom; - mRegionMinTop = minTop; - } - - @Override - public @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY) { - int i = regionStopIndex(touchX, touchY); - while (i-- > 0) { - NodeRegion nodeRegion = mNodeRegions[i]; - if (!nodeRegion.mIsVirtual) { - // only interested in virtual nodes - continue; - } - if (regionAboveTouch(i, touchX, touchY)) { - break; - } - if (nodeRegion.withinBounds(touchX, touchY)) { - return nodeRegion; - } - } - - return null; - } - - @Override - public @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY) { - int i = regionStopIndex(touchX, touchY); - while (i-- > 0) { - NodeRegion nodeRegion = mNodeRegions[i]; - if (regionAboveTouch(i, touchX, touchY)) { - break; - } - if (nodeRegion.withinBounds(touchX, touchY)) { - return nodeRegion; - } - } - - return null; - } - - private void clip(int id, View view) { - mClippedSubviews.put(id, view); - } - - private void unclip(int id) { - mClippedSubviews.remove(id); - } - - private boolean isClipped(int id) { - return mClippedSubviews.get(id) != null; - } - - private boolean isNotClipped(int id) { - return mClippedSubviews.get(id) == null; - } - - @Override - void onClippedViewDropped(View view) { - unclip(view.getId()); - mFlatViewGroup.removeDetachedView(view); - } - - @Override - public void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) { - mClippingViewGroups.clear(); - for (int viewToAdd : viewsToAdd) { - // Views that are just temporarily detached are marked with a negative value. - boolean newView = viewToAdd > 0; - if (!newView) { - viewToAdd = -viewToAdd; - } - - int commandArrayIndex = mDrawViewIndexMap.get(viewToAdd); - DrawView drawView = (DrawView) mDrawCommands[commandArrayIndex]; - View view = viewResolver.getView(drawView.reactTag); - ensureViewHasNoParent(view); - - // these views need to support recursive clipping of subviews - if (view instanceof ReactClippingViewGroup && - ((ReactClippingViewGroup) view).getRemoveClippedSubviews()) { - mClippingViewGroups.add((ReactClippingViewGroup) view); - } - - if (newView) { - // This view was not previously attached to this parent. - drawView.mWasMounted = true; - if (animating(view) || withinBounds(commandArrayIndex)) { - // View should be drawn. This view can't currently be clipped because it wasn't - // previously attached to this parent. - mFlatViewGroup.addViewInLayout(view); - } else { - clip(drawView.reactTag, view); - } - } else { - // This view was previously attached, and just temporarily detached. - if (drawView.mWasMounted) { - // The DrawView has been mounted before. - if (isNotClipped(drawView.reactTag)) { - // The DrawView is not clipped. Attach it. - mFlatViewGroup.attachViewToParent(view); - } - // else The DrawView has been previously mounted and is clipped, so don't attach it. - } else { - // We are mounting it, so lets get this part out of the way. - drawView.mWasMounted = true; - // The DrawView has not been mounted before, which means the bounds changed and triggered - // a new DrawView when it was collected from the shadow node. We have a view with the - // same id temporarily detached, but its bounds have changed. - if (animating(view) || withinBounds(commandArrayIndex)) { - // View should be drawn. - if (isClipped(drawView.reactTag)) { - // View was clipped, so add it. - mFlatViewGroup.addViewInLayout(view); - unclip(drawView.reactTag); - } else { - // View was just temporarily removed, so attach it. We already know it isn't clipped, - // so no need to unclip it. - mFlatViewGroup.attachViewToParent(view); - } - } else { - // View should be clipped. - if (isNotClipped(drawView.reactTag)) { - // View was onscreen. - mFlatViewGroup.removeDetachedView(view); - clip(drawView.reactTag, view); - } - // else view is already clipped and not within bounds. - } - } - } - } - - for (int viewToDetach : viewsToDetach) { - View view = viewResolver.getView(viewToDetach); - if (view.getParent() != null) { - throw new RuntimeException("Trying to remove view not owned by FlatViewGroup"); - } else { - mFlatViewGroup.removeDetachedView(view); - } - // The view isn't clipped anymore, but gone entirely. - unclip(viewToDetach); - } - } - - /** - * Returns true if a view is currently animating. - */ - private static boolean animating(View view) { - Animation animation = view.getAnimation(); - return animation != null && !animation.hasEnded(); - } - - /** - * Returns true if a command index is currently onscreen. - */ - private boolean withinBounds(int i) { - return mStart <= i && i < mStop; - } - - @Override - public boolean updateClippingRect() { - ReactClippingViewGroupHelper.calculateClippingRect(mFlatViewGroup, mClippingRect); - if (mFlatViewGroup.getParent() == null || mClippingRect.top == mClippingRect.bottom) { - // If we are unparented or are clipping to an empty rect, no op. Return false so we don't - // invalidate. - return false; - } - - int start = commandStartIndex(); - int stop = commandStopIndex(start); - if (mStart <= start && stop <= mStop) { - // We would only be removing children, don't invalidate and don't bother changing the - // attached children. - updateClippingRecursively(); - return false; - } - - mStart = start; - mStop = stop; - - updateClippingToCurrentRect(); - updateClippingRecursively(); - return true; - } - - private void updateClippingRecursively() { - for (int i = 0, children = mClippingViewGroups.size(); i < children; i++) { - ReactClippingViewGroup view = mClippingViewGroups.get(i); - if (isNotClipped(((View) view).getId())) { - view.updateClippingRect(); - } - } - } - - /** - * Used either after we have updated the current rect, or when we have mounted new commands and - * the rect hasn't changed. Updates the clipping after mStart and mStop have been set to the - * correct values. For draw commands, this is all it takes to update the command mounting, as - * draw commands are only attached in a conceptual sense, and don't rely on the android view - * hierarchy. - * - * For native children, we have to walk through our current views and remove any that are no - * longer on screen, and add those that are newly on screen. As an optimization for fling, if we - * are removing two or more native views we instead detachAllViews from the {@link FlatViewGroup} - * and re-attach or add as needed. - * - * This approximation is roughly correct, as we tend to add and remove the same amount of views, - * and each add and remove pair is O(n); detachAllViews and re-attach requires two passes, so - * using this once we are removing more than two native views is a good breakpoint. - */ - private void updateClippingToCurrentRect() { - for (int i = 0, size = mFlatViewGroup.getChildCount(); i < size; i++) { - View view = mFlatViewGroup.getChildAt(i); - int index = mDrawViewIndexMap.get(view.getId()); - if (withinBounds(index) || animating(view)) { - mViewsToKeep.add(view); - } else { - mViewsToRemove.append(i, view); - clip(view.getId(), view); - } - } - - int removeSize = mViewsToRemove.size(); - boolean removeAll = removeSize > 2; - - if (removeAll) { - // Detach all, as we are changing quite a few views, whether flinging or otherwise. - mFlatViewGroup.detachAllViewsFromParent(); - - for (int i = 0; i < removeSize; i++) { - mFlatViewGroup.removeDetachedView(mViewsToRemove.valueAt(i)); - } - } else { - // Simple clipping sweep, as we are changing relatively few views. - while (removeSize-- > 0) { - mFlatViewGroup.removeViewsInLayout(mViewsToRemove.keyAt(removeSize), 1); - } - } - mViewsToRemove.clear(); - - int current = mStart; - int childIndex = 0; - - for (int i = 0, size = mViewsToKeep.size(); i < size; i++) { - View view = mViewsToKeep.get(i); - int commandIndex = mDrawViewIndexMap.get(view.getId()); - if (current <= commandIndex) { - while (current != commandIndex) { - if (mDrawCommands[current] instanceof DrawView) { - DrawView drawView = (DrawView) mDrawCommands[current]; - mFlatViewGroup.addViewInLayout( - Assertions.assumeNotNull(mClippedSubviews.get(drawView.reactTag)), - childIndex++); - unclip(drawView.reactTag); - } - current++; - } - // We are currently at the command index, but we want to increment beyond it. - current++; - } - if (removeAll) { - mFlatViewGroup.attachViewToParent(view, childIndex); - } - // We want to make sure we increment the child index even if we didn't detach it to maintain - // order. - childIndex++; - } - mViewsToKeep.clear(); - - while (current < mStop) { - if (mDrawCommands[current] instanceof DrawView) { - DrawView drawView = (DrawView) mDrawCommands[current]; - mFlatViewGroup.addViewInLayout( - Assertions.assumeNotNull(mClippedSubviews.get(drawView.reactTag)), - childIndex++); - unclip(drawView.reactTag); - } - current++; - } - } - - @Override - public void getClippingRect(Rect outClippingRect) { - outClippingRect.set(mClippingRect); - } - - @Override - public SparseArray getDetachedViews() { - return mClippedSubviews; - } - - /** - * Draws the unclipped commands on the given canvas. This would be much simpler if we didn't - * have to worry about animating views, as we could simply: - * - * for (int i = start; i < stop; i++) { - * drawCommands[i].draw(...); - * } - * - * This is complicated however by animating views, which may occur before or after the current - * clipping rect. Consider the following array: - * - * +--------------+ - * | DrawView | 0 - * | *animating* | - * +--------------+ - * | DrawCommmand | 1 - * | *clipped* | - * +--------------+ - * | DrawCommand | 2 start - * | | - * +--------------+ - * | DrawCommand | 3 - * | | - * +--------------+ - * | DrawView | 4 - * | | - * +--------------+ - * | DrawView | 5 stop - * | *clipped* | - * +--------------+ - * | DrawView | 6 - * | *animating* | - * +--------------+ - * - * 2, 3, and 4 are onscreen according to bounds, while 0 and 6 are onscreen according to - * animation. We have to walk through the attached children making sure to draw any draw - * commands that should be drawn before that draw view, as well as making sure not to draw any - * draw commands that are out of bounds. - * - * @param canvas The canvas to draw on. - */ - @Override - public void draw(Canvas canvas) { - int commandIndex = mStart; - int size = mFlatViewGroup.getChildCount(); - - // Iterate through the children, making sure that we draw any draw commands we haven't drawn - // that should happen before the next draw view. - for (int i = 0; i < size; i++) { - // This is the command index of the next view that we need to draw. Since a view might be - // animating, this view is either before all the commands onscreen, onscreen, or after the - // onscreen commands. - int viewIndex = mDrawViewIndexMap.get(mFlatViewGroup.getChildAt(i).getId()); - if (mStop < viewIndex) { - // The current view is outside of the viewport bounds. We want to draw all the commands - // up to the stop, then draw all the views outside the viewport bounds. - while (commandIndex < mStop) { - mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas); - } - // We are now out of commands to draw, so we could just draw the remaining attached - // children, but the for loop logic will draw the rest anyway. - } else if (commandIndex <= viewIndex) { - // The viewIndex is within our onscreen bounds (or == stop). We want to draw all the - // commands from the current position to the current view, inclusive. - while (commandIndex < viewIndex) { - mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas); - } - // Command index now == viewIndex, so increment beyond it. - commandIndex++; - } - mDrawCommands[viewIndex].draw(mFlatViewGroup, canvas); - } - - // If we get here, it means we have drawn all the views, now just draw the remaining draw - // commands. - while (commandIndex < mStop) { - DrawCommand command = mDrawCommands[commandIndex++]; - if (command instanceof DrawView) { - // We should never have more DrawView commands at this point. But in case we do, fail safely - // by ignoring the DrawView command - FLog.w( - TAG, - "Unexpected DrawView command at index " + (commandIndex-1) + " with mStop=" + - mStop + ". " + Arrays.toString(mDrawCommands)); - continue; - } - command.draw(mFlatViewGroup, canvas); - } - } - - @Override - void debugDraw(Canvas canvas) { - // Draws clipped draw commands, but does not draw clipped views. - for (DrawCommand drawCommand : mDrawCommands) { - if (drawCommand instanceof DrawView) { - if (isNotClipped(((DrawView) drawCommand).reactTag)) { - drawCommand.debugDraw(mFlatViewGroup, canvas); - } - // else, don't draw, and don't increment index - } else { - drawCommand.debugDraw(mFlatViewGroup, canvas); - } - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawBackgroundColor.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawBackgroundColor.java deleted file mode 100644 index ec9291180..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawBackgroundColor.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Canvas; -import android.graphics.Paint; - -/** - * Draws background for a FlatShadowNode as a solid rectangle. - */ -/* package */ final class DrawBackgroundColor extends AbstractDrawCommand { - - private static final Paint PAINT = new Paint(); - - private final int mBackgroundColor; - - /* package */ DrawBackgroundColor(int backgroundColor) { - mBackgroundColor = backgroundColor; - } - - @Override - public void onDraw(Canvas canvas) { - PAINT.setColor(mBackgroundColor); - canvas.drawRect(getLeft(), getTop(), getRight(), getBottom(), PAINT); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawBorder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawBorder.java deleted file mode 100644 index d48b3d326..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawBorder.java +++ /dev/null @@ -1,458 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.DashPathEffect; -import android.graphics.Paint; -import android.graphics.Path; - -import com.facebook.react.uimanager.Spacing; - -/* package */ final class DrawBorder extends AbstractDrawBorder { - - private static final Paint PAINT = new Paint(Paint.ANTI_ALIAS_FLAG); - private static final float[] TMP_FLOAT_ARRAY = new float[4]; - - private static final int BORDER_STYLE_SOLID = 0; - private static final int BORDER_STYLE_DOTTED = 1; - private static final int BORDER_STYLE_DASHED = 2; - - private static final int BORDER_LEFT_COLOR_SET = 1 << 1; - private static final int BORDER_TOP_COLOR_SET = 1 << 2; - private static final int BORDER_RIGHT_COLOR_SET = 1 << 3; - private static final int BORDER_BOTTOM_COLOR_SET = 1 << 4; - private static final int BORDER_PATH_EFFECT_DIRTY = 1 << 5; - - // ~0 == 0xFFFFFFFF, all bits set to 1. - private static final int ALL_BITS_SET = ~0; - // 0 == 0x00000000, all bits set to 0. - private static final int ALL_BITS_UNSET = 0; - - private float mBorderLeftWidth; - private float mBorderTopWidth; - private float mBorderRightWidth; - private float mBorderBottomWidth; - - private int mBorderLeftColor; - private int mBorderTopColor; - private int mBorderRightColor; - private int mBorderBottomColor; - - private int mBorderStyle = BORDER_STYLE_SOLID; - - private int mBackgroundColor; - - private @Nullable DashPathEffect mPathEffectForBorderStyle; - private @Nullable Path mPathForBorder; - - public void setBorderWidth(int position, float borderWidth) { - switch (position) { - case Spacing.LEFT: - mBorderLeftWidth = borderWidth; - break; - case Spacing.TOP: - mBorderTopWidth = borderWidth; - break; - case Spacing.RIGHT: - mBorderRightWidth = borderWidth; - break; - case Spacing.BOTTOM: - mBorderBottomWidth = borderWidth; - break; - case Spacing.ALL: - setBorderWidth(borderWidth); - break; - } - } - - public float getBorderWidth(int position) { - switch (position) { - case Spacing.LEFT: - return mBorderLeftWidth; - case Spacing.TOP: - return mBorderTopWidth; - case Spacing.RIGHT: - return mBorderRightWidth; - case Spacing.BOTTOM: - return mBorderBottomWidth; - case Spacing.ALL: - return getBorderWidth(); - } - - return 0.0f; - } - - public void setBorderStyle(@Nullable String style) { - if ("dotted".equals(style)) { - mBorderStyle = BORDER_STYLE_DOTTED; - } else if ("dashed".equals(style)) { - mBorderStyle = BORDER_STYLE_DASHED; - } else { - mBorderStyle = BORDER_STYLE_SOLID; - } - - setFlag(BORDER_PATH_EFFECT_DIRTY); - } - - public void resetBorderColor(int position) { - switch (position) { - case Spacing.LEFT: - resetFlag(BORDER_LEFT_COLOR_SET); - break; - case Spacing.TOP: - resetFlag(BORDER_TOP_COLOR_SET); - break; - case Spacing.RIGHT: - resetFlag(BORDER_RIGHT_COLOR_SET); - break; - case Spacing.BOTTOM: - resetFlag(BORDER_BOTTOM_COLOR_SET); - break; - case Spacing.ALL: - setBorderColor(Color.BLACK); - break; - } - } - - public void setBorderColor(int position, int borderColor) { - switch (position) { - case Spacing.LEFT: - mBorderLeftColor = borderColor; - setFlag(BORDER_LEFT_COLOR_SET); - break; - case Spacing.TOP: - mBorderTopColor = borderColor; - setFlag(BORDER_TOP_COLOR_SET); - break; - case Spacing.RIGHT: - mBorderRightColor = borderColor; - setFlag(BORDER_RIGHT_COLOR_SET); - break; - case Spacing.BOTTOM: - mBorderBottomColor = borderColor; - setFlag(BORDER_BOTTOM_COLOR_SET); - break; - case Spacing.ALL: - setBorderColor(borderColor); - break; - } - } - - public int getBorderColor(int position) { - int defaultColor = getBorderColor(); - switch (position) { - case Spacing.LEFT: - return resolveBorderColor(BORDER_LEFT_COLOR_SET, mBorderLeftColor, defaultColor); - case Spacing.TOP: - return resolveBorderColor(BORDER_TOP_COLOR_SET, mBorderTopColor, defaultColor); - case Spacing.RIGHT: - return resolveBorderColor(BORDER_RIGHT_COLOR_SET, mBorderRightColor, defaultColor); - case Spacing.BOTTOM: - return resolveBorderColor(BORDER_BOTTOM_COLOR_SET, mBorderBottomColor, defaultColor); - case Spacing.ALL: - return defaultColor; - } - - return defaultColor; - } - - public void setBackgroundColor(int backgroundColor) { - mBackgroundColor = backgroundColor; - } - - public int getBackgroundColor() { - return mBackgroundColor; - } - - @Override - protected void onDraw(Canvas canvas) { - if (getBorderRadius() >= 0.5f || getPathEffectForBorderStyle() != null) { - drawRoundedBorders(canvas); - } else { - drawRectangularBorders(canvas); - } - } - - @Override - protected @Nullable DashPathEffect getPathEffectForBorderStyle() { - if (isFlagSet(BORDER_PATH_EFFECT_DIRTY)) { - switch (mBorderStyle) { - case BORDER_STYLE_DOTTED: - mPathEffectForBorderStyle = createDashPathEffect(getBorderWidth()); - break; - - case BORDER_STYLE_DASHED: - mPathEffectForBorderStyle = createDashPathEffect(getBorderWidth() * 3); - break; - - default: - mPathEffectForBorderStyle = null; - break; - } - - resetFlag(BORDER_PATH_EFFECT_DIRTY); - } - - return mPathEffectForBorderStyle; - } - - private void drawRoundedBorders(Canvas canvas) { - if (mBackgroundColor != 0) { - PAINT.setColor(mBackgroundColor); - canvas.drawPath(getPathForBorderRadius(), PAINT); - } - - drawBorders(canvas); - } - - /** - * Quickly determine if all the set border colors are equal. Bitwise AND all the set colors - * together, then OR them all together. If the AND and the OR are the same, then the colors - * are compatible, so return this color. - * - * Used to avoid expensive path creation and expensive calls to canvas.drawPath - * - * @return A compatible border color, or zero if the border colors are not compatible. - */ - private static int fastBorderCompatibleColorOrZero( - float borderLeft, - float borderTop, - float borderRight, - float borderBottom, - int colorLeft, - int colorTop, - int colorRight, - int colorBottom) { - int andSmear = (borderLeft > 0 ? colorLeft : ALL_BITS_SET) & - (borderTop > 0 ? colorTop : ALL_BITS_SET) & - (borderRight > 0 ? colorRight : ALL_BITS_SET) & - (borderBottom > 0 ? colorBottom : ALL_BITS_SET); - int orSmear = (borderLeft > 0 ? colorLeft : ALL_BITS_UNSET) | - (borderTop > 0 ? colorTop : ALL_BITS_UNSET) | - (borderRight > 0 ? colorRight : ALL_BITS_UNSET) | - (borderBottom > 0 ? colorBottom : ALL_BITS_UNSET); - return andSmear == orSmear ? andSmear : 0; - } - - private void drawRectangularBorders(Canvas canvas) { - int defaultColor = getBorderColor(); - float defaultWidth = getBorderWidth(); - - float top = getTop(); - float borderTop = resolveWidth(mBorderTopWidth, defaultWidth); - float topInset = top + borderTop; - int topColor = resolveBorderColor(BORDER_TOP_COLOR_SET, mBorderTopColor, defaultColor); - - float bottom = getBottom(); - float borderBottom = resolveWidth(mBorderBottomWidth, defaultWidth); - float bottomInset = bottom - borderBottom; - int bottomColor = resolveBorderColor(BORDER_BOTTOM_COLOR_SET, mBorderBottomColor, defaultColor); - - float left = getLeft(); - float borderLeft = resolveWidth(mBorderLeftWidth, defaultWidth); - float leftInset = left + borderLeft; - int leftColor = resolveBorderColor(BORDER_LEFT_COLOR_SET, mBorderLeftColor, defaultColor); - - float right = getRight(); - float borderRight = resolveWidth(mBorderRightWidth, defaultWidth); - float rightInset = right - borderRight; - int rightColor = resolveBorderColor(BORDER_RIGHT_COLOR_SET, mBorderRightColor, defaultColor); - - // Check for fast path to border drawing. - int fastBorderColor = fastBorderCompatibleColorOrZero( - borderLeft, - borderTop, - borderRight, - borderBottom, - leftColor, - topColor, - rightColor, - bottomColor); - if (fastBorderColor != 0) { - // Fast border color draw. - if (Color.alpha(fastBorderColor) != 0) { - // Border color is not transparent. - - // Draw center. - if (Color.alpha(mBackgroundColor) != 0) { - PAINT.setColor(mBackgroundColor); - if (Color.alpha(fastBorderColor) == 255) { - // The border will draw over the edges, so only draw the inset background. - canvas.drawRect(leftInset, topInset, rightInset, bottomInset, PAINT); - } else { - // The border is opaque, so we have to draw the entire background color. - canvas.drawRect(left, top, right, bottom, PAINT); - } - } - - PAINT.setColor(fastBorderColor); - if (borderLeft > 0) { - canvas.drawRect(left, top, leftInset, bottom - borderBottom, PAINT); - } - if (borderTop > 0) { - canvas.drawRect(left + borderLeft, top, right, topInset, PAINT); - } - if (borderRight > 0) { - canvas.drawRect(rightInset, top + borderTop, right, bottom, PAINT); - } - if (borderBottom > 0) { - canvas.drawRect(left, bottomInset, right - borderRight, bottom, PAINT); - } - } - } else { - if (mPathForBorder == null) { - mPathForBorder = new Path(); - } - - // Draw center. Any of the borders might be opaque or transparent, so we need to draw this. - if (Color.alpha(mBackgroundColor) != 0) { - PAINT.setColor(mBackgroundColor); - canvas.drawRect(left, top, right, bottom, PAINT); - } - - // Draw top. - if (borderTop != 0 && Color.alpha(topColor) != 0) { - PAINT.setColor(topColor); - updatePathForTopBorder( - mPathForBorder, - top, - topInset, - left, - leftInset, - right, - rightInset); - canvas.drawPath(mPathForBorder, PAINT); - } - - // Draw bottom. - if (borderBottom != 0 && Color.alpha(bottomColor) != 0) { - PAINT.setColor(bottomColor); - updatePathForBottomBorder( - mPathForBorder, - bottom, - bottomInset, - left, - leftInset, - right, - rightInset); - canvas.drawPath(mPathForBorder, PAINT); - } - - // Draw left. - if (borderLeft != 0 && Color.alpha(leftColor) != 0) { - PAINT.setColor(leftColor); - updatePathForLeftBorder( - mPathForBorder, - top, - topInset, - bottom, - bottomInset, - left, - leftInset); - canvas.drawPath(mPathForBorder, PAINT); - } - - // Draw right. - if (borderRight != 0 && Color.alpha(rightColor) != 0) { - PAINT.setColor(rightColor); - updatePathForRightBorder( - mPathForBorder, - top, - topInset, - bottom, - bottomInset, - right, - rightInset); - canvas.drawPath(mPathForBorder, PAINT); - } - } - } - - private static void updatePathForTopBorder( - Path path, - float top, - float topInset, - float left, - float leftInset, - float right, - float rightInset) { - path.reset(); - path.moveTo(left, top); - path.lineTo(leftInset, topInset); - path.lineTo(rightInset, topInset); - path.lineTo(right, top); - path.lineTo(left, top); - } - - private static void updatePathForBottomBorder( - Path path, - float bottom, - float bottomInset, - float left, - float leftInset, - float right, - float rightInset) { - path.reset(); - path.moveTo(left, bottom); - path.lineTo(right, bottom); - path.lineTo(rightInset, bottomInset); - path.lineTo(leftInset, bottomInset); - path.lineTo(left, bottom); - } - - private static void updatePathForLeftBorder( - Path path, - float top, - float topInset, - float bottom, - float bottomInset, - float left, - float leftInset) { - path.reset(); - path.moveTo(left, top); - path.lineTo(leftInset, topInset); - path.lineTo(leftInset, bottomInset); - path.lineTo(left, bottom); - path.lineTo(left, top); - } - - private static void updatePathForRightBorder( - Path path, - float top, - float topInset, - float bottom, - float bottomInset, - float right, - float rightInset) { - path.reset(); - path.moveTo(right, top); - path.lineTo(right, bottom); - path.lineTo(rightInset, bottomInset); - path.lineTo(rightInset, topInset); - path.lineTo(right, top); - } - - private int resolveBorderColor(int flag, int color, int defaultColor) { - return isFlagSet(flag) ? color : defaultColor; - } - - private static float resolveWidth(float width, float defaultWidth) { - return (width == 0 || /* check for NaN */ width != width) ? defaultWidth : width; - } - - private static DashPathEffect createDashPathEffect(float borderWidth) { - for (int i = 0; i < 4; ++i) { - TMP_FLOAT_ARRAY[i] = borderWidth; - } - return new DashPathEffect(TMP_FLOAT_ARRAY, 0); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommand.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommand.java deleted file mode 100644 index b7f2d5f41..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommand.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Canvas; - -/** - * DrawCommand is an interface that shadow nodes need to implement to do the drawing. - * Instances of DrawCommand are created in background thread and passed to UI thread. - * Once a DrawCommand is shared with UI thread, it can no longer be mutated in background thread. - */ -public abstract class DrawCommand { - // used by StateBuilder, FlatViewGroup and FlatShadowNode - /* package */ static final DrawCommand[] EMPTY_ARRAY = new DrawCommand[0]; - - /** - * Performs drawing into the given canvas. - * - * @param parent The parent to get child information from, if needed - * @param canvas The canvas to draw into - */ - abstract void draw(FlatViewGroup parent, Canvas canvas); - - /** - * Performs debug bounds drawing into the given canvas. - * - * @param parent The parent to get child information from, if needed - * @param canvas The canvas to draw into - */ - abstract void debugDraw(FlatViewGroup parent, Canvas canvas); - - abstract float getLeft(); - - abstract float getTop(); - - abstract float getRight(); - - abstract float getBottom(); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommandManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommandManager.java deleted file mode 100644 index bdab8db25..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommandManager.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Canvas; -import android.graphics.Rect; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.View; -import android.view.ViewParent; - -/** - * Underlying logic which handles draw commands, views and node regions when clipping in a - * {@link FlatViewGroup}. - */ -/* package */ abstract class DrawCommandManager { - - /** - * Mount a set of draw commands to this manager. The order the commands are given is the order in - * which they should be drawn. If any of the commands are new DrawViews, then mountViews will be - * called after by the UIManager. - * - * @param drawCommands The draw commands to mount. - * @param drawViewIndexMap Mapping of ids to index position within the draw command array. - * @param maxBottom At each index i, the maximum bottom value (or right value in the case of - * horizontal clipping) value of all draw commands at or below i. - * @param minTop At each index i, the minimum top value (or left value in the case of horizontal - * clipping) value of all draw commands at or below i. - * @param willMountViews Whether we are going to also receive a mountViews command in this state - * cycle. - */ - abstract void mountDrawCommands( - DrawCommand[] drawCommands, - SparseIntArray drawViewIndexMap, - float[] maxBottom, - float[] minTop, - boolean willMountViews); - - /** - * Add and detach a set of views. The views added here will already have a DrawView passed in - * mountDrawCommands. - * - * @param viewResolver - * @param viewsToAdd The views to add, by tag. If this is a new view, this will be reactTag, - * otherwise it will be -reactTag. This allows to optimize when we have already attached - * views. - * @param viewsToDetach The views to detach, by tag. These will all be positive. - */ - abstract void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach); - - /** - * Get the current clipping rect and adjust clipping so that when draw is dispatched we do as - * little work as possible. - * - * @return true if the FlatViewGroup should invalidate. - */ - abstract boolean updateClippingRect(); - - /** - * Sets an input rect to match the bounds of our current clipping rect. - * - * @param outClippingRect Set the out - */ - abstract void getClippingRect(Rect outClippingRect); - - /** - * Return the views that are currently detached, so they can be cleaned up when we are. - * - * @return A collection of the currently detached views. - */ - abstract SparseArray getDetachedViews(); - - /** - * Draw the relevant items. This should do as little work as possible. - * - * @param canvas The canvas to draw on. - */ - abstract void draw(Canvas canvas); - - /** - * Draws layout bounds for debug. - * - * @param canvas The canvas to draw on. - */ - abstract void debugDraw(Canvas canvas); - - /** - * Mount node regions, which are the hit boxes of the shadow node children of this FlatViewGroup, - * though some may not have a corresponding draw command. - * - * @param nodeRegions Array of node regions to mount. - * @param maxBottom At each index i, the maximum bottom value (or right value in the case of - * horizontal clipping) value of all node regions at or below i. - * @param minTop At each index i, the minimum top value (or left value in the case of horizontal - * clipping) value of all draw commands at or below i. - */ - abstract void mountNodeRegions(NodeRegion[] nodeRegions, float[] maxBottom, float[] minTop); - - /** - * Find a matching node region for a touch. - * - * @param touchX X coordinate of touch. - * @param touchY Y coordinate of touch. - * @return Matching node region, or null if none are found. - */ - abstract @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY); - - /** - * Find a matching virtual node region for a touch. - * - * @param touchX X coordinate of touch. - * @param touchY Y coordinate of touch. - * @return Matching node region, or null if none are found. - */ - abstract @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY); - - /** - * Event that is fired when a clipped view is dropped. - * - * @param view the view that is dropped - */ - abstract void onClippedViewDropped(View view); - - /** - * Throw a runtime exception if a view we are trying to attach is already parented. - * - * @param view The view to check. - */ - protected static void ensureViewHasNoParent(View view) { - ViewParent oldParent = view.getParent(); - if (oldParent != null) { - throw new RuntimeException( - "Cannot add view " + view + " to DrawCommandManager while it has a parent " + oldParent); - } - } - - /** - * Get a draw command manager that will clip vertically (The view scrolls up and down). - * - * @param flatViewGroup FlatViewGroup to use for drawing. - * @param drawCommands List of commands to mount. - * @return Vertically clipping draw command manager. - */ - static DrawCommandManager getVerticalClippingInstance( - FlatViewGroup flatViewGroup, - DrawCommand[] drawCommands) { - return new VerticalDrawCommandManager(flatViewGroup, drawCommands); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImage.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImage.java deleted file mode 100644 index 05b9503d0..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImage.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.content.Context; - -import com.facebook.drawee.drawable.ScalingUtils.ScaleType; -import com.facebook.react.bridge.ReadableArray; - -/** - * Common interface for DrawImageWithDrawee. - */ -/* package */ interface DrawImage extends AttachDetachListener { - /** - * Returns true if an image source was assigned to the DrawImage. - * A DrawImage with no source will not draw anything. - */ - boolean hasImageRequest(); - - /** - * Assigns a new image source to the DrawImage, or null to clear the image request. - */ - void setSource(Context context, @Nullable ReadableArray sources); - - /** - * Assigns a tint color to apply to the image drawn. - */ - void setTintColor(int tintColor); - - /** - * Assigns a scale type to draw to the image with. - */ - void setScaleType(ScaleType scaleType); - - /** - * Returns a scale type to draw to the image with. - */ - ScaleType getScaleType(); - - /** - * React tag used for dispatching ImageLoadEvents, or 0 to ignore events. - */ - void setReactTag(int reactTag); - - void setBorderWidth(float borderWidth); - - float getBorderWidth(); - - void setBorderRadius(float borderRadius); - - float getBorderRadius(); - - void setBorderColor(int borderColor); - - int getBorderColor(); - - void setFadeDuration(int fadeDuration); - - void setProgressiveRenderingEnabled(boolean enabled); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImageWithDrawee.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImageWithDrawee.java deleted file mode 100644 index 8797cce68..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImageWithDrawee.java +++ /dev/null @@ -1,315 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.drawable.Animatable; -import android.net.Uri; -import com.facebook.drawee.controller.ControllerListener; -import com.facebook.drawee.drawable.ScalingUtils.ScaleType; -import com.facebook.drawee.generic.GenericDraweeHierarchy; -import com.facebook.drawee.generic.RoundingParams; -import com.facebook.imagepipeline.common.ResizeOptions; -import com.facebook.imagepipeline.request.ImageRequest; -import com.facebook.imagepipeline.request.ImageRequestBuilder; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.views.image.GlobalImageLoadListener; -import com.facebook.react.views.image.ImageLoadEvent; -import com.facebook.react.views.image.ImageResizeMode; -import com.facebook.react.views.image.ReactImageView; -import com.facebook.react.views.imagehelper.ImageSource; -import com.facebook.react.views.imagehelper.MultiSourceHelper; -import com.facebook.react.views.imagehelper.MultiSourceHelper.MultiSourceResult; -import java.util.LinkedList; -import java.util.List; -import javax.annotation.Nullable; - -/** - * DrawImageWithDrawee is a DrawCommand that can draw a local or remote image. - * It uses DraweeRequestHelper internally to fetch and cache the images. - */ -/* package */ final class DrawImageWithDrawee extends AbstractDrawCommand - implements DrawImage, ControllerListener { - private static final String LOCAL_FILE_SCHEME = "file"; - private static final String LOCAL_CONTENT_SCHEME = "content"; - - private final List mSources = new LinkedList<>(); - private final @Nullable GlobalImageLoadListener mGlobalImageLoadListener; - private @Nullable DraweeRequestHelper mRequestHelper; - private @Nullable PorterDuffColorFilter mColorFilter; - private ScaleType mScaleType = ImageResizeMode.defaultValue(); - private float mBorderWidth; - private float mBorderRadius; - private int mBorderColor; - private int mReactTag; - private boolean mProgressiveRenderingEnabled; - private int mFadeDuration = ReactImageView.REMOTE_IMAGE_FADE_DURATION_MS; - private @Nullable FlatViewGroup.InvalidateCallback mCallback; - - public DrawImageWithDrawee(@Nullable GlobalImageLoadListener globalImageLoadListener) { - mGlobalImageLoadListener = globalImageLoadListener; - } - - @Override - public boolean hasImageRequest() { - return !mSources.isEmpty(); - } - - @Override - public void setSource(Context context, @Nullable ReadableArray sources) { - mSources.clear(); - if (sources != null && sources.size() != 0) { - // Optimize for the case where we have just one uri, case in which we don't need the sizes - if (sources.size() == 1) { - ReadableMap source = sources.getMap(0); - mSources.add(new ImageSource(context, source.getString("uri"))); - } else { - for (int idx = 0; idx < sources.size(); idx++) { - ReadableMap source = sources.getMap(idx); - mSources.add(new ImageSource( - context, - source.getString("uri"), - source.getDouble("width"), - source.getDouble("height"))); - } - } - } - } - - @Override - public void setTintColor(int tintColor) { - if (tintColor == 0) { - mColorFilter = null; - } else { - mColorFilter = new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); - } - } - - @Override - public void setScaleType(ScaleType scaleType) { - mScaleType = scaleType; - } - - @Override - public ScaleType getScaleType() { - return mScaleType; - } - - @Override - public void setBorderWidth(float borderWidth) { - mBorderWidth = borderWidth; - } - - @Override - public float getBorderWidth() { - return mBorderWidth; - } - - @Override - public void setBorderRadius(float borderRadius) { - mBorderRadius = borderRadius; - } - - @Override - public float getBorderRadius() { - return mBorderRadius; - } - - @Override - public void setBorderColor(int borderColor) { - mBorderColor = borderColor; - } - - @Override - public int getBorderColor() { - return mBorderColor; - } - - @Override - public void setFadeDuration(int fadeDuration) { - mFadeDuration = fadeDuration; - } - - @Override - public void setProgressiveRenderingEnabled(boolean enabled) { - mProgressiveRenderingEnabled = enabled; - } - - @Override - public void setReactTag(int reactTag) { - mReactTag = reactTag; - } - - @Override - public void onDraw(Canvas canvas) { - if (mRequestHelper != null) { - mRequestHelper.getDrawable().draw(canvas); - } - } - - @Override - public void onAttached(FlatViewGroup.InvalidateCallback callback) { - mCallback = callback; - - if (mRequestHelper == null) { - // this is here to help us debug t12048319, in which we have a null request helper on attach - throw new RuntimeException( - "No DraweeRequestHelper - width: " + - (getRight() - getLeft()) + - " - height: " + (getBottom() - getTop() + - " - number of sources: " + mSources.size())); - } - - GenericDraweeHierarchy hierarchy = mRequestHelper.getHierarchy(); - - RoundingParams roundingParams = hierarchy.getRoundingParams(); - if (shouldDisplayBorder()) { - if (roundingParams == null) { - roundingParams = new RoundingParams(); - } - - roundingParams.setBorder(mBorderColor, mBorderWidth); - roundingParams.setCornersRadius(mBorderRadius); - - // changes won't take effect until we re-apply rounding params, so do it now. - hierarchy.setRoundingParams(roundingParams); - } else if (roundingParams != null) { - // clear rounding params - hierarchy.setRoundingParams(null); - } - - hierarchy.setActualImageScaleType(mScaleType); - hierarchy.setActualImageColorFilter(mColorFilter); - hierarchy.setFadeDuration(mFadeDuration); - - hierarchy.getTopLevelDrawable().setBounds( - Math.round(getLeft()), - Math.round(getTop()), - Math.round(getRight()), - Math.round(getBottom())); - - mRequestHelper.attach(callback); - } - - @Override - public void onDetached() { - if (mRequestHelper != null) { - mRequestHelper.detach(); - } - } - - @Override - public void onSubmit(String id, Object callerContext) { - if (mCallback != null && mReactTag != 0) { - mCallback.dispatchImageLoadEvent(mReactTag, ImageLoadEvent.ON_LOAD_START); - } - } - - @Override - public void onFinalImageSet( - String id, - @Nullable Object imageInfo, - @Nullable Animatable animatable) { - if (mCallback != null && mReactTag != 0) { - mCallback.dispatchImageLoadEvent(mReactTag, ImageLoadEvent.ON_LOAD); - mCallback.dispatchImageLoadEvent(mReactTag, ImageLoadEvent.ON_LOAD_END); - } - } - - @Override - public void onIntermediateImageSet(String id, @Nullable Object imageInfo) { - } - - @Override - public void onIntermediateImageFailed(String id, Throwable throwable) { - } - - @Override - public void onFailure(String id, Throwable throwable) { - if (mCallback != null && mReactTag != 0) { - mCallback.dispatchImageLoadEvent(mReactTag, ImageLoadEvent.ON_ERROR); - mCallback.dispatchImageLoadEvent(mReactTag, ImageLoadEvent.ON_LOAD_END); - } - } - - @Override - public void onRelease(String id) { - } - - @Override - protected void onBoundsChanged() { - super.onBoundsChanged(); - computeRequestHelper(); - } - - private void computeRequestHelper() { - MultiSourceResult multiSource = MultiSourceHelper.getBestSourceForSize( - Math.round(getRight() - getLeft()), - Math.round(getBottom() - getTop()), - mSources); - ImageSource source = multiSource.getBestResult(); - ImageSource cachedSource = multiSource.getBestResultInCache(); - if (source == null) { - mRequestHelper = null; - return; - } - - ResizeOptions resizeOptions = null; - if (shouldResize(source)) { - final int width = (int) (getRight() - getLeft()); - final int height = (int) (getBottom() - getTop()); - resizeOptions = new ResizeOptions(width, height); - } - - ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(source.getUri()) - .setResizeOptions(resizeOptions) - .setProgressiveRenderingEnabled(mProgressiveRenderingEnabled) - .build(); - if (mGlobalImageLoadListener != null) { - mGlobalImageLoadListener.onLoadAttempt(source.getUri()); - } - - ImageRequest cachedImageRequest = null; - if (cachedSource != null) { - cachedImageRequest = ImageRequestBuilder.newBuilderWithSource(cachedSource.getUri()) - .setResizeOptions(resizeOptions) - .setProgressiveRenderingEnabled(mProgressiveRenderingEnabled) - .build(); - } - mRequestHelper = new - DraweeRequestHelper(Assertions.assertNotNull(imageRequest), cachedImageRequest, this); - } - - private boolean shouldDisplayBorder() { - return mBorderColor != 0 || mBorderRadius >= 0.5f; - } - - private static boolean shouldResize(ImageSource imageSource) { - // Resizing is inferior to scaling. See http://frescolib.org/docs/resizing-rotating.html - // We resize here only for images likely to be from the device's camera, where the app developer - // has no control over the original size - Uri uri = imageSource.getUri(); - String type = uri == null ? null : uri.getScheme(); - // one day, we can replace this with what non-Nodes does, which is: - // UriUtil.isLocalContentUri || UriUtil.isLocalFileUri - // not doing this just to save including eyt another BUCK dependency - return LOCAL_FILE_SCHEME.equals(type) || LOCAL_CONTENT_SCHEME.equals(type); - } - - @Override - protected void onDebugDrawHighlight(Canvas canvas) { - if (mCallback != null) { - debugDrawCautionHighlight(canvas, "Invalidate Drawee"); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawTextLayout.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawTextLayout.java deleted file mode 100644 index 495a53e0d..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawTextLayout.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Canvas; -import android.text.Layout; - -import com.facebook.fbui.textlayoutbuilder.util.LayoutMeasureUtil; - -/** - * DrawTextLayout is a DrawCommand that draw {@link Layout}. - */ -/* package */ final class DrawTextLayout extends AbstractDrawCommand { - - private Layout mLayout; - private float mLayoutWidth; - private float mLayoutHeight; - - /* package */ DrawTextLayout(Layout layout) { - setLayout(layout); - } - - /** - * Assigns a new {@link Layout} to draw. - */ - public void setLayout(Layout layout) { - mLayout = layout; - mLayoutWidth = layout.getWidth(); - mLayoutHeight = LayoutMeasureUtil.getHeight(layout); - } - - public Layout getLayout() { - return mLayout; - } - - public float getLayoutWidth() { - // mLayout.getWidth() doesn't return correct width of the text Layout - return mLayoutWidth; - } - - public float getLayoutHeight() { - return mLayoutHeight; - } - - @Override - protected void onDraw(Canvas canvas) { - float left = getLeft(); - float top = getTop(); - - canvas.translate(left, top); - mLayout.draw(canvas); - canvas.translate(-left, -top); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawView.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawView.java deleted file mode 100644 index e2bdce5fb..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawView.java +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Canvas; -import android.graphics.Path; -import android.graphics.RectF; - -/* package */ final class DrawView extends AbstractDrawCommand { - public static final DrawView[] EMPTY_ARRAY = new DrawView[0]; - // the minimum rounded clipping value before we actually do rounded clipping - /* package */ static final float MINIMUM_ROUNDED_CLIPPING_VALUE = 0.5f; - private final RectF TMP_RECT = new RectF(); - - /* package */ final int reactTag; - // Indicates whether this DrawView has been previously mounted to a clipping FlatViewGroup. This - // lets us know that the bounds haven't changed, as a bounds change would trigger a new DrawView, - // which will set this to false for the new DrawView. This is safe, despite the dual access with - // FlatViewGroup, because the FlatViewGroup copy is only ever modified by the FlatViewGroup. - // Changing how this boolean is used should be handled with caution, as race conditions are the - // quickest way to create unreproducible super bugs. - /* package */ boolean mWasMounted; - - // the clipping radius - if this is greater than MINIMUM_ROUNDED_CLIPPING_VALUE, we clip using - // a rounded path, otherwise we clip in a rectangular fashion. - private float mClipRadius; - - // the path to clip against if we're doing path clipping for rounded borders. - @Nullable private Path mPath; - - // These should only ever be set from within the DrawView, they serve to provide clipping bounds - // for FlatViewGroups, which have strange clipping when it comes to overflow: visible. They are - // left package protected to speed up direct access. For overflow visible, these are the adjusted - // bounds while taking overflowing elements into account, other wise they are just the regular - // bounds of the view. - /* package */ float mLogicalLeft; - /* package */ float mLogicalTop; - /* package */ float mLogicalRight; - /* package */ float mLogicalBottom; - - public DrawView(int reactTag) { - this.reactTag = reactTag; - } - - /** - * Similar to updateBoundsAndFreeze, but thread safe as the mounting flag is modified on the UI - * thread. - * - * @return A DrawView with the passed bounds and clipping bounds. If we can use the same - * DrawView, it will just be this, otherwise it will be a frozen copy. - */ - public DrawView collectDrawView( - float left, - float top, - float right, - float bottom, - float logicalLeft, - float logicalTop, - float logicalRight, - float logicalBottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom, - float clipRadius) { - if (!isFrozen()) { - // We haven't collected this draw view yet, so we can just set everything. - setBounds(left, top, right, bottom); - setClipBounds(clipLeft, clipTop, clipRight, clipBottom); - setClipRadius(clipRadius); - setLogicalBounds(logicalLeft, logicalTop, logicalRight, logicalBottom); - freeze(); - return this; - } - - boolean boundsMatch = boundsMatch(left, top, right, bottom); - boolean clipBoundsMatch = clipBoundsMatch(clipLeft, clipTop, clipRight, clipBottom); - boolean clipRadiusMatch = mClipRadius == clipRadius; - boolean logicalBoundsMatch = - logicalBoundsMatch(logicalLeft, logicalTop, logicalRight, logicalBottom); - - // See if we can reuse the draw view. - if (boundsMatch && clipBoundsMatch && clipRadiusMatch && logicalBoundsMatch) { - return this; - } - - DrawView drawView = (DrawView) mutableCopy(); - - if (!boundsMatch) { - drawView.setBounds(left, top, right, bottom); - } - - if (!clipBoundsMatch) { - drawView.setClipBounds(clipLeft, clipTop, clipRight, clipBottom); - } - - if (!logicalBoundsMatch) { - drawView.setLogicalBounds(logicalLeft, logicalTop, logicalRight, logicalBottom); - } - - if (!clipRadiusMatch || !boundsMatch) { - // If the bounds change, we need to update the clip path. - drawView.setClipRadius(clipRadius); - } - - // It is very important that we unset this, as our spec is that newly created DrawViews - // are handled differently by the FlatViewGroup. This is needed because clone() maintains - // the previous state. - drawView.mWasMounted = false; - - drawView.freeze(); - - return drawView; - } - - private boolean logicalBoundsMatch(float left, float top, float right, float bottom) { - return left == mLogicalLeft && top == mLogicalTop && - right == mLogicalRight && bottom == mLogicalBottom; - } - - private void setLogicalBounds(float left, float top, float right, float bottom) { - // Do rounding up front and off of the UI thread. - mLogicalLeft = left; - mLogicalTop = top; - mLogicalRight = right; - mLogicalBottom = bottom; - } - - @Override - public void draw(FlatViewGroup parent, Canvas canvas) { - onPreDraw(parent, canvas); - if (mNeedsClipping || mClipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) { - canvas.save(Canvas.CLIP_SAVE_FLAG); - applyClipping(canvas); - parent.drawNextChild(canvas); - canvas.restore(); - } else { - parent.drawNextChild(canvas); - } - } - - /** - * Set the clip radius. Should only be called when the clip radius is first set or when it - * changes, in order to avoid extra work. - * - * @param clipRadius The new clip radius. - */ - void setClipRadius(float clipRadius) { - mClipRadius = clipRadius; - if (clipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) { - // update the path that we'll clip based on - updateClipPath(); - } else { - mPath = null; - } - } - - /** - * Update the path with which we'll clip this view - */ - private void updateClipPath() { - mPath = new Path(); - - TMP_RECT.set( - getLeft(), - getTop(), - getRight(), - getBottom()); - - // set the path - mPath.addRoundRect( - TMP_RECT, - mClipRadius, - mClipRadius, - Path.Direction.CW); - } - - @Override - protected void applyClipping(Canvas canvas) { - // only clip using a path if our radius is greater than some minimum threshold, because - // clipPath is more expensive than clipRect. - if (mClipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) { - canvas.clipPath(mPath); - } else { - super.applyClipping(canvas); - } - } - - @Override - protected void onDraw(Canvas canvas) { - // no op as we override draw. - } - - @Override - protected void onDebugDraw(FlatViewGroup parent, Canvas canvas) { - parent.debugDrawNextChild(canvas); - } - - @Override - protected void onDebugDrawHighlight(Canvas canvas) { - if (mPath != null) { - debugDrawWarningHighlight(canvas, "borderRadius: " + mClipRadius); - } else if (!boundsMatch(mLogicalLeft, mLogicalTop, mLogicalRight, mLogicalBottom)) { - StringBuilder warn = new StringBuilder("Overflow: { "); - String[] names = { "left: ", "top: ", "right: ", "bottom: "}; - int i = 0; - float[] offsets = new float[4]; - offsets[i++] = getLeft() - mLogicalLeft; - offsets[i++] = getTop() - mLogicalTop; - offsets[i++] = mLogicalRight - getRight(); - offsets[i++] = mLogicalBottom - getBottom(); - - for (i = 0; i < 4; i++) { - if (offsets[i] != 0f) { - warn.append(names[i]); - warn.append(offsets[i]); - warn.append(", "); - } - } - - warn.append("}"); - - debugDrawCautionHighlight(canvas, warn.toString()); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DraweeRequestHelper.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DraweeRequestHelper.java deleted file mode 100644 index bf32f7403..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DraweeRequestHelper.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.content.res.Resources; -import android.graphics.drawable.Drawable; - -import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; -import com.facebook.drawee.controller.ControllerListener; -import com.facebook.drawee.generic.GenericDraweeHierarchy; -import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; -import com.facebook.drawee.interfaces.DraweeController; -import com.facebook.imagepipeline.request.ImageRequest; -import com.facebook.infer.annotation.Assertions; - -/* package */ final class DraweeRequestHelper { - - private static GenericDraweeHierarchyBuilder sHierarchyBuilder; - private static AbstractDraweeControllerBuilder sControllerBuilder; - - /* package */ static void setResources(Resources resources) { - sHierarchyBuilder = new GenericDraweeHierarchyBuilder(resources); - } - - /* package */ static void setDraweeControllerBuilder(AbstractDraweeControllerBuilder builder) { - sControllerBuilder = builder; - } - - private final DraweeController mDraweeController; - private int mAttachCounter; - - /* package */ DraweeRequestHelper( - ImageRequest imageRequest, - @Nullable ImageRequest cachedImageRequest, - ControllerListener listener) { - AbstractDraweeControllerBuilder controllerBuilder = sControllerBuilder - .setImageRequest(imageRequest) - .setCallerContext(RCTImageView.getCallerContext()) - .setControllerListener(listener); - - if (cachedImageRequest != null) { - controllerBuilder.setLowResImageRequest(cachedImageRequest); - } - DraweeController controller = controllerBuilder.build(); - - controller.setHierarchy(sHierarchyBuilder.build()); - - mDraweeController = controller; - } - - /* package */ void attach(FlatViewGroup.InvalidateCallback callback) { - ++mAttachCounter; - if (mAttachCounter == 1) { - getDrawable().setCallback(callback.get()); - mDraweeController.onAttach(); - } - } - - /* package */ void detach() { - --mAttachCounter; - if (mAttachCounter == 0) { - mDraweeController.onDetach(); - } - } - - /* package */ GenericDraweeHierarchy getHierarchy() { - return (GenericDraweeHierarchy) Assertions.assumeNotNull(mDraweeController.getHierarchy()); - } - - /* package */ Drawable getDrawable() { - return getHierarchy().getTopLevelDrawable(); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/ElementsList.java b/ReactAndroid/src/main/java/com/facebook/react/flat/ElementsList.java deleted file mode 100644 index e741cde43..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/ElementsList.java +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.lang.reflect.Array; - -/** - * Diffing scope stack class that supports 3 main operations: start(), add() an element and - * finish(). - * - * When started, it takes a baseline array to compare to. When adding a new element, it checks - * whether a corresponding element in baseline array is the same. On finish(), it will return null - * if baseline array contains exactly the same elements that were added with a sequence of add() - * calls, or a new array of the recorded elements: - * - * Example 1: - * ----- - * start([A]) - * add(A) - * finish() -> null (because [A] == [A]) - * - * Example 2: - * ---- - * start([A]) - * add(B) - * finish() -> [B] (because [A] != [B]) - * - * Example 3: - * ---- - * start([A]) - * add(B) - * add(A) - * finish() -> [B, A] (because [B, A] != [A]) - * - * Example 4: - * ---- - * start([A, B]) - * add(B) - * add(A) - * finish() -> [B, A] (because [B, A] != [A, B]) - * - * It is important that start/finish can be nested: - * ---- - * start([A]) - * add(A) - * start([B]) - * add(B) - * finish() -> null - * add(C) - * finish() -> [A, C] - * - * StateBuilder is using this class to check if e.g. a DrawCommand list for a given View needs to be - * updated. - */ -/* package */ final class ElementsList { - - private static final class Scope { - Object[] elements; - int index; - int size; - } - - // List of scopes. These are never cleared, but instead recycled when a new scope is needed at - // a given depth. - private final ArrayList mScopesStack = new ArrayList<>(); - // Working list of all new elements we are gathering across scopes. Whenever we get a call to - // finish() we pop the new elements off the collection, either discarding them if there was no - // change from the base or accumulating and returning them as a list of new elements. - private final ArrayDeque mElements = new ArrayDeque<>(); - private final E[] mEmptyArray; - private Scope mCurrentScope = null; - private int mScopeIndex = 0; - - public ElementsList(E[] emptyArray) { - mEmptyArray = emptyArray; - mScopesStack.add(mCurrentScope); - } - - /** - * Starts a new scope. - */ - public void start(Object[] elements) { - pushScope(); - - Scope scope = getCurrentScope(); - scope.elements = elements; - scope.index = 0; - scope.size = mElements.size(); - } - - /** - * Finish current scope, returning null if there were no changes recorded, or a new array - * containing all the newly recorded elements otherwise. - */ - public E[] finish() { - Scope scope = getCurrentScope(); - popScope(); - - E[] result = null; - int size = mElements.size() - scope.size; - if (scope.index != scope.elements.length) { - result = extractElements(size); - } else { - // downsize - for (int i = 0; i < size; ++i) { - mElements.pollLast(); - } - } - - // To prevent resource leaks. - scope.elements = null; - - return result; - } - - /** - * Adds a new element to the list. This method can be optimized to avoid inserts on same - * elements, but would involve copying from scope.elements when we extract elements. - */ - public void add(E element) { - Scope scope = getCurrentScope(); - - if (scope.index < scope.elements.length && - scope.elements[scope.index] == element) { - ++scope.index; - } else { - scope.index = Integer.MAX_VALUE; - } - - mElements.add(element); - } - - /** - * Resets all references to elements in our new stack to null to avoid memory leaks. - */ - public void clear() { - if (getCurrentScope() != null) { - throw new RuntimeException("Must call finish() for every start() call being made."); - } - mElements.clear(); - } - - /** - * Extracts last size elements into an array. Used to extract our new array of items from our - * stack when the new items != old items. - */ - private E[] extractElements(int size) { - if (size == 0) { - // avoid allocating empty array - return mEmptyArray; - } - - E[] elements = (E[]) Array.newInstance(mEmptyArray.getClass().getComponentType(), size); - for (int i = size - 1; i >= 0; --i) { - elements[i] = mElements.pollLast(); - } - - return elements; - } - - /** - * Saves current scope in a stack. - */ - private void pushScope() { - ++mScopeIndex; - if (mScopeIndex == mScopesStack.size()) { - // We reached a new deepest scope, we need to create a scope for this depth. - mCurrentScope = new Scope(); - mScopesStack.add(mCurrentScope); - } else { - // We have had a scope at this depth before, lets recycle it. - mCurrentScope = mScopesStack.get(mScopeIndex); - } - } - - /** - * Restores last saved current scope. Doesn't actually remove the scope, as scopes are - * recycled. - */ - private void popScope() { - --mScopeIndex; - mCurrentScope = mScopesStack.get(mScopeIndex); - } - - /** - * Returns current scope. - */ - private Scope getCurrentScope() { - return mCurrentScope; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewManager.java deleted file mode 100644 index f6382b80b..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewManager.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.react.uimanager.BaseViewManager; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.views.art.ARTSurfaceView; -import com.facebook.yoga.YogaMeasureFunction; -import com.facebook.yoga.YogaMeasureMode; -import com.facebook.yoga.YogaNode; - -public class FlatARTSurfaceViewManager extends - BaseViewManager { - - /* package */ static final String REACT_CLASS = "ARTSurfaceView"; - - private static final YogaMeasureFunction MEASURE_FUNCTION = new YogaMeasureFunction() { - @Override - public long measure( - YogaNode node, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode) { - throw new IllegalStateException("SurfaceView should have explicit width and height set"); - } - }; - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - public FlatARTSurfaceViewShadowNode createShadowNodeInstance() { - FlatARTSurfaceViewShadowNode node = new FlatARTSurfaceViewShadowNode(); - node.setMeasureFunction(MEASURE_FUNCTION); - return node; - } - - @Override - public Class getShadowNodeClass() { - return FlatARTSurfaceViewShadowNode.class; - } - - @Override - protected ARTSurfaceView createViewInstance(ThemedReactContext reactContext) { - return new ARTSurfaceView(reactContext); - } - - @Override - public void updateExtraData(ARTSurfaceView root, Object extraData) { - root.setSurfaceTextureListener((FlatARTSurfaceViewShadowNode) extraData); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewShadowNode.java deleted file mode 100644 index 09fa6a42a..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatARTSurfaceViewShadowNode.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.SurfaceTexture; -import android.util.Log; -import android.view.Surface; -import android.view.TextureView; - -import com.facebook.react.common.ReactConstants; -import com.facebook.react.uimanager.ReactShadowNode; -import com.facebook.react.uimanager.UIViewOperationQueue; -import com.facebook.react.views.art.ARTVirtualNode; -import com.facebook.yoga.YogaValue; -import com.facebook.yoga.YogaUnit; - -/* package */ class FlatARTSurfaceViewShadowNode extends FlatShadowNode - implements AndroidView, TextureView.SurfaceTextureListener { - private boolean mPaddingChanged = false; - private @Nullable Surface mSurface; - - /* package */ FlatARTSurfaceViewShadowNode() { - forceMountToView(); - forceMountChildrenToView(); - } - - @Override - public boolean isVirtual() { - return false; - } - - @Override - public boolean isVirtualAnchor() { - return true; - } - - @Override - public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) { - super.onCollectExtraUpdates(uiUpdater); - drawOutput(); - uiUpdater.enqueueUpdateExtraData(getReactTag(), this); - } - - private void drawOutput() { - if (mSurface == null || !mSurface.isValid()) { - markChildrenUpdatesSeen(this); - return; - } - - try { - Canvas canvas = mSurface.lockCanvas(null); - canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - - Paint paint = new Paint(); - for (int i = 0; i < getChildCount(); i++) { - ARTVirtualNode child = (ARTVirtualNode) getChildAt(i); - child.draw(canvas, paint, 1f); - child.markUpdateSeen(); - } - - if (mSurface == null) { - return; - } - - mSurface.unlockCanvasAndPost(canvas); - } catch (IllegalArgumentException | IllegalStateException e) { - Log.e(ReactConstants.TAG, e.getClass().getSimpleName() + " in Surface.unlockCanvasAndPost"); - } - } - - private void markChildrenUpdatesSeen(ReactShadowNode shadowNode) { - for (int i = 0; i < shadowNode.getChildCount(); i++) { - ReactShadowNode child = shadowNode.getChildAt(i); - child.markUpdateSeen(); - markChildrenUpdatesSeen(child); - } - } - - @Override - public boolean needsCustomLayoutForChildren() { - return false; - } - - @Override - public boolean isPaddingChanged() { - return mPaddingChanged; - } - - @Override - public void resetPaddingChanged() { - mPaddingChanged = false; - } - - @Override - public void setPadding(int spacingType, float padding) { - YogaValue current = getStylePadding(spacingType); - if (current.unit != YogaUnit.POINT || current.value != padding) { - super.setPadding(spacingType, padding); - mPaddingChanged = true; - markUpdated(); - } - } - - @Override - public void setPaddingPercent(int spacingType, float percent) { - YogaValue current = getStylePadding(spacingType); - if (current.unit != YogaUnit.PERCENT || current.value != percent) { - super.setPadding(spacingType, percent); - mPaddingChanged = true; - markUpdated(); - } - } - - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - mSurface = new Surface(surface); - drawOutput(); - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - surface.release(); - mSurface = null; - return true; - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) {} -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatMeasuredViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatMeasuredViewGroup.java deleted file mode 100644 index 7e7ee2e46..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatMeasuredViewGroup.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Rect; - -/** - * Helper interface to provide measuring of FlatViewGroup when needed. We don't override onMeasure - * for FlatViewGroup, which means that draw commands don't contributed to the measured width and - * height. This allows us to expose our calculated dimensions taking into account draw commands, - * without changing the visibility of the FlatViewGroup. - */ -public interface FlatMeasuredViewGroup { - /** - * @return A rect consisting of the left, top, right, and bottommost edge among all children, - * including draw commands. - */ - Rect measureWithCommands(); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatNativeViewHierarchyManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatNativeViewHierarchyManager.java deleted file mode 100644 index ac1e0aadd..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatNativeViewHierarchyManager.java +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import java.util.ArrayList; -import java.util.List; - -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.View; -import android.view.View.MeasureSpec; -import android.view.ViewGroup; - -import com.facebook.react.uimanager.NativeViewHierarchyManager; -import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.ViewManagerRegistry; - -/** - * FlatNativeViewHierarchyManager is the only class that performs View manipulations. All of this - * class methods can only be called from UI thread by {@link FlatUIViewOperationQueue}. - */ -/* package */ final class FlatNativeViewHierarchyManager extends NativeViewHierarchyManager - implements ViewResolver { - - /* package */ FlatNativeViewHierarchyManager(ViewManagerRegistry viewManagers) { - super(viewManagers, new FlatRootViewManager()); - } - - @Override - public View getView(int reactTag) { - return super.resolveView(reactTag); - } - - @Override - public void addRootView( - int tag, - SizeMonitoringFrameLayout view, - ThemedReactContext themedContext) { - FlatViewGroup root = new FlatViewGroup(themedContext); - view.addView(root); - - // When unmounting, ReactInstanceManager.detachViewFromInstance() will check id of the - // top-level View (SizeMonitoringFrameLayout) and pass it back to JS. We want that View's id to - // be set, otherwise NativeViewHierarchyManager will not be able to cleanup properly. - view.setId(tag); - - addRootViewGroup(tag, root, themedContext); - } - - /** - * Updates DrawCommands and AttachDetachListeners of a FlatViewGroup specified by a reactTag. - * - * @param reactTag reactTag to lookup FlatViewGroup by - * @param drawCommands if non-null, new draw commands to execute during the drawing. - * @param listeners if non-null, new attach-detach listeners. - */ - /* package */ void updateMountState( - int reactTag, - @Nullable DrawCommand[] drawCommands, - @Nullable AttachDetachListener[] listeners, - @Nullable NodeRegion[] nodeRegions) { - FlatViewGroup view = (FlatViewGroup) resolveView(reactTag); - if (drawCommands != null) { - view.mountDrawCommands(drawCommands); - } - if (listeners != null) { - view.mountAttachDetachListeners(listeners); - } - if (nodeRegions != null) { - view.mountNodeRegions(nodeRegions); - } - } - - /** - * Updates DrawCommands and AttachDetachListeners of a clipping FlatViewGroup specified by a - * reactTag. - * - * @param reactTag The react tag to lookup FlatViewGroup by. - * @param drawCommands If non-null, new draw commands to execute during the drawing. - * @param drawViewIndexMap Mapping of react tags to the index of the corresponding DrawView - * command in the draw command array. - * @param commandMaxBot At each index i, the maximum bottom value (or right value in the case of - * horizontal clipping) value of all draw commands at or below i. - * @param commandMinTop At each index i, the minimum top value (or left value in the case of - * horizontal clipping) value of all draw commands at or below i. - * @param listeners If non-null, new attach-detach listeners. - * @param nodeRegions Node regions to mount. - * @param regionMaxBot At each index i, the maximum bottom value (or right value in the case of - * horizontal clipping) value of all node regions at or below i. - * @param regionMinTop At each index i, the minimum top value (or left value in the case of - * horizontal clipping) value of all draw commands at or below i. - * @param willMountViews Whether we are going to also send a mountViews command in this state - * cycle. - */ - /* package */ void updateClippingMountState( - int reactTag, - @Nullable DrawCommand[] drawCommands, - SparseIntArray drawViewIndexMap, - float[] commandMaxBot, - float[] commandMinTop, - @Nullable AttachDetachListener[] listeners, - @Nullable NodeRegion[] nodeRegions, - float[] regionMaxBot, - float[] regionMinTop, - boolean willMountViews) { - FlatViewGroup view = (FlatViewGroup) resolveView(reactTag); - if (drawCommands != null) { - view.mountClippingDrawCommands( - drawCommands, - drawViewIndexMap, - commandMaxBot, - commandMinTop, - willMountViews); - } - if (listeners != null) { - view.mountAttachDetachListeners(listeners); - } - if (nodeRegions != null) { - view.mountClippingNodeRegions(nodeRegions, regionMaxBot, regionMinTop); - } - } - - /* package */ void updateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) { - View view = resolveView(reactTag); - if (view instanceof FlatViewGroup) { - ((FlatViewGroup) view).mountViews(this, viewsToAdd, viewsToDetach); - return; - } - - ViewGroup viewGroup = (ViewGroup) view; - ViewGroupManager viewManager = (ViewGroupManager) resolveViewManager(reactTag); - List listOfViews = new ArrayList<>(viewsToAdd.length); - - // batch the set of additions - some view managers can take advantage of the batching to - // decrease operations, etc. - for (int viewIdToAdd : viewsToAdd) { - int tag = Math.abs(viewIdToAdd); - listOfViews.add(resolveView(tag)); - } - viewManager.addViews(viewGroup, listOfViews); - } - - /** - * Updates View bounds, possibly re-measuring and re-layouting it if the size changed. - * - * @param reactTag reactTag to lookup a View by - * @param left left coordinate relative to parent - * @param top top coordinate relative to parent - * @param right right coordinate relative to parent - * @param bottom bottom coordinate relative to parent - */ - /* package */ void updateViewBounds(int reactTag, int left, int top, int right, int bottom) { - View view = resolveView(reactTag); - int width = right - left; - int height = bottom - top; - if (view.getWidth() != width || view.getHeight() != height) { - // size changed, we need to measure and layout the View - view.measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - view.layout(left, top, right, bottom); - } else { - // same size, only location changed, there is a faster route. - view.offsetLeftAndRight(left - view.getLeft()); - view.offsetTopAndBottom(top - view.getTop()); - } - } - - /* package */ void setPadding( - int reactTag, - int paddingLeft, - int paddingTop, - int paddingRight, - int paddingBottom) { - resolveView(reactTag).setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); - } - - /* package */ void dropViews(SparseIntArray viewsToDrop) { - for (int i = 0, count = viewsToDrop.size(); i < count; i++) { - int viewToDrop = viewsToDrop.keyAt(i); - View view = null; - if (viewToDrop > 0) { - try { - view = resolveView(viewToDrop); - dropView(view); - } catch (Exception e) { - // the view is already dropped, nothing we can do - } - } else { - // Root views are noted with a negative tag from StateBuilder. - removeRootView(-viewToDrop); - } - - int parentTag = viewsToDrop.valueAt(i); - // this only happens for clipped, non-root views - clipped because there is no parent, and - // not a root view (because we explicitly pass -1 for root views). - if (parentTag > 0 && view != null && view.getParent() == null) { - // this can only happen if the parent exists (if the parent were removed first, it'd also - // remove the child, so trying to explicitly remove the child afterwards would crash at - // the resolveView call above) - we also explicitly check for a null parent, implying that - // we are either clipped (or that we already removed the child from its parent, in which - // case this will essentially be a no-op). - View parent = resolveView(parentTag); - if (parent instanceof FlatViewGroup) { - ((FlatViewGroup) parent).onViewDropped(view); - } - } - } - } - - @Override - protected void dropView(View view) { - super.dropView(view); - - // As a result of removeClippedSubviews, some views have strong references but are not attached - // to a parent. consequently, when the parent gets removed, these Views don't get cleaned up, - // because they aren't children (they also aren't removed from mTagsToViews, thus causing a - // leak). To solve this, we ask for said detached views and explicitly drop them. - if (view instanceof FlatViewGroup) { - FlatViewGroup flatViewGroup = (FlatViewGroup) view; - if (flatViewGroup.getRemoveClippedSubviews()) { - SparseArray detachedViews = flatViewGroup.getDetachedViews(); - for (int i = 0, size = detachedViews.size(); i < size; i++) { - View detachedChild = detachedViews.valueAt(i); - try { - dropView(detachedChild); - } catch (Exception e) { - // if the view is already dropped, ignore any exceptions - // in reality, we should find out the edge cases that cause - // this to happen and properly fix them. - } - // trigger onDetachedFromWindow and clean up this detached/clipped view - flatViewGroup.removeDetachedView(detachedChild); - } - } - } - } - - /* package */ void detachAllChildrenFromViews(int[] viewsToDetachAllChildrenFrom) { - for (int viewTag : viewsToDetachAllChildrenFrom) { - View view = resolveView(viewTag); - if (view instanceof FlatViewGroup) { - ((FlatViewGroup) view).detachAllViewsFromParent(); - continue; - } - - ViewGroup viewGroup = (ViewGroup) view; - ViewGroupManager viewManager = (ViewGroupManager) resolveViewManager(viewTag); - viewManager.removeAllViews(viewGroup); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatReactModalShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatReactModalShadowNode.java deleted file mode 100644 index 01191247c..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatReactModalShadowNode.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.Point; -import android.view.Display; -import android.view.Surface; -import android.view.WindowManager; -import com.facebook.react.uimanager.ReactShadowNodeImpl; -import com.facebook.yoga.YogaUnit; -import com.facebook.yoga.YogaValue; - -/** - * FlatReactModalShadowNode - * - * This is a Nodes specific shadow node for modals. This is required because we wrap any - * non-FlatShadowNode node in a NativeViewWrapper. In the case of a modal shadow node, we need - * to treat it as its own node so that we can add the custom measurement behavior that is there - * in the non-Nodes version when we add a child. - * - * {@see {@link com.facebook.react.views.modal.ModalHostShadowNode}} - */ -class FlatReactModalShadowNode extends FlatShadowNode implements AndroidView { - - private final Point mMinPoint = new Point(); - private final Point mMaxPoint = new Point(); - private boolean mPaddingChanged; - - FlatReactModalShadowNode() { - forceMountToView(); - forceMountChildrenToView(); - } - - /** - * We need to set the styleWidth and styleHeight of the one child (represented by the - * within the in Modal.js. This needs to fill the entire window. - */ - @Override - @TargetApi(16) - public void addChildAt(ReactShadowNodeImpl child, int i) { - super.addChildAt(child, i); - - Context context = getThemedContext(); - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); - // getCurrentSizeRange will return the min and max width and height that the window can be - display.getCurrentSizeRange(mMinPoint, mMaxPoint); - - int width, height; - int rotation = display.getRotation(); - if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { - // If we are vertical the width value comes from min width and height comes from max height - width = mMinPoint.x; - height = mMaxPoint.y; - } else { - // If we are horizontal the width value comes from max width and height comes from min height - width = mMaxPoint.x; - height = mMinPoint.y; - } - child.setStyleWidth(width); - child.setStyleHeight(height); - } - - @Override - public boolean needsCustomLayoutForChildren() { - return false; - } - - @Override - public boolean isPaddingChanged() { - return mPaddingChanged; - } - - @Override - public void resetPaddingChanged() { - mPaddingChanged = false; - } - - @Override - public void setPadding(int spacingType, float padding) { - YogaValue current = getStylePadding(spacingType); - if (current.unit != YogaUnit.POINT || current.value != padding) { - super.setPadding(spacingType, padding); - mPaddingChanged = true; - markUpdated(); - } - } - - @Override - public void setPaddingPercent(int spacingType, float percent) { - YogaValue current = getStylePadding(spacingType); - if (current.unit != YogaUnit.PERCENT || current.value != percent) { - super.setPadding(spacingType, percent); - mPaddingChanged = true; - markUpdated(); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootShadowNode.java deleted file mode 100644 index b60ab205b..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootShadowNode.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -/** - * Root node of the shadow node hierarchy. Currently, the only node that can actually map to a View. - */ -/* package */ final class FlatRootShadowNode extends FlatShadowNode { - - /* package */ FlatRootShadowNode() { - forceMountToView(); - signalBackingViewIsCreated(); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootViewManager.java deleted file mode 100644 index 929c8ce81..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatRootViewManager.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.view.ViewGroup; - -import com.facebook.react.uimanager.RootViewManager; - -/* package */ class FlatRootViewManager extends RootViewManager { - - @Override - public void removeAllViews(ViewGroup parent) { - parent.removeAllViewsInLayout(); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java deleted file mode 100644 index 9f96433b8..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java +++ /dev/null @@ -1,570 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Rect; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.uimanager.LayoutShadowNode; -import com.facebook.react.uimanager.OnLayoutEvent; -import com.facebook.react.uimanager.ReactClippingViewGroupHelper; -import com.facebook.react.uimanager.ReactShadowNode; -import com.facebook.react.uimanager.ReactShadowNodeImpl; -import com.facebook.react.uimanager.ReactStylesDiffMap; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import javax.annotation.Nullable; - -/** - * FlatShadowNode is a base class for all shadow node used in FlatUIImplementation. It extends - * {@link LayoutShadowNode} by adding an ability to prepare DrawCommands off the UI thread. - */ -/* package */ class FlatShadowNode extends LayoutShadowNode { - - /* package */ static final FlatShadowNode[] EMPTY_ARRAY = new FlatShadowNode[0]; - - private static final String PROP_OPACITY = "opacity"; - private static final String PROP_RENDER_TO_HARDWARE_TEXTURE = "renderToHardwareTextureAndroid"; - private static final String PROP_ACCESSIBILITY_LABEL = "accessibilityLabel"; - private static final String PROP_ACCESSIBILITY_COMPONENT_TYPE = "accessibilityComponentType"; - private static final String PROP_ACCESSIBILITY_LIVE_REGION = "accessibilityLiveRegion"; - private static final String PROP_IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility"; - private static final String PROP_TEST_ID = "testID"; - private static final String PROP_TRANSFORM = "transform"; - protected static final String PROP_REMOVE_CLIPPED_SUBVIEWS = - ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS; - protected static final String PROP_HORIZONTAL = "horizontal"; - private static final Rect LOGICAL_OFFSET_EMPTY = new Rect(); - // When we first initialize a backing view, we create a view we are going to throw away anyway, - // so instead initialize with a shared view. - private static final DrawView EMPTY_DRAW_VIEW = new DrawView(0); - - private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY; - private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY; - private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY; - private FlatShadowNode[] mNativeChildren = FlatShadowNode.EMPTY_ARRAY; - private NodeRegion mNodeRegion = NodeRegion.EMPTY; - private int mNativeParentTag; - private int mViewLeft; - private int mViewTop; - private int mViewRight; - private int mViewBottom; - private boolean mBackingViewIsCreated; - private @Nullable DrawView mDrawView; - private @Nullable DrawBackgroundColor mDrawBackground; - private boolean mIsUpdated = true; - private boolean mForceMountChildrenToView; - private float mClipLeft; - private float mClipTop; - private float mClipRight; - private float mClipBottom; - - // Used to track whether any of the NodeRegions overflow this Node. This is used to determine - // whether or not we can detach this Node in the context of a container with - // setRemoveClippedSubviews enabled. - private boolean mOverflowsContainer; - // this Rect contains the offset to get the "logical bounds" (i.e. bounds that include taking - // into account overflow visible). - private Rect mLogicalOffset = LOGICAL_OFFSET_EMPTY; - - // last OnLayoutEvent info, only used when shouldNotifyOnLayout() is true. - private int mLayoutX; - private int mLayoutY; - private int mLayoutWidth; - private int mLayoutHeight; - - // clip radius - float mClipRadius; - boolean mClipToBounds = false; - - /* package */ void handleUpdateProperties(ReactStylesDiffMap styles) { - if (!mountsToView()) { - // Make sure we mount this FlatShadowNode to a View if any of these properties are present. - if (styles.hasKey(PROP_OPACITY) || - styles.hasKey(PROP_RENDER_TO_HARDWARE_TEXTURE) || - styles.hasKey(PROP_TEST_ID) || - styles.hasKey(PROP_ACCESSIBILITY_LABEL) || - styles.hasKey(PROP_ACCESSIBILITY_COMPONENT_TYPE) || - styles.hasKey(PROP_ACCESSIBILITY_LIVE_REGION) || - styles.hasKey(PROP_TRANSFORM) || - styles.hasKey(PROP_IMPORTANT_FOR_ACCESSIBILITY) || - styles.hasKey(PROP_REMOVE_CLIPPED_SUBVIEWS)) { - forceMountToView(); - } - } - } - - /* package */ final void forceMountChildrenToView() { - if (mForceMountChildrenToView) { - return; - } - - mForceMountChildrenToView = true; - for (int i = 0, childCount = getChildCount(); i != childCount; ++i) { - ReactShadowNode child = getChildAt(i); - if (child instanceof FlatShadowNode) { - ((FlatShadowNode) child).forceMountToView(); - } - } - } - - /** - * Collects DrawCommands produced by this FlatShadowNode. - */ - protected void collectState( - StateBuilder stateBuilder, - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - if (mDrawBackground != null) { - mDrawBackground = (DrawBackgroundColor) mDrawBackground.updateBoundsAndFreeze( - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom); - stateBuilder.addDrawCommand(mDrawBackground); - } - } - - /** - * Return whether or not this node draws anything - * - * This is used to decide whether or not to collect the NodeRegion for this node. This ensures - * that any FlatShadowNode that does not emit any DrawCommands should not bother handling touch - * (i.e. if it draws absolutely nothing, it is, for all intents and purposes, a layout only node). - * - * @return whether or not this is node draws anything - */ - boolean doesDraw() { - // if it mounts to view or draws a background, we can collect it - otherwise, no, unless a - // child suggests some alternative behavior - return mDrawView != null || mDrawBackground != null; - } - - @ReactProp(name = ViewProps.BACKGROUND_COLOR) - public void setBackgroundColor(int backgroundColor) { - mDrawBackground = (backgroundColor == 0) ? null : new DrawBackgroundColor(backgroundColor); - invalidate(); - } - - @Override - public void setOverflow(String overflow) { - super.setOverflow(overflow); - mClipToBounds = "hidden".equals(overflow); - if (mClipToBounds) { - mOverflowsContainer = false; - if (mClipRadius > DrawView.MINIMUM_ROUNDED_CLIPPING_VALUE) { - // mount to a view if we are overflow: hidden and are clipping, so that we can do one - // clipPath to clip all the children of this node (both DrawCommands and Views). - forceMountToView(); - } - } else { - updateOverflowsContainer(); - } - invalidate(); - } - - public final boolean clipToBounds() { - return mClipToBounds; - } - - @Override - public final int getScreenX() { - return mViewLeft; - } - - @Override - public final int getScreenY() { - return mViewTop; - } - - @Override - public final int getScreenWidth() { - if (mountsToView()) { - return mViewRight - mViewLeft; - } else { - return Math.round(mNodeRegion.getRight() - mNodeRegion.getLeft()); - } - } - - @Override - public final int getScreenHeight() { - if (mountsToView()) { - return mViewBottom - mViewTop; - } else { - return Math.round(mNodeRegion.getBottom() - mNodeRegion.getTop()); - } - } - - @Override - public void addChildAt(ReactShadowNodeImpl child, int i) { - super.addChildAt(child, i); - if (mForceMountChildrenToView && child instanceof FlatShadowNode) { - ((FlatShadowNode) child).forceMountToView(); - } - } - - /** - * Marks root node as updated to trigger a StateBuilder pass to collect DrawCommands for the node - * tree. Use it when FlatShadowNode is updated but doesn't require a layout pass (e.g. background - * color is changed). - */ - protected final void invalidate() { - FlatShadowNode node = this; - - while (true) { - if (node.mountsToView()) { - if (node.mIsUpdated) { - // already updated - return; - } - - node.mIsUpdated = true; - } - - ReactShadowNode parent = node.getParent(); - if (parent == null) { - // not attached to a hierarchy yet - return; - } - - node = (FlatShadowNode) parent; - } - } - - @Override - public void markUpdated() { - super.markUpdated(); - mIsUpdated = true; - invalidate(); - } - - /* package */ final boolean isUpdated() { - return mIsUpdated; - } - - /* package */ final void resetUpdated() { - mIsUpdated = false; - } - - /* package */ final boolean clipBoundsChanged( - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - return mClipLeft != clipLeft || mClipTop != clipTop || - mClipRight != clipRight || mClipBottom != clipBottom; - } - - /* package */ final void setClipBounds( - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - mClipLeft = clipLeft; - mClipTop = clipTop; - mClipRight = clipRight; - mClipBottom = clipBottom; - } - - /** - * Returns an array of DrawCommands to perform during the View's draw pass. - */ - /* package */ final DrawCommand[] getDrawCommands() { - return mDrawCommands; - } - - /** - * Sets an array of DrawCommands to perform during the View's draw pass. StateBuilder uses old - * draw commands to compare to new draw commands and see if the View needs to be redrawn. - */ - /* package */ final void setDrawCommands(DrawCommand[] drawCommands) { - mDrawCommands = drawCommands; - } - - /** - * Sets an array of AttachDetachListeners to call onAttach/onDetach when they are attached to or - * detached from a View that this shadow node maps to. - */ - /* package */ final void setAttachDetachListeners(AttachDetachListener[] listeners) { - mAttachDetachListeners = listeners; - } - - /** - * Returns an array of AttachDetachListeners associated with this shadow node. - */ - /* package */ final AttachDetachListener[] getAttachDetachListeners() { - return mAttachDetachListeners; - } - - /* package */ final FlatShadowNode[] getNativeChildren() { - return mNativeChildren; - } - - /* package */ final void setNativeChildren(FlatShadowNode[] nativeChildren) { - mNativeChildren = nativeChildren; - } - - /* package */ final int getNativeParentTag() { - return mNativeParentTag; - } - - /* package */ final void setNativeParentTag(int nativeParentTag) { - mNativeParentTag = nativeParentTag; - } - - /* package */ final NodeRegion[] getNodeRegions() { - return mNodeRegions; - } - - /* package */ final void setNodeRegions(NodeRegion[] nodeRegion) { - mNodeRegions = nodeRegion; - updateOverflowsContainer(); - } - - /* package */ final void updateOverflowsContainer() { - boolean overflowsContainer = false; - int width = (int) (mNodeRegion.getRight() - mNodeRegion.getLeft()); - int height = (int) (mNodeRegion.getBottom() - mNodeRegion.getTop()); - - float leftBound = 0; - float rightBound = width; - float topBound = 0; - float bottomBound = height; - Rect logicalOffset = null; - - // when we are overflow:visible, we try to figure out if any of the children are outside - // of the bounds of this view. since NodeRegion bounds are relative to their parent (i.e. - // 0, 0 is always the start), we see how much outside of the bounds we are (negative left - // or top, or bottom that's more than height or right that's more than width). we set these - // offsets in mLogicalOffset for being able to more intelligently determine whether or not - // to clip certain subviews. - if (!mClipToBounds && height > 0 && width > 0) { - for (NodeRegion region : mNodeRegions) { - if (region.getLeft() < leftBound) { - leftBound = region.getLeft(); - overflowsContainer = true; - } - - if (region.getRight() > rightBound) { - rightBound = region.getRight(); - overflowsContainer = true; - } - - if (region.getTop() < topBound) { - topBound = region.getTop(); - overflowsContainer = true; - } - - if (region.getBottom() > bottomBound) { - bottomBound = region.getBottom(); - overflowsContainer = true; - } - } - - if (overflowsContainer) { - logicalOffset = new Rect( - (int) leftBound, - (int) topBound, - (int) (rightBound - width), - (int) (bottomBound - height)); - } - } - - // if we don't overflow, let's check if any of the immediate children overflow. - // this is "indirectly recursive," since this method is called when setNodeRegions is called, - // and the children call setNodeRegions before their parent. consequently, when a node deep - // inside the tree overflows, its immediate parent has mOverflowsContainer set to true, and, - // by extension, so do all of its ancestors, sufficing here to only check the immediate - // child's mOverflowsContainer value instead of recursively asking if each child overflows its - // container. - if (!overflowsContainer && mNodeRegion != NodeRegion.EMPTY) { - int children = getChildCount(); - for (int i = 0; i < children; i++) { - ReactShadowNode node = getChildAt(i); - if (node instanceof FlatShadowNode && ((FlatShadowNode) node).mOverflowsContainer) { - Rect childLogicalOffset = ((FlatShadowNode) node).mLogicalOffset; - if (logicalOffset == null) { - logicalOffset = new Rect(); - } - // TODO: t11674025 - improve this - a grandparent may end up having smaller logical - // bounds than its children (because the grandparent's size may be larger than that of - // its child, so the grandchild overflows its parent but not its grandparent). currently, - // if a 100x100 view has a 5x5 view, and inside it has a 10x10 view, the inner most view - // overflows its parent but not its grandparent - the logical bounds on the grandparent - // will still be 5x5 (because they're inherited from the child's logical bounds). this - // has the effect of causing us to clip 5px later than we really have to. - logicalOffset.union(childLogicalOffset); - overflowsContainer = true; - } - } - } - - // if things changed, notify the parent(s) about said changes - while in many cases, this will - // be extra work (since we process this for the parents after the children), in some cases, - // we may have no new node regions in the parent, but have a new node region in the child, and, - // as a result, the parent may not get the correct value for overflows container. - if (mOverflowsContainer != overflowsContainer) { - mOverflowsContainer = overflowsContainer; - mLogicalOffset = logicalOffset == null ? LOGICAL_OFFSET_EMPTY : logicalOffset; - } - } - - /* package */ void updateNodeRegion( - float left, - float top, - float right, - float bottom, - boolean isVirtual) { - if (!mNodeRegion.matches(left, top, right, bottom, isVirtual)) { - setNodeRegion(new NodeRegion(left, top, right, bottom, getReactTag(), isVirtual)); - } - } - - protected final void setNodeRegion(NodeRegion nodeRegion) { - mNodeRegion = nodeRegion; - updateOverflowsContainer(); - } - - /* package */ final NodeRegion getNodeRegion() { - return mNodeRegion; - } - - /** - * Sets boundaries of the View that this node maps to relative to the parent left/top coordinate. - */ - /* package */ final void setViewBounds(int left, int top, int right, int bottom) { - mViewLeft = left; - mViewTop = top; - mViewRight = right; - mViewBottom = bottom; - } - - /** - * Left position of the View this node maps to relative to the parent View. - */ - /* package */ final int getViewLeft() { - return mViewLeft; - } - - /** - * Top position of the View this node maps to relative to the parent View. - */ - /* package */ final int getViewTop() { - return mViewTop; - } - - /** - * Right position of the View this node maps to relative to the parent View. - */ - /* package */ final int getViewRight() { - return mViewRight; - } - - /** - * Bottom position of the View this node maps to relative to the parent View. - */ - /* package */ final int getViewBottom() { - return mViewBottom; - } - - /* package */ final void forceMountToView() { - if (isVirtual()) { - return; - } - - if (mDrawView == null) { - // Create a new DrawView, but we might not know our react tag yet, so set it to 0 in the - // meantime. - mDrawView = EMPTY_DRAW_VIEW; - invalidate(); - - // reset NodeRegion to allow it getting garbage-collected - mNodeRegion = NodeRegion.EMPTY; - } - } - - /* package */ final DrawView collectDrawView( - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - Assertions.assumeNotNull(mDrawView); - if (mDrawView == EMPTY_DRAW_VIEW) { - // This is the first time we have collected this DrawView, but we have to create a new - // DrawView anyway, as reactTag is final, and our DrawView instance is the static copy. - mDrawView = new DrawView(getReactTag()); - } - - // avoid path clipping if overflow: visible - float clipRadius = mClipToBounds ? mClipRadius : 0.0f; - // We have the correct react tag, but we may need a new copy with updated bounds. If the bounds - // match or were never set, the same view is returned. - mDrawView = mDrawView.collectDrawView( - left, - top, - right, - bottom, - left + mLogicalOffset.left, - top + mLogicalOffset.top, - right + mLogicalOffset.right, - bottom + mLogicalOffset.bottom, - clipLeft, - clipTop, - clipRight, - clipBottom, - clipRadius); - return mDrawView; - } - - @Nullable - /* package */ final OnLayoutEvent obtainLayoutEvent(int x, int y, int width, int height) { - if (mLayoutX == x && mLayoutY == y && mLayoutWidth == width && mLayoutHeight == height) { - return null; - } - - mLayoutX = x; - mLayoutY = y; - mLayoutWidth = width; - mLayoutHeight = height; - - return OnLayoutEvent.obtain(getReactTag(), x, y, width, height); - } - - /* package */ final boolean mountsToView() { - return mDrawView != null; - } - - /* package */ final boolean isBackingViewCreated() { - return mBackingViewIsCreated; - } - - /* package */ final void signalBackingViewIsCreated() { - mBackingViewIsCreated = true; - } - - public boolean clipsSubviews() { - return false; - } - - public boolean isHorizontal() { - return false; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatTextShadowNode.java deleted file mode 100644 index 90a349d2e..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatTextShadowNode.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.text.SpannableStringBuilder; - -import com.facebook.react.uimanager.ReactShadowNode; - -/** - * Base class for RCTVirtualText and RCTRawText. - */ -/* package */ abstract class FlatTextShadowNode extends FlatShadowNode { - - // these 2 are only used between collectText() and applySpans() calls. - private int mTextBegin; - private int mTextEnd; - - /** - * Propagates changes up to RCTText without dirtying current node. - */ - protected void notifyChanged(boolean shouldRemeasure) { - ReactShadowNode parent = getParent(); - if (parent instanceof FlatTextShadowNode) { - ((FlatTextShadowNode) parent).notifyChanged(shouldRemeasure); - } - } - - @Override - public boolean isVirtual() { - return true; - } - - /** - * Recursively visits FlatTextShadowNode and its children, - * appending text to SpannableStringBuilder. - */ - /* package */ final void collectText(SpannableStringBuilder builder) { - mTextBegin = builder.length(); - performCollectText(builder); - mTextEnd = builder.length(); - } - - /** - * Whether or not to allow empty spans to be set - * This is used to bypass an optimization in {@code applySpans} that skips applying spans if - * there is no text (since, for TextInput, for example, we want to apply the span even if there - * is no text so that newly typed text gets styled properly). - * - * @return a boolean representing whether or not we should allow empty spans - */ - /* package */ boolean shouldAllowEmptySpans() { - return false; - } - - /* package */ boolean isEditable() { - return false; - } - - /** - * Recursively visits FlatTextShadowNode and its children, - * applying spans to SpannableStringBuilder. - */ - /* package */ final void applySpans(SpannableStringBuilder builder, boolean isEditable) { - if (mTextBegin != mTextEnd || shouldAllowEmptySpans()) { - performApplySpans(builder, mTextBegin, mTextEnd, isEditable); - } - } - - protected abstract void performCollectText(SpannableStringBuilder builder); - protected abstract void performApplySpans( - SpannableStringBuilder builder, - int begin, - int end, - boolean isEditable); - protected abstract void performCollectAttachDetachListeners(StateBuilder stateBuilder); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java deleted file mode 100644 index 74174d2b6..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java +++ /dev/null @@ -1,608 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.modules.fresco.FrescoModule; -import com.facebook.react.modules.i18nmanager.I18nUtil; -import com.facebook.react.uimanager.ReactShadowNode; -import com.facebook.react.uimanager.ReactStylesDiffMap; -import com.facebook.react.uimanager.UIImplementation; -import com.facebook.react.uimanager.ViewManager; -import com.facebook.react.uimanager.ViewManagerRegistry; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.yoga.YogaDirection; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.annotation.Nullable; - -/** - * FlatUIImplementation builds on top of UIImplementation and allows pre-creating everything - * required for drawing (DrawCommands) and touching (NodeRegions) views in background thread - * for faster drawing and interactions. - */ -public class FlatUIImplementation extends UIImplementation { - - private static final Map> flatManagerClassMap; - - static { - flatManagerClassMap = new HashMap<>(); - flatManagerClassMap.put(RCTViewManager.REACT_CLASS, RCTViewManager.class); - flatManagerClassMap.put(RCTTextManager.REACT_CLASS, RCTTextManager.class); - flatManagerClassMap.put(RCTRawTextManager.REACT_CLASS, RCTRawTextManager.class); - flatManagerClassMap.put(RCTVirtualTextManager.REACT_CLASS, RCTVirtualTextManager.class); - flatManagerClassMap.put(RCTTextInlineImageManager.REACT_CLASS, RCTTextInlineImageManager.class); - flatManagerClassMap.put(RCTImageViewManager.REACT_CLASS, RCTImageViewManager.class); - flatManagerClassMap.put(RCTTextInputManager.REACT_CLASS, RCTTextInputManager.class); - flatManagerClassMap.put(RCTViewPagerManager.REACT_CLASS, RCTViewPagerManager.class); - flatManagerClassMap.put(FlatARTSurfaceViewManager.REACT_CLASS, FlatARTSurfaceViewManager.class); - flatManagerClassMap.put(RCTModalHostManager.REACT_CLASS, RCTModalHostManager.class); - } - - /** - * Build the map of view managers, checking that the managers FlatUI requires are correctly - * overridden. - */ - private static Map buildViewManagerMap(List viewManagers) { - Map viewManagerMap = new HashMap<>(); - for (ViewManager viewManager : viewManagers) { - viewManagerMap.put(viewManager.getName(), viewManager); - } - for (Map.Entry> entry : flatManagerClassMap.entrySet()) { - String name = entry.getKey(); - ViewManager maybeFlatViewManager = viewManagerMap.get(name); - if (maybeFlatViewManager == null) { - // We don't have a view manager for this name in the package, no need to add one. - continue; - } - - Class flatClazz = entry.getValue(); - if (maybeFlatViewManager.getClass() != flatClazz) { - // If we have instances that have flat equivalents, override them. - try { - viewManagerMap.put(name, flatClazz.newInstance()); - } catch (IllegalAccessException e) { - throw new RuntimeException("Unable to access flat class for " + name, e); - } catch (InstantiationException e) { - throw new RuntimeException("Unable to instantiate flat class for " + name, e); - } - } - } - return viewManagerMap; - } - - public static FlatUIImplementation createInstance( - ReactApplicationContext reactContext, - List viewManagers, - EventDispatcher eventDispatcher, - boolean memoryImprovementEnabled, - int minTimeLeftInFrameForNonBatchedOperationMs) { - - Map viewManagerMap = buildViewManagerMap(viewManagers); - - RCTImageViewManager imageViewManager = - (RCTImageViewManager) viewManagerMap.get(RCTImageViewManager.REACT_CLASS); - if (imageViewManager != null) { - Object callerContext = imageViewManager.getCallerContext(); - if (callerContext != null) { - RCTImageView.setCallerContext(callerContext); - } - } - DraweeRequestHelper.setResources(reactContext.getResources()); - - TypefaceCache.setAssetManager(reactContext.getAssets()); - - ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagerMap); - FlatNativeViewHierarchyManager nativeViewHierarchyManager = new FlatNativeViewHierarchyManager( - viewManagerRegistry); - FlatUIViewOperationQueue operationsQueue = - new FlatUIViewOperationQueue( - reactContext, nativeViewHierarchyManager, minTimeLeftInFrameForNonBatchedOperationMs); - return new FlatUIImplementation( - reactContext, - imageViewManager, - viewManagerRegistry, - operationsQueue, - eventDispatcher, - memoryImprovementEnabled - ); - } - - /** - * Helper class that sorts moveTo/moveFrom arrays passed to #manageChildren(). - * Not used outside of the said method. - */ - private final MoveProxy mMoveProxy = new MoveProxy(); - private final ReactApplicationContext mReactContext; - private @Nullable RCTImageViewManager mRCTImageViewManager; - private final StateBuilder mStateBuilder; - private final boolean mMemoryImprovementEnabled; - - private FlatUIImplementation( - ReactApplicationContext reactContext, - @Nullable RCTImageViewManager rctImageViewManager, - ViewManagerRegistry viewManagers, - FlatUIViewOperationQueue operationsQueue, - EventDispatcher eventDispatcher, - boolean memoryImprovementEnabled) { - super(reactContext, viewManagers, operationsQueue, eventDispatcher); - mReactContext = reactContext; - mRCTImageViewManager = rctImageViewManager; - mStateBuilder = new StateBuilder(operationsQueue); - mMemoryImprovementEnabled = memoryImprovementEnabled; - } - - @Override - protected ReactShadowNode createRootShadowNode() { - if (mRCTImageViewManager != null) { - // This is not the best place to initialize DraweeRequestHelper, but order of module - // initialization is undefined, and this is pretty much the earliest when we are guarantied - // that Fresco is initialized and DraweeControllerBuilder can be queried. This also happens - // relatively rarely to have any performance considerations. - mReactContext.getNativeModule(FrescoModule.class); // initialize Fresco - DraweeRequestHelper.setDraweeControllerBuilder( - mRCTImageViewManager.getDraweeControllerBuilder()); - mRCTImageViewManager = null; - } - - ReactShadowNode node = new FlatRootShadowNode(); - I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance(); - if (sharedI18nUtilInstance.isRTL(mReactContext)) { - node.setLayoutDirection(YogaDirection.RTL); - } - return node; - } - - @Override - protected ReactShadowNode createShadowNode(String className) { - ReactShadowNode cssNode = super.createShadowNode(className); - if (cssNode instanceof FlatShadowNode || cssNode.isVirtual()) { - return cssNode; - } - - ViewManager viewManager = resolveViewManager(className); - return new NativeViewWrapper(viewManager); - } - - @Override - protected void handleCreateView( - ReactShadowNode cssNode, - int rootViewTag, - @Nullable ReactStylesDiffMap styles) { - if (cssNode instanceof FlatShadowNode) { - FlatShadowNode node = (FlatShadowNode) cssNode; - - if (styles != null) { - node.handleUpdateProperties(styles); - } - - if (node.mountsToView()) { - mStateBuilder.enqueueCreateOrUpdateView(node, styles); - } - } else { - super.handleCreateView(cssNode, rootViewTag, styles); - } - } - - @Override - protected void handleUpdateView( - ReactShadowNode cssNode, - String className, - ReactStylesDiffMap styles) { - if (cssNode instanceof FlatShadowNode) { - FlatShadowNode node = (FlatShadowNode) cssNode; - - node.handleUpdateProperties(styles); - - if (node.mountsToView()) { - mStateBuilder.enqueueCreateOrUpdateView(node, styles); - } - } else { - super.handleUpdateView(cssNode, className, styles); - } - } - - @Override - public void manageChildren( - int viewTag, - @Nullable ReadableArray moveFrom, - @Nullable ReadableArray moveTo, - @Nullable ReadableArray addChildTags, - @Nullable ReadableArray addAtIndices, - @Nullable ReadableArray removeFrom) { - - ReactShadowNode parentNode = resolveShadowNode(viewTag); - - // moveFrom and removeFrom are defined in original order before any mutations. - removeChildren(parentNode, moveFrom, moveTo, removeFrom); - - // moveTo and addAtIndices are defined in final order after all the mutations applied. - addChildren(parentNode, addChildTags, addAtIndices); - } - - @Override - public void setChildren( - int viewTag, - ReadableArray children) { - - ReactShadowNode parentNode = resolveShadowNode(viewTag); - - for (int i = 0; i < children.size(); i++) { - ReactShadowNode addToChild = resolveShadowNode(children.getInt(i)); - addChildAt(parentNode, addToChild, i, i - 1); - } - } - - @Override - public void measure(int reactTag, Callback callback) { - measureHelper(reactTag, false, callback); - } - - private void measureHelper(int reactTag, boolean relativeToWindow, Callback callback) { - FlatShadowNode node = (FlatShadowNode) resolveShadowNode(reactTag); - if (node.mountsToView()) { - mStateBuilder.ensureBackingViewIsCreated(node); - if (relativeToWindow) { - super.measureInWindow(reactTag, callback); - } else { - super.measure(reactTag, callback); - } - return; - } - - // virtual nodes do not have values for width and height, so get these values - // from the first non-virtual parent node - while (node != null && node.isVirtual()) { - node = (FlatShadowNode) node.getParent(); - } - - if (node == null) { - // everything is virtual, this shouldn't happen so just silently return - return; - } - - float width = node.getLayoutWidth(); - float height = node.getLayoutHeight(); - - boolean nodeMountsToView = node.mountsToView(); - // this is to avoid double-counting xInParent and yInParent when we visit - // the while loop, below. - float xInParent = nodeMountsToView ? node.getLayoutX() : 0; - float yInParent = nodeMountsToView ? node.getLayoutY() : 0; - - while (!node.mountsToView()) { - if (!node.isVirtual()) { - xInParent += node.getLayoutX(); - yInParent += node.getLayoutY(); - } - - node = Assertions.assumeNotNull((FlatShadowNode) node.getParent()); - } - - float parentWidth = node.getLayoutWidth(); - float parentHeight = node.getLayoutHeight(); - - FlatUIViewOperationQueue operationsQueue = mStateBuilder.getOperationsQueue(); - operationsQueue.enqueueMeasureVirtualView( - node.getReactTag(), - xInParent / parentWidth, - yInParent / parentHeight, - width / parentWidth, - height / parentHeight, - relativeToWindow, - callback); - } - - private void ensureMountsToViewAndBackingViewIsCreated(int reactTag) { - FlatShadowNode node = (FlatShadowNode) resolveShadowNode(reactTag); - if (node.isBackingViewCreated()) { - return; - } - node.forceMountToView(); - mStateBuilder.ensureBackingViewIsCreated(node); - } - - @Override - public void findSubviewIn(int reactTag, float targetX, float targetY, Callback callback) { - ensureMountsToViewAndBackingViewIsCreated(reactTag); - super.findSubviewIn(reactTag, targetX, targetY, callback); - } - - @Override - public void measureInWindow(int reactTag, Callback callback) { - measureHelper(reactTag, true, callback); - } - - @Override - public void addAnimation(int reactTag, int animationID, Callback onSuccess) { - ensureMountsToViewAndBackingViewIsCreated(reactTag); - super.addAnimation(reactTag, animationID, onSuccess); - } - - @Override - public void dispatchViewManagerCommand(int reactTag, int commandId, ReadableArray commandArgs) { - // Make sure that our target view is actually a view, then delay command dispatch until after - // we have updated the view hierarchy. - ensureMountsToViewAndBackingViewIsCreated(reactTag); - mStateBuilder.enqueueViewManagerCommand(reactTag, commandId, commandArgs); - } - - @Override - public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Callback success) { - ensureMountsToViewAndBackingViewIsCreated(reactTag); - super.showPopupMenu(reactTag, items, error, success); - } - - @Override - public void sendAccessibilityEvent(int reactTag, int eventType) { - ensureMountsToViewAndBackingViewIsCreated(reactTag); - super.sendAccessibilityEvent(reactTag, eventType); - } - - /** - * Removes all children defined by moveFrom and removeFrom from a given parent, - * preparing elements in moveFrom to be re-added at proper index. - */ - private void removeChildren( - ReactShadowNode parentNode, - @Nullable ReadableArray moveFrom, - @Nullable ReadableArray moveTo, - @Nullable ReadableArray removeFrom) { - - int prevIndex = Integer.MAX_VALUE; - - mMoveProxy.setup(moveFrom, moveTo); - - int moveFromIndex = mMoveProxy.size() - 1; - int moveFromChildIndex = (moveFromIndex == -1) ? -1 : mMoveProxy.getMoveFrom(moveFromIndex); - - int numToRemove = removeFrom == null ? 0 : removeFrom.size(); - int[] indicesToRemove = new int[numToRemove]; - if (numToRemove > 0) { - Assertions.assertNotNull(removeFrom); - for (int i = 0; i < numToRemove; i++) { - int indexToRemove = removeFrom.getInt(i); - indicesToRemove[i] = indexToRemove; - } - } - - // this isn't guaranteed to be sorted actually - Arrays.sort(indicesToRemove); - - int removeFromIndex; - int removeFromChildIndex; - if (removeFrom == null) { - removeFromIndex = -1; - removeFromChildIndex = -1; - } else { - removeFromIndex = indicesToRemove.length - 1; - removeFromChildIndex = indicesToRemove[removeFromIndex]; - } - - // both moveFrom and removeFrom are already sorted, but combined order is not sorted. Use - // a merge step from mergesort to walk over both arrays and extract elements in sorted order. - - while (true) { - if (moveFromChildIndex > removeFromChildIndex) { - moveChild(removeChildAt(parentNode, moveFromChildIndex, prevIndex), moveFromIndex); - prevIndex = moveFromChildIndex; - - --moveFromIndex; - moveFromChildIndex = (moveFromIndex == -1) ? -1 : mMoveProxy.getMoveFrom(moveFromIndex); - } else if (removeFromChildIndex > moveFromChildIndex) { - removeChild(removeChildAt(parentNode, removeFromChildIndex, prevIndex), parentNode); - prevIndex = removeFromChildIndex; - - --removeFromIndex; - removeFromChildIndex = (removeFromIndex == -1) ? -1 : indicesToRemove[removeFromIndex]; - } else { - // moveFromChildIndex == removeFromChildIndex can only be if both are equal to -1 - // which means that we exhausted both arrays, and all children are removed. - break; - } - } - } - - /** - * Unregisters given element and all of its children from ShadowNodeRegistry, - * and drops all Views used by it and its children. - */ - private void removeChild(ReactShadowNode child, ReactShadowNode parentNode) { - dropNativeViews(child, parentNode); - removeShadowNode(child); - } - - private void dropNativeViews(ReactShadowNode child, ReactShadowNode parentNode) { - if (child instanceof FlatShadowNode) { - FlatShadowNode node = (FlatShadowNode) child; - if (node.mountsToView() && node.isBackingViewCreated()) { - int tag = -1; - - // this tag is used to remove the reference to this dropping view if it it's clipped. - // we need to figure out the correct "view parent" tag to do this. note that this is - // not necessarily getParent().getReactTag(), since getParent() may represent something - // that's not a View - we need to find the first View (what would represent - // view.getParent() on the ui thread), which is what this code is finding. - ReactShadowNode tmpNode = parentNode; - while (tmpNode != null) { - if (tmpNode instanceof FlatShadowNode) { - FlatShadowNode flatTmpNode = (FlatShadowNode) tmpNode; - if (flatTmpNode.mountsToView() && flatTmpNode.isBackingViewCreated() && - flatTmpNode.getParent() != null) { - tag = flatTmpNode.getReactTag(); - break; - } - } - tmpNode = tmpNode.getParent(); - } - - // this will recursively drop all subviews - mStateBuilder.dropView(node, tag); - return; - } - } - - for (int i = 0, childCount = child.getChildCount(); i != childCount; ++i) { - dropNativeViews(child.getChildAt(i), child); - } - } - - /** - * Prepares a given element to be moved to a new position. - */ - private void moveChild(ReactShadowNode child, int moveFromIndex) { - mMoveProxy.setChildMoveFrom(moveFromIndex, child); - } - - /** - * Adds all children from addChildTags and moveFrom/moveTo. - */ - private void addChildren( - ReactShadowNode parentNode, - @Nullable ReadableArray addChildTags, - @Nullable ReadableArray addAtIndices) { - - int prevIndex = -1; - - int moveToIndex; - int moveToChildIndex; - if (mMoveProxy.size() == 0) { - moveToIndex = Integer.MAX_VALUE; - moveToChildIndex = Integer.MAX_VALUE; - } else { - moveToIndex = 0; - moveToChildIndex = mMoveProxy.getMoveTo(0); - } - - int numNodesToAdd; - int addToIndex; - int addToChildIndex; - if (addAtIndices == null) { - numNodesToAdd = 0; - addToIndex = Integer.MAX_VALUE; - addToChildIndex = Integer.MAX_VALUE; - } else { - numNodesToAdd = addAtIndices.size(); - addToIndex = 0; - addToChildIndex = addAtIndices.getInt(0); - } - - // both mMoveProxy and addChildTags are already sorted, but combined order is not sorted. Use - // a merge step from mergesort to walk over both arrays and extract elements in sorted order. - - while (true) { - if (addToChildIndex < moveToChildIndex) { - ReactShadowNode addToChild = resolveShadowNode(addChildTags.getInt(addToIndex)); - addChildAt(parentNode, addToChild, addToChildIndex, prevIndex); - prevIndex = addToChildIndex; - - ++addToIndex; - if (addToIndex == numNodesToAdd) { - addToChildIndex = Integer.MAX_VALUE; - } else { - addToChildIndex = addAtIndices.getInt(addToIndex); - } - } else if (moveToChildIndex < addToChildIndex) { - ReactShadowNode moveToChild = mMoveProxy.getChildMoveTo(moveToIndex); - addChildAt(parentNode, moveToChild, moveToChildIndex, prevIndex); - prevIndex = moveToChildIndex; - - ++moveToIndex; - if (moveToIndex == mMoveProxy.size()) { - moveToChildIndex = Integer.MAX_VALUE; - } else { - moveToChildIndex = mMoveProxy.getMoveTo(moveToIndex); - } - } else { - // moveToChildIndex == addToChildIndex can only be if both are equal to Integer.MAX_VALUE - // which means that we exhausted both arrays, and all children are added. - break; - } - } - } - - /** - * Removes a child from parent, verifying that we are removing in descending order. - */ - private static ReactShadowNode removeChildAt( - ReactShadowNode parentNode, - int index, - int prevIndex) { - if (index >= prevIndex) { - throw new RuntimeException( - "Invariant failure, needs sorting! " + index + " >= " + prevIndex); - } - - return parentNode.removeChildAt(index); - } - - /** - * Adds a child to parent, verifying that we are adding in ascending order. - */ - private static void addChildAt( - ReactShadowNode parentNode, - ReactShadowNode childNode, - int index, - int prevIndex) { - if (index <= prevIndex) { - throw new RuntimeException( - "Invariant failure, needs sorting! " + index + " <= " + prevIndex); - } - - parentNode.addChildAt(childNode, index); - } - - @Override - protected void updateViewHierarchy() { - super.updateViewHierarchy(); - mStateBuilder.afterUpdateViewHierarchy(mEventDispatcher); - } - - @Override - protected void applyUpdatesRecursive( - ReactShadowNode cssNode, - float absoluteX, - float absoluteY) { - mStateBuilder.applyUpdates((FlatRootShadowNode) cssNode); - } - - @Override - public void removeRootView(int rootViewTag) { - if (mMemoryImprovementEnabled) { - removeRootShadowNode(rootViewTag); - } - mStateBuilder.removeRootView(rootViewTag); - } - - @Override - public void setJSResponder(int possiblyVirtualReactTag, boolean blockNativeResponder) { - ReactShadowNode node = resolveShadowNode(possiblyVirtualReactTag); - while (node.isVirtual()) { - node = node.getParent(); - } - int tag = node.getReactTag(); - - // if the node in question doesn't mount to a View, find the first parent that does mount to - // a View. without this, we'll crash when we try to set the JSResponder, since part of that - // is to find the parent view and ask it to not intercept touch events. - while (node instanceof FlatShadowNode && !((FlatShadowNode) node).mountsToView()) { - node = node.getParent(); - } - - FlatUIViewOperationQueue operationsQueue = mStateBuilder.getOperationsQueue(); - operationsQueue.enqueueSetJSResponder( - node == null ? tag : node.getReactTag(), - possiblyVirtualReactTag, - blockNativeResponder); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java deleted file mode 100644 index 3806e2ebc..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java +++ /dev/null @@ -1,527 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.react.uimanager.UIViewOperationQueue; -import android.util.SparseIntArray; -import android.view.View; -import android.view.ViewGroup; -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.IllegalViewOperationException; -import com.facebook.react.uimanager.NoSuchNativeViewException; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.TouchTargetHelper; -import com.facebook.react.uimanager.UIViewOperationQueue; -import java.util.ArrayList; -import javax.annotation.Nullable; - -/** - * FlatUIViewOperationQueue extends {@link UIViewOperationQueue} to add - * FlatUIImplementation-specific methods that need to run in UI thread. - */ -/* package */ final class FlatUIViewOperationQueue extends UIViewOperationQueue { - - private static final int[] MEASURE_BUFFER = new int[4]; - - private final FlatNativeViewHierarchyManager mNativeViewHierarchyManager; - private final ProcessLayoutRequests mProcessLayoutRequests = new ProcessLayoutRequests(); - - private final class ProcessLayoutRequests implements UIViewOperationQueue.UIOperation { - @Override - public void execute() { - FlatViewGroup.processLayoutRequests(); - } - } - - /** - * UIOperation that updates DrawCommands for a View defined by reactTag. - */ - private final class UpdateMountState implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final @Nullable DrawCommand[] mDrawCommands; - private final @Nullable AttachDetachListener[] mAttachDetachListeners; - private final @Nullable NodeRegion[] mNodeRegions; - - private UpdateMountState( - int reactTag, - @Nullable DrawCommand[] drawCommands, - @Nullable AttachDetachListener[] listeners, - @Nullable NodeRegion[] nodeRegions) { - mReactTag = reactTag; - mDrawCommands = drawCommands; - mAttachDetachListeners = listeners; - mNodeRegions = nodeRegions; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.updateMountState( - mReactTag, - mDrawCommands, - mAttachDetachListeners, - mNodeRegions); - } - } - - /** - * UIOperation that updates DrawCommands for a View defined by reactTag. - */ - private final class UpdateClippingMountState implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final @Nullable DrawCommand[] mDrawCommands; - private final SparseIntArray mDrawViewIndexMap; - private final float[] mCommandMaxBot; - private final float[] mCommandMinTop; - private final @Nullable AttachDetachListener[] mAttachDetachListeners; - private final @Nullable NodeRegion[] mNodeRegions; - private final float[] mRegionMaxBot; - private final float[] mRegionMinTop; - private final boolean mWillMountViews; - - private UpdateClippingMountState( - int reactTag, - @Nullable DrawCommand[] drawCommands, - SparseIntArray drawViewIndexMap, - float[] commandMaxBot, - float[] commandMinTop, - @Nullable AttachDetachListener[] listeners, - @Nullable NodeRegion[] nodeRegions, - float[] regionMaxBot, - float[] regionMinTop, - boolean willMountViews) { - mReactTag = reactTag; - mDrawCommands = drawCommands; - mDrawViewIndexMap = drawViewIndexMap; - mCommandMaxBot = commandMaxBot; - mCommandMinTop = commandMinTop; - mAttachDetachListeners = listeners; - mNodeRegions = nodeRegions; - mRegionMaxBot = regionMaxBot; - mRegionMinTop = regionMinTop; - mWillMountViews = willMountViews; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.updateClippingMountState( - mReactTag, - mDrawCommands, - mDrawViewIndexMap, - mCommandMaxBot, - mCommandMinTop, - mAttachDetachListeners, - mNodeRegions, - mRegionMaxBot, - mRegionMinTop, - mWillMountViews); - } - } - - private final class UpdateViewGroup implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final int[] mViewsToAdd; - private final int[] mViewsToDetach; - - private UpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) { - mReactTag = reactTag; - mViewsToAdd = viewsToAdd; - mViewsToDetach = viewsToDetach; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.updateViewGroup(mReactTag, mViewsToAdd, mViewsToDetach); - } - } - - /** - * UIOperation that updates View bounds for a View defined by reactTag. - */ - public final class UpdateViewBounds implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final int mLeft; - private final int mTop; - private final int mRight; - private final int mBottom; - - private UpdateViewBounds(int reactTag, int left, int top, int right, int bottom) { - mReactTag = reactTag; - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.updateViewBounds(mReactTag, mLeft, mTop, mRight, mBottom); - } - } - - private final class SetPadding implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final int mPaddingLeft; - private final int mPaddingTop; - private final int mPaddingRight; - private final int mPaddingBottom; - - private SetPadding( - int reactTag, - int paddingLeft, - int paddingTop, - int paddingRight, - int paddingBottom) { - mReactTag = reactTag; - mPaddingLeft = paddingLeft; - mPaddingTop = paddingTop; - mPaddingRight = paddingRight; - mPaddingBottom = paddingBottom; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.setPadding( - mReactTag, - mPaddingLeft, - mPaddingTop, - mPaddingRight, - mPaddingBottom); - } - } - - private final class DropViews implements UIViewOperationQueue.UIOperation { - - private final SparseIntArray mViewsToDrop; - - private DropViews(ArrayList viewsToDrop, ArrayList parentsForViewsToDrop) { - SparseIntArray sparseIntArray = new SparseIntArray(); - for (int i = 0, count = viewsToDrop.size(); i < count; i++) { - sparseIntArray.put(viewsToDrop.get(i), parentsForViewsToDrop.get(i)); - } - mViewsToDrop = sparseIntArray; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.dropViews(mViewsToDrop); - } - } - - private final class MeasureVirtualView implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final float mScaledX; - private final float mScaledY; - private final float mScaledWidth; - private final float mScaledHeight; - private final Callback mCallback; - private final boolean mRelativeToWindow; - - private MeasureVirtualView( - int reactTag, - float scaledX, - float scaledY, - float scaledWidth, - float scaledHeight, - boolean relativeToWindow, - Callback callback) { - mReactTag = reactTag; - mScaledX = scaledX; - mScaledY = scaledY; - mScaledWidth = scaledWidth; - mScaledHeight = scaledHeight; - mCallback = callback; - mRelativeToWindow = relativeToWindow; - } - - @Override - public void execute() { - try { - // Measure native View - if (mRelativeToWindow) { - // relative to the window - mNativeViewHierarchyManager.measureInWindow(mReactTag, MEASURE_BUFFER); - } else { - // relative to the root view - mNativeViewHierarchyManager.measure(mReactTag, MEASURE_BUFFER); - } - } catch (NoSuchNativeViewException noSuchNativeViewException) { - // Invoke with no args to signal failure and to allow JS to clean up the callback - // handle. - mCallback.invoke(); - return; - } - - float nativeViewX = MEASURE_BUFFER[0]; - float nativeViewY = MEASURE_BUFFER[1]; - float nativeViewWidth = MEASURE_BUFFER[2]; - float nativeViewHeight = MEASURE_BUFFER[3]; - - // Calculate size of the virtual child inside native View. - float x = PixelUtil.toDIPFromPixel(mScaledX * nativeViewWidth + nativeViewX); - float y = PixelUtil.toDIPFromPixel(mScaledY * nativeViewHeight + nativeViewY); - float width = PixelUtil.toDIPFromPixel(mScaledWidth * nativeViewWidth); - float height = PixelUtil.toDIPFromPixel(mScaledHeight * nativeViewHeight); - - if (mRelativeToWindow) { - mCallback.invoke(x, y, width, height); - } else { - mCallback.invoke(0, 0, width, height, x, y); - } - } - } - - public final class DetachAllChildrenFromViews implements UIViewOperationQueue.UIOperation { - private @Nullable int[] mViewsToDetachAllChildrenFrom; - - public void setViewsToDetachAllChildrenFrom(int[] viewsToDetachAllChildrenFrom) { - mViewsToDetachAllChildrenFrom = viewsToDetachAllChildrenFrom; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.detachAllChildrenFromViews(mViewsToDetachAllChildrenFrom); - } - } - - private final class FindTargetForTouchOperation implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final float mTargetX; - private final float mTargetY; - private final Callback mCallback; - private final int[] NATIVE_VIEW_BUFFER = new int[1]; - - private FindTargetForTouchOperation( - final int reactTag, - final float targetX, - final float targetY, - final Callback callback) { - super(); - mReactTag = reactTag; - mTargetX = targetX; - mTargetY = targetY; - mCallback = callback; - } - - @Override - public void execute() { - try { - mNativeViewHierarchyManager.measure(mReactTag, MEASURE_BUFFER); - } catch (IllegalViewOperationException e) { - mCallback.invoke(); - return; - } - - // Because React coordinates are relative to root container, and measure() operates - // on screen coordinates, we need to offset values using root container location. - final float containerX = (float) MEASURE_BUFFER[0]; - final float containerY = (float) MEASURE_BUFFER[1]; - - View view = mNativeViewHierarchyManager.getView(mReactTag); - final int touchTargetReactTag = TouchTargetHelper.findTargetTagForTouch( - mTargetX, - mTargetY, - (ViewGroup) view, - NATIVE_VIEW_BUFFER); - - try { - mNativeViewHierarchyManager.measure( - NATIVE_VIEW_BUFFER[0], - MEASURE_BUFFER); - } catch (IllegalViewOperationException e) { - mCallback.invoke(); - return; - } - - NodeRegion region = NodeRegion.EMPTY; - boolean isNativeView = NATIVE_VIEW_BUFFER[0] == touchTargetReactTag; - if (!isNativeView) { - // NATIVE_VIEW_BUFFER[0] is a FlatViewGroup, touchTargetReactTag is the touch target and - // isn't an Android View - try to get its NodeRegion - view = mNativeViewHierarchyManager.getView(NATIVE_VIEW_BUFFER[0]); - if (view instanceof FlatViewGroup) { - region = ((FlatViewGroup) view).getNodeRegionForTag(mReactTag); - } - } - - int resultTag = region == NodeRegion.EMPTY ? touchTargetReactTag : region.mTag; - float x = PixelUtil.toDIPFromPixel(region.getLeft() + MEASURE_BUFFER[0] - containerX); - float y = PixelUtil.toDIPFromPixel(region.getTop() + MEASURE_BUFFER[1] - containerY); - float width = PixelUtil.toDIPFromPixel(isNativeView ? - MEASURE_BUFFER[2] : region.getRight() - region.getLeft()); - float height = PixelUtil.toDIPFromPixel(isNativeView ? - MEASURE_BUFFER[3] : region.getBottom() - region.getTop()); - mCallback.invoke(resultTag, x, y, width, height); - } - } - - /** - * Used to delay view manager command dispatch until after the view hierarchy is updated. - * Mirrors command operation dispatch, but is only used in Nodes for view manager commands. - */ - public final class ViewManagerCommand implements UIViewOperationQueue.UIOperation { - - private final int mReactTag; - private final int mCommand; - private final @Nullable ReadableArray mArgs; - - public ViewManagerCommand( - int reactTag, - int command, - @Nullable ReadableArray args) { - mReactTag = reactTag; - mCommand = command; - mArgs = args; - } - - @Override - public void execute() { - mNativeViewHierarchyManager.dispatchCommand(mReactTag, mCommand, mArgs); - } - } - - public FlatUIViewOperationQueue( - ReactApplicationContext reactContext, - FlatNativeViewHierarchyManager nativeViewHierarchyManager, - int minTimeLeftInFrameForNonBatchedOperationMs) { - super(reactContext, nativeViewHierarchyManager, minTimeLeftInFrameForNonBatchedOperationMs); - - mNativeViewHierarchyManager = nativeViewHierarchyManager; - } - - /** - * Enqueues a new UIOperation that will update DrawCommands for a View defined by reactTag. - */ - public void enqueueUpdateMountState( - int reactTag, - @Nullable DrawCommand[] drawCommands, - @Nullable AttachDetachListener[] listeners, - @Nullable NodeRegion[] nodeRegions) { - enqueueUIOperation(new UpdateMountState( - reactTag, - drawCommands, - listeners, - nodeRegions)); - } - - /** - * Enqueues a new UIOperation that will update DrawCommands for a View defined by reactTag. - */ - public void enqueueUpdateClippingMountState( - int reactTag, - @Nullable DrawCommand[] drawCommands, - SparseIntArray drawViewIndexMap, - float[] commandMaxBot, - float[] commandMinTop, - @Nullable AttachDetachListener[] listeners, - @Nullable NodeRegion[] nodeRegions, - float[] regionMaxBot, - float[] regionMinTop, - boolean willMountViews) { - enqueueUIOperation(new UpdateClippingMountState( - reactTag, - drawCommands, - drawViewIndexMap, - commandMaxBot, - commandMinTop, - listeners, - nodeRegions, - regionMaxBot, - regionMinTop, - willMountViews)); - } - - public void enqueueUpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) { - enqueueUIOperation(new UpdateViewGroup(reactTag, viewsToAdd, viewsToDetach)); - } - - /** - * Creates a new UIOperation that will update View bounds for a View defined by reactTag. - */ - public UpdateViewBounds createUpdateViewBounds( - int reactTag, - int left, - int top, - int right, - int bottom) { - return new UpdateViewBounds(reactTag, left, top, right, bottom); - } - - public ViewManagerCommand createViewManagerCommand( - int reactTag, - int command, - @Nullable ReadableArray args) { - return new ViewManagerCommand(reactTag, command, args); - } - - /* package */ void enqueueFlatUIOperation(UIViewOperationQueue.UIOperation operation) { - enqueueUIOperation(operation); - } - - public void enqueueSetPadding( - int reactTag, - int paddingLeft, - int paddingTop, - int paddingRight, - int paddingBottom) { - enqueueUIOperation( - new SetPadding(reactTag, paddingLeft, paddingTop, paddingRight, paddingBottom)); - } - - public void enqueueDropViews( - ArrayList viewsToDrop, - ArrayList parentsOfViewsToDrop) { - enqueueUIOperation(new DropViews(viewsToDrop, parentsOfViewsToDrop)); - } - - public void enqueueMeasureVirtualView( - int reactTag, - float scaledX, - float scaledY, - float scaledWidth, - float scaledHeight, - boolean relativeToWindow, - Callback callback) { - enqueueUIOperation(new MeasureVirtualView( - reactTag, - scaledX, - scaledY, - scaledWidth, - scaledHeight, - relativeToWindow, - callback)); - } - - public void enqueueProcessLayoutRequests() { - enqueueUIOperation(mProcessLayoutRequests); - } - - public DetachAllChildrenFromViews enqueueDetachAllChildrenFromViews() { - DetachAllChildrenFromViews op = new DetachAllChildrenFromViews(); - enqueueUIOperation(op); - return op; - } - - @Override - public void enqueueFindTargetForTouch( - final int reactTag, - final float targetX, - final float targetY, - final Callback callback) { - enqueueUIOperation( - new FindTargetForTouchOperation(reactTag, targetX, targetY, callback)); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java deleted file mode 100644 index eb5f4cf15..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java +++ /dev/null @@ -1,1197 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.SoftAssertions; -import com.facebook.react.touch.OnInterceptTouchEventListener; -import com.facebook.react.touch.ReactHitSlopView; -import com.facebook.react.touch.ReactInterceptingViewGroup; -import com.facebook.react.uimanager.PointerEvents; -import com.facebook.react.uimanager.ReactClippingViewGroup; -import com.facebook.react.uimanager.ReactCompoundViewGroup; -import com.facebook.react.uimanager.ReactPointerEventsView; -import com.facebook.react.uimanager.UIManagerModule; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.views.image.ImageLoadEvent; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import javax.annotation.Nullable; - -/** - * A view that the {@link FlatShadowNode} hierarchy maps to. Can mount and draw native views as - * well as draw commands. We reuse some of Android's ViewGroup logic, but in Nodes we try to - * minimize the amount of shadow nodes that map to native children, so we have a lot of logic - * specific to draw commands. - * - * In a very simple case with no Android children, the FlatViewGroup will receive: - * - * flatViewGroup.mountDrawCommands(...); - * flatViewGroup.dispatchDraw(...); - * - * The draw commands are mounted, then draw iterates through and draws them one by one. - * - * In a simple case where there are native children: - * - * flatViewGroup.mountDrawCommands(...); - * flatViewGroup.detachAllViewsFromParent(...); - * flatViewGroup.mountViews(...); - * flatViewGroup.dispatchDraw(...); - * - * Draw commands are mounted, with a draw view command for each mounted view. As an optimization - * we then detach all views from the FlatViewGroup, then allow mountViews to selectively reattach - * and add views in order. We do this as adding a single view is a O(n) operation (On average you - * have to move all the views in the array to the right one position), as is dropping and re-adding - * all views (One pass to clear the array and one pass to re-attach detached children and add new - * children). - * - * FlatViewGroups also have arrays of node regions, which are little more than a rects that - * represents a touch target. Native views contain their own touch logic, but not all react tags - * map to native views. We use node regions to find touch targets among commands as well as nodes - * which map to native views. - * - * In the case of clipping, much of the underlying logic for is handled by - * {@link DrawCommandManager}. This lets us separate logic, while also allowing us to save on - * memory for data structures only used in clipping. In a case of a clipping FlatViewGroup which - * is scrolling: - * - * flatViewGroup.setRemoveClippedSubviews(true); - * flatViewGroup.mountClippingDrawCommands(...); - * flatViewGroup.detachAllViewsFromParent(...); - * flatViewGroup.mountViews(...); - * flatViewGroup.updateClippingRect(...); - * flatViewGroup.dispatchDraw(...); - * flatViewGroup.updateClippingRect(...); - * flatViewGroup.dispatchDraw(...); - * flatViewGroup.updateClippingRect(...); - * flatViewGroup.dispatchDraw(...); - * - * Setting remove clipped subviews creates a {@link DrawCommandManager} to handle clipping, which - * allows the rest of the methods to simply call in to draw command manager to handle the clipping - * logic. - */ -/* package */ final class FlatViewGroup extends ViewGroup - implements ReactInterceptingViewGroup, ReactClippingViewGroup, - ReactCompoundViewGroup, ReactHitSlopView, ReactPointerEventsView, FlatMeasuredViewGroup { - /** - * Helper class that allows our AttachDetachListeners to invalidate the hosting View. When a - * listener gets an attach it is passed an invalidate callback for the FlatViewGroup it is being - * attached to. - */ - static final class InvalidateCallback extends WeakReference { - - private InvalidateCallback(FlatViewGroup view) { - super(view); - } - - /** - * Propagates invalidate() call up to the hosting View (if it's still alive) - */ - public void invalidate() { - FlatViewGroup view = get(); - if (view != null) { - view.invalidate(); - } - } - - /** - * Propagates image load events to javascript if the hosting view is still alive. - * - * @param reactTag The view id. - * @param imageLoadEvent The event type. - */ - public void dispatchImageLoadEvent(int reactTag, int imageLoadEvent) { - FlatViewGroup view = get(); - if (view == null) { - return; - } - - ReactContext reactContext = ((ReactContext) view.getContext()); - reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( - new ImageLoadEvent(reactTag, imageLoadEvent)); - } - } - - // Draws the name of the draw commands at the bottom right corner of it's bounds. - private static final boolean DEBUG_DRAW_TEXT = false; - // Draws colored rectangles over known performance issues. - /* package */ static final boolean DEBUG_HIGHLIGHT_PERFORMANCE_ISSUES = false; - // Force layout bounds drawing. This can also be enabled by turning on layout bounds in Android. - private static final boolean DEBUG_DRAW = DEBUG_DRAW_TEXT || DEBUG_HIGHLIGHT_PERFORMANCE_ISSUES; - // Resources for debug drawing. - private boolean mAndroidDebugDraw; - private static Paint sDebugTextPaint; - private static Paint sDebugTextBackgroundPaint; - private static Paint sDebugRectPaint; - private static Paint sDebugCornerPaint; - private static Rect sDebugRect; - - private static final ArrayList LAYOUT_REQUESTS = new ArrayList<>(); - private static final Rect VIEW_BOUNDS = new Rect(); - - // An invalidate callback singleton for this FlatViewGroup. - private @Nullable InvalidateCallback mInvalidateCallback; - private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY; - private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY; - private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY; - - // The index of the next native child to draw. This is used in dispatchDraw to check that we are - // actually drawing all of our attached children, then is reset to 0. - private int mDrawChildIndex = 0; - private boolean mIsAttached = false; - private boolean mIsLayoutRequested = false; - private boolean mNeedsOffscreenAlphaCompositing = false; - private Drawable mHotspot; - private PointerEvents mPointerEvents = PointerEvents.AUTO; - private long mLastTouchDownTime; - private @Nullable OnInterceptTouchEventListener mOnInterceptTouchEventListener; - - private static final SparseArray EMPTY_DETACHED_VIEWS = new SparseArray<>(0); - // Provides clipping, drawing and node region finding logic if subview clipping is enabled. - private @Nullable DrawCommandManager mDrawCommandManager; - - private @Nullable Rect mHitSlopRect; - - /* package */ FlatViewGroup(Context context) { - super(context); - setClipChildren(false); - } - - @Override - protected void detachAllViewsFromParent() { - super.detachAllViewsFromParent(); - } - - @Override - @SuppressLint("MissingSuperCall") - public void requestLayout() { - if (mIsLayoutRequested) { - return; - } - - mIsLayoutRequested = true; - LAYOUT_REQUESTS.add(this); - } - - @Override - public int reactTagForTouch(float touchX, float touchY) { - /* - * Make sure we don't find any children if the pointer events are set to BOX_ONLY. - * There is no need to special-case any other modes, because if PointerEvents are set to: - * a) PointerEvents.AUTO - all children are included, nothing to exclude - * b) PointerEvents.NONE - this method will NOT be executed, because the View will be filtered - * out by TouchTargetHelper. - * c) PointerEvents.BOX_NONE - TouchTargetHelper will make sure that {@link #reactTagForTouch()} - * doesn't return getId(). - */ - SoftAssertions.assertCondition( - mPointerEvents != PointerEvents.NONE, - "TouchTargetHelper should not allow calling this method when pointer events are NONE"); - - if (mPointerEvents != PointerEvents.BOX_ONLY) { - NodeRegion nodeRegion = virtualNodeRegionWithinBounds(touchX, touchY); - if (nodeRegion != null) { - return nodeRegion.getReactTag(touchX, touchY); - } - } - - // no children found - return getId(); - } - - @Override - public boolean interceptsTouchEvent(float touchX, float touchY) { - NodeRegion nodeRegion = anyNodeRegionWithinBounds(touchX, touchY); - return nodeRegion != null && nodeRegion.mIsVirtual; - } - - /** - * Secretly Overrides the hidden ViewGroup.onDebugDraw method. This is hidden in the Android - * ViewGroup, but still gets called in super.dispatchDraw. Overriding here allows us to draw - * layout bounds for Nodes when android is drawing layout bounds. - */ - protected void onDebugDraw(Canvas canvas) { - // Android is drawing layout bounds, so we should as well. - mAndroidDebugDraw = true; - } - - /** - * Draw FlatViewGroup on a canvas. Also checks that all children are drawn, as a draw view calls - * back to the FlatViewGroup to draw each child. - * - * @param canvas The canvas to draw on. - */ - @Override - public void dispatchDraw(Canvas canvas) { - mAndroidDebugDraw = false; - super.dispatchDraw(canvas); - - if (mDrawCommandManager != null) { - mDrawCommandManager.draw(canvas); - } else { - for (DrawCommand drawCommand : mDrawCommands) { - drawCommand.draw(this, canvas); - } - } - - if (mDrawChildIndex != getChildCount()) { - throw new RuntimeException( - "Did not draw all children: " + mDrawChildIndex + " / " + getChildCount()); - } - mDrawChildIndex = 0; - - if (DEBUG_DRAW || mAndroidDebugDraw) { - initDebugDrawResources(); - debugDraw(canvas); - } - - if (mHotspot != null) { - mHotspot.draw(canvas); - } - } - - /** - * Draws layout bounds for debug. Optionally can draw the name of the DrawCommand so you can - * distinguish commands easier. - * - * @param canvas The canvas to draw on. - */ - private void debugDraw(Canvas canvas) { - if (mDrawCommandManager != null) { - mDrawCommandManager.debugDraw(canvas); - } else { - for (DrawCommand drawCommand : mDrawCommands) { - drawCommand.debugDraw(this, canvas); - } - } - mDrawChildIndex = 0; - } - - /** - * This override exists to suppress the default drawing behaviour of the ViewGroup. dispatchDraw - * calls super.dispatchDraw, which lets Android perform some of our child management logic. - * super.dispatchDraw then calls our drawChild, which is suppressed. - * - * dispatchDraw within the FlatViewGroup then calls super.drawChild, which actually draws the - * child. - * - * // Pseudocode example. - * Class FlatViewGroup { - * void dispatchDraw() { - * super.dispatchDraw(); // Eventually calls our drawChild, which is a no op. - * super.drawChild(); // Calls the actual drawChild. - * } - * - * boolean drawChild(...) { - * // No op. - * } - * } - * - * Class ViewGroup { - * void dispatchDraw() { - * drawChild(); // No op. - * } - * - * boolean drawChild(...) { - * getChildAt(...).draw(); - * } - * } - * - * @return false, as we are suppressing drawChild. - */ - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - // suppress - // no drawing -> no invalidate -> return false - return false; - } - - /** - * Draw layout bounds for the next child. - * - * @param canvas The canvas to draw on. - */ - /* package */ void debugDrawNextChild(Canvas canvas) { - View child = getChildAt(mDrawChildIndex); - // Draw FlatViewGroups a different color than regular child views. - int color = child instanceof FlatViewGroup ? Color.DKGRAY : Color.RED; - debugDrawRect( - canvas, - color, - child.getLeft(), - child.getTop(), - child.getRight(), - child.getBottom()); - ++mDrawChildIndex; - } - - // Used in debug drawing. - /* package */ int dipsToPixels(int dips) { - float scale = getResources().getDisplayMetrics().density; - return (int) (dips * scale + 0.5f); - } - - // Used in debug drawing. - private static void fillRect(Canvas canvas, Paint paint, float x1, float y1, float x2, float y2) { - if (x1 != x2 && y1 != y2) { - if (x1 > x2) { - float tmp = x1; x1 = x2; x2 = tmp; - } - if (y1 > y2) { - float tmp = y1; y1 = y2; y2 = tmp; - } - canvas.drawRect(x1, y1, x2, y2, paint); - } - } - - // Used in debug drawing. - private static int sign(float x) { - return (x >= 0) ? 1 : -1; - } - - // Used in debug drawing. - private static void drawCorner( - Canvas c, - Paint paint, - float x1, - float y1, - float dx, - float dy, - float lw) { - fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy)); - fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy); - } - - // Used in debug drawing. - private static void drawRectCorners( - Canvas canvas, - float x1, - float y1, - float x2, - float y2, - Paint paint, - int lineLength, - int lineWidth) { - drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth); - drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth); - drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth); - drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth); - } - - /** - * Makes sure that we only initialize one instance of each of our layout bounds drawing - * resources. - */ - private void initDebugDrawResources() { - if (sDebugTextPaint == null) { - sDebugTextPaint = new Paint(); - sDebugTextPaint.setTextAlign(Paint.Align.RIGHT); - sDebugTextPaint.setTextSize(dipsToPixels(9)); - sDebugTextPaint.setTypeface(Typeface.MONOSPACE); - sDebugTextPaint.setAntiAlias(true); - sDebugTextPaint.setColor(Color.RED); - } - if (sDebugTextBackgroundPaint == null) { - sDebugTextBackgroundPaint = new Paint(); - sDebugTextBackgroundPaint.setColor(Color.WHITE); - sDebugTextBackgroundPaint.setAlpha(200); - sDebugTextBackgroundPaint.setStyle(Paint.Style.FILL); - } - if (sDebugRectPaint == null) { - sDebugRectPaint = new Paint(); - sDebugRectPaint.setAlpha(100); - sDebugRectPaint.setStyle(Paint.Style.STROKE); - } - if (sDebugCornerPaint == null) { - sDebugCornerPaint = new Paint(); - sDebugCornerPaint.setAlpha(200); - sDebugCornerPaint.setColor(Color.rgb(63, 127, 255)); - sDebugCornerPaint.setStyle(Paint.Style.FILL); - } - if (sDebugRect == null) { - sDebugRect = new Rect(); - } - } - - /** - * Used in drawing layout bounds, draws a layout bounds rectangle similar to the Android default - * implementation, with a specifiable border color. - * - * @param canvas The canvas to draw on. - * @param color The border color of the layout bounds. - * @param left Left bound of the rectangle. - * @param top Top bound of the rectangle. - * @param right Right bound of the rectangle. - * @param bottom Bottom bound of the rectangle. - */ - private void debugDrawRect( - Canvas canvas, - int color, - float left, - float top, - float right, - float bottom) { - debugDrawNamedRect(canvas, color, "", left, top, right, bottom); - } - - /** - * Used in drawing layout bounds, draws a layout bounds rectangle similar to the Android default - * implementation, with a specifiable border color. Also draws a name text in the bottom right - * corner of the rectangle if DEBUG_DRAW_TEXT is set. - * - * @param canvas The canvas to draw on. - * @param color The border color of the layout bounds. - * @param name Name to be drawn on top of the rectangle if DEBUG_DRAW_TEXT is set. - * @param left Left bound of the rectangle. - * @param top Top bound of the rectangle. - * @param right Right bound of the rectangle. - * @param bottom Bottom bound of the rectangle. - */ - /* package */ void debugDrawNamedRect( - Canvas canvas, - int color, - String name, - float left, - float top, - float right, - float bottom) { - if (DEBUG_DRAW_TEXT && !name.isEmpty()) { - sDebugTextPaint.getTextBounds(name, 0, name.length(), sDebugRect); - int inset = dipsToPixels(2); - float textRight = right - inset - 1; - float textBottom = bottom - inset - 1; - canvas.drawRect( - textRight - sDebugRect.right - inset, - textBottom + sDebugRect.top - inset, - textRight + inset, - textBottom + inset, - sDebugTextBackgroundPaint); - canvas.drawText(name, textRight, textBottom, sDebugTextPaint); - } - // Retain the alpha component. - sDebugRectPaint.setColor((sDebugRectPaint.getColor() & 0xFF000000) | (color & 0x00FFFFFF)); - sDebugRectPaint.setAlpha(100); - canvas.drawRect( - left, - top, - right - 1, - bottom - 1, - sDebugRectPaint); - drawRectCorners( - canvas, - left, - top, - right, - bottom, - sDebugCornerPaint, - dipsToPixels(8), - dipsToPixels(1)); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - // nothing to do here - } - - @Override - @SuppressLint("MissingSuperCall") - protected boolean verifyDrawable(Drawable who) { - return true; - } - - @Override - protected void onAttachedToWindow() { - if (mIsAttached) { - // This is possible, unfortunately. - return; - } - - mIsAttached = true; - - super.onAttachedToWindow(); - dispatchOnAttached(mAttachDetachListeners); - - // This is a no op if we aren't clipping, so let updateClippingRect handle the check for us. - updateClippingRect(); - } - - @Override - protected void onDetachedFromWindow() { - if (!mIsAttached) { - throw new RuntimeException("Double detach"); - } - - mIsAttached = false; - - super.onDetachedFromWindow(); - dispatchOnDetached(mAttachDetachListeners); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - if (mHotspot != null) { - mHotspot.setBounds(0, 0, w, h); - invalidate(); - } - - // This is a no op if we aren't clipping, so let updateClippingRect handle the check for us. - updateClippingRect(); - } - - @Override - public void dispatchDrawableHotspotChanged(float x, float y) { - if (mHotspot != null) { - mHotspot.setHotspot(x, y); - invalidate(); - } - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - - if (mHotspot != null && mHotspot.isStateful()) { - mHotspot.setState(getDrawableState()); - } - } - - @Override - public void jumpDrawablesToCurrentState() { - super.jumpDrawablesToCurrentState(); - if (mHotspot != null) { - mHotspot.jumpToCurrentState(); - } - } - - @Override - public void invalidate() { - // By default, invalidate() only invalidates the View's boundaries, which works great in most - // cases but may fail with overflow: visible (i.e. View clipping disabled) when View width or - // height is 0. This is because invalidate() has an optimization where it will not invalidate - // empty Views at all. A quick fix is to invalidate a slightly larger region to make sure we - // never hit that optimization. - // - // Another thing to note is that this may not work correctly with software rendering because - // in software, Android tracks dirty regions to redraw. We would need to collect information - // about all children boundaries (recursively) to track dirty region precisely. - invalidate(0, 0, getWidth() + 1, getHeight() + 1); - } - - /** - * We override this to allow developers to determine whether they need offscreen alpha compositing - * or not. See the documentation of needsOffscreenAlphaCompositing in View.js. - */ - @Override - public boolean hasOverlappingRendering() { - return mNeedsOffscreenAlphaCompositing; - } - - @Override - public void setOnInterceptTouchEventListener(OnInterceptTouchEventListener listener) { - mOnInterceptTouchEventListener = listener; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - final long downTime = ev.getDownTime(); - if (downTime != mLastTouchDownTime) { - mLastTouchDownTime = downTime; - if (interceptsTouchEvent(ev.getX(), ev.getY())) { - return true; - } - } - - if (mOnInterceptTouchEventListener != null && - mOnInterceptTouchEventListener.onInterceptTouchEvent(this, ev)) { - return true; - } - // We intercept the touch event if the children are not supposed to receive it. - if (mPointerEvents == PointerEvents.NONE || mPointerEvents == PointerEvents.BOX_ONLY) { - return true; - } - return super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - // We do not accept the touch event if this view is not supposed to receive it. - if (mPointerEvents == PointerEvents.NONE) { - return false; - } - - if (mPointerEvents == PointerEvents.BOX_NONE) { - // We cannot always return false here because some child nodes could be flatten into this View - NodeRegion nodeRegion = virtualNodeRegionWithinBounds(ev.getX(), ev.getY()); - if (nodeRegion == null) { - // no child to handle this touch event, bailing out. - return false; - } - } - - // The root view always assumes any view that was tapped wants the touch - // and sends the event to JS as such. - // We don't need to do bubbling in native (it's already happening in JS). - // For an explanation of bubbling and capturing, see - // http://javascript.info/tutorial/bubbling-and-capturing#capturing - return true; - } - - @Override - public PointerEvents getPointerEvents() { - return mPointerEvents; - } - - /*package*/ void setPointerEvents(PointerEvents pointerEvents) { - mPointerEvents = pointerEvents; - } - - /** - * See the documentation of needsOffscreenAlphaCompositing in View.js. - */ - /* package */ void setNeedsOffscreenAlphaCompositing(boolean needsOffscreenAlphaCompositing) { - mNeedsOffscreenAlphaCompositing = needsOffscreenAlphaCompositing; - } - - /* package */ void setHotspot(Drawable hotspot) { - if (mHotspot != null) { - mHotspot.setCallback(null); - unscheduleDrawable(mHotspot); - } - - if (hotspot != null) { - hotspot.setCallback(this); - if (hotspot.isStateful()) { - hotspot.setState(getDrawableState()); - } - } - - mHotspot = hotspot; - invalidate(); - } - - /** - * Draws the next child of the FlatViewGroup. Each draw view calls FlatViewGroup.drawNextChild, - * which keeps track of the current child index to draw. - * - * @param canvas The canvas to draw on. - */ - /* package */ void drawNextChild(Canvas canvas) { - View child = getChildAt(mDrawChildIndex); - if (child instanceof FlatViewGroup) { - super.drawChild(canvas, child, getDrawingTime()); - } else { - // Make sure non-React Views clip properly. - canvas.save(Canvas.CLIP_SAVE_FLAG); - child.getHitRect(VIEW_BOUNDS); - canvas.clipRect(VIEW_BOUNDS); - super.drawChild(canvas, child, getDrawingTime()); - canvas.restore(); - } - - ++mDrawChildIndex; - } - - /** - * Mount a list of draw commands to this FlatViewGroup. Draw commands sometimes map to a view, - * as in the case of {@link DrawView}, and sometimes to a simple canvas operation. We only - * receive a call to mount draw commands when our commands have changed, so we always invalidate. - * - * A call to mount draw commands will only be followed by a call to mount views if the draw view - * commands within the draw command array have changed since last mount. - * - * @param drawCommands The draw commands to mount. - */ - /* package */ void mountDrawCommands(DrawCommand[] drawCommands) { - mDrawCommands = drawCommands; - invalidate(); - } - - /** - * Mount a list of draw commands to this FlatViewGroup, which is clipping subviews. Clipping - * logic is handled by a {@link DrawCommandManager}, which provides a better explanation of - * these arguments and logic. - * - * A call to mount draw commands will only be followed by a call to mount views if the draw view - * commands within the draw command array have changed since last mount, which is indicated here - * by willMountViews. - * - * @param drawCommands The draw commands to mount. - * @param drawViewIndexMap See {@link DrawCommandManager}. - * @param maxBottom See {@link DrawCommandManager}. - * @param minTop See {@link DrawCommandManager}. - * @param willMountViews True if we will also receive a mountViews call. If we are going to - * receive a call to mount views, that will take care of updating the commands that are - * currently onscreen, otherwise we need to update the onscreen commands. - */ - /* package */ void mountClippingDrawCommands( - DrawCommand[] drawCommands, - SparseIntArray drawViewIndexMap, - float[] maxBottom, - float[] minTop, - boolean willMountViews) { - Assertions.assertNotNull(mDrawCommandManager).mountDrawCommands( - drawCommands, - drawViewIndexMap, - maxBottom, - minTop, - willMountViews); - invalidate(); - } - - /** - * Handle a subview being dropped - * In most cases, we are informed about a subview being dropped via mountViews, but in some - * cases (such as when both the child and parent get explicit removes in the same frame), - * we may not find out, so this is called when the child is dropped so the parent can clean up - * strong references to the child. - * - * @param view the view being dropped - */ - void onViewDropped(View view) { - if (mDrawCommandManager != null) { - // for now, we only care about clearing clipped subview references - mDrawCommandManager.onClippedViewDropped(view); - } - } - - /** - * Return the NodeRegion which matches a reactTag, or EMPTY if none match. - * - * @param reactTag The reactTag to look for - * @return The matching NodeRegion, or NodeRegion.EMPTY if none match. - */ - /* package */ NodeRegion getNodeRegionForTag(int reactTag) { - for (NodeRegion region : mNodeRegions) { - if (region.matchesTag(reactTag)) { - return region; - } - } - return NodeRegion.EMPTY; - } - - /** - * Return a list of FlatViewGroups that are detached (due to being clipped) but that we have a - * strong reference to. This is used by the FlatNativeViewHierarchyManager to explicitly clean up - * those views when removing this parent. - * - * @return A Collection of Views to clean up. - */ - /* package */ SparseArray getDetachedViews() { - if (mDrawCommandManager == null) { - return EMPTY_DETACHED_VIEWS; - } - return mDrawCommandManager.getDetachedViews(); - } - - /** - * Remove the detached view from the parent - * This is used in the DrawCommandManagers and during cleanup to trigger onDetachedFromWindow on - * any views that were in a temporary detached state due to them being clipped. This is called - * for cleanup of said views by FlatNativeViewHierarchyManager. - * - * @param view the detached View to remove - */ - void removeDetachedView(View view) { - removeDetachedView(view, false); - } - - @Override - public void removeAllViewsInLayout() { - // whenever we want to remove all views in a layout, we also want to remove all the - // DrawCommands, otherwise, we can have a mismatch between the DrawView DrawCommands - // and the Views to draw (note that because removeAllViewsInLayout doesn't call invalidate, - // we don't actually need to modify mDrawCommands, but we do it just in case). - mDrawCommands = DrawCommand.EMPTY_ARRAY; - super.removeAllViewsInLayout(); - } - - /** - * Mounts attach detach listeners to a FlatViewGroup. The Nodes spec states that children and - * commands deal gracefully with multiple attaches and detaches, and as long as: - * - * attachCount - detachCount > 0 - * - * Then children still consider themselves as attached. - * - * @param listeners The listeners to mount. - */ - /* package */ void mountAttachDetachListeners(AttachDetachListener[] listeners) { - if (mIsAttached) { - // Ordering of the following 2 statements is very important. While logically it makes sense to - // detach old listeners first, and only then attach new listeners, this is not very efficient, - // because a listener can be in both lists. In this case, it will be detached first and then - // re-attached immediately. This is undesirable for a couple of reasons: - // 1) performance. Detaching is slow because it may cancel an ongoing network request - // 2) it may cause flicker: an image that was already loaded may get unloaded. - // - // For this reason, we are attaching new listeners first. What this means is that listeners - // that are in both lists need to gracefully handle a secondary attach and detach events, - // (i.e. onAttach() being called when already attached, followed by a detach that should be - // ignored) turning them into no-ops. This will result in no performance loss and no flicker, - // because ongoing network requests don't get cancelled. - dispatchOnAttached(listeners); - dispatchOnDetached(mAttachDetachListeners); - } - mAttachDetachListeners = listeners; - } - - /** - * Mount node regions to a FlatViewGroup. A node region is a touch target for a react tag. As - * not all react tags map to a view, we use node regions to determine whether a non-native region - * should receive a touch. - * - * @param nodeRegions The node regions to mount. - */ - /* package */ void mountNodeRegions(NodeRegion[] nodeRegions) { - mNodeRegions = nodeRegions; - } - - /** - * Mount node regions in clipping. See {@link DrawCommandManager} for more complete - * documentation. - * - * @param nodeRegions The node regions to mount. - * @param maxBottom See {@link DrawCommandManager}. - * @param minTop See {@link DrawCommandManager}. - */ - /* package */ void mountClippingNodeRegions( - NodeRegion[] nodeRegions, - float[] maxBottom, - float[] minTop) { - mNodeRegions = nodeRegions; - Assertions.assertNotNull(mDrawCommandManager).mountNodeRegions(nodeRegions, maxBottom, minTop); - } - - /** - * Mount a list of views to add, and dismount a list of views to detach. Ids will not appear in - * both lists, aka: - * Set(viewsToAdd + viewsToDetach).size() == viewsToAdd.length + viewsToDetach.length - * - * Every time we get any change in the views in a FlatViewGroup, we detach all views first, then - * reattach / remove them as needed. viewsToAdd is odd in that the ids also specify whether - * the view is new to us, or if we were already the parent. If it is new to us, then the id has - * a positive value, otherwise we are already the parent, but it was previously detached, since - * we detach everything when anything changes. - * - * The reason we detach everything is that a single detach is on the order of O(n), as in the - * average case we have to move half of the views one position to the right, and a single add is - * the same. Removing all views is also on the order of O(n), as you delete everything backward - * from the end, while adding a new set of views is also on the order of O(n), as you just add - * them all back in order. ArrayLists are weird. - * - * @param viewResolver Resolves the views from their id. - * @param viewsToAdd id of views to add if they weren't just attached to us, or -id if they are - * just being reattached. - * @param viewsToDetach id of views that we don't own anymore. They either moved to a new parent, - * or are being removed entirely. - */ - /* package */ void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) { - if (mDrawCommandManager != null) { - mDrawCommandManager.mountViews(viewResolver, viewsToAdd, viewsToDetach); - } else { - for (int viewToAdd : viewsToAdd) { - if (viewToAdd > 0) { - View view = viewResolver.getView(viewToAdd); - ensureViewHasNoParent(view); - addViewInLayout(view); - } else { - View view = viewResolver.getView(-viewToAdd); - ensureViewHasNoParent(view); - // We aren't clipping, so attach all the things, clipping is handled by the draw command - // manager, if we have one. - attachViewToParent(view); - } - } - - for (int viewToDetach : viewsToDetach) { - View view = viewResolver.getView(viewToDetach); - if (view.getParent() != null) { - throw new RuntimeException("Trying to remove view not owned by FlatViewGroup"); - } else { - removeDetachedView(view, false); - } - } - } - - invalidate(); - } - - /** - * Exposes the protected addViewInLayout call for the {@link DrawCommandManager}. - * - * @param view The view to add. - */ - /* package */ void addViewInLayout(View view) { - addViewInLayout(view, -1, ensureLayoutParams(view.getLayoutParams()), true); - } - - /** - * Exposes the protected addViewInLayout call for the {@link DrawCommandManager}. - * - * @param view The view to add. - * @param index The index position at which to add this child. - */ - /* package */ void addViewInLayout(View view, int index) { - addViewInLayout(view, index, ensureLayoutParams(view.getLayoutParams()), true); - } - - /** - * Exposes the protected attachViewToParent call for the {@link DrawCommandManager}. - * - * @param view The view to attach. - */ - /* package */ void attachViewToParent(View view) { - attachViewToParent(view, -1, ensureLayoutParams(view.getLayoutParams())); - } - - /** - * Exposes the protected attachViewToParent call for the {@link DrawCommandManager}. - * - * @param view The view to attach. - * @param index The index position at which to attach this child. - */ - /* package */ void attachViewToParent(View view, int index) { - attachViewToParent(view, index, ensureLayoutParams(view.getLayoutParams())); - } - - private void processLayoutRequest() { - mIsLayoutRequested = false; - for (int i = 0, childCount = getChildCount(); i != childCount; ++i) { - View child = getChildAt(i); - if (!child.isLayoutRequested()) { - continue; - } - - child.measure( - MeasureSpec.makeMeasureSpec(child.getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(child.getHeight(), MeasureSpec.EXACTLY)); - child.layout(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); - } - } - - /** - * Called after the view hierarchy is updated in {@link StateBuilder}, to process all the - * FlatViewGroups that have requested layout. - */ - /* package */ static void processLayoutRequests() { - for (int i = 0, numLayoutRequests = LAYOUT_REQUESTS.size(); i != numLayoutRequests; ++i) { - FlatViewGroup flatViewGroup = LAYOUT_REQUESTS.get(i); - flatViewGroup.processLayoutRequest(); - } - LAYOUT_REQUESTS.clear(); - } - - // Helper method for measure functionality provided by MeasuredViewGroup. - @Override - public Rect measureWithCommands() { - int childCount = getChildCount(); - if (childCount == 0 && mDrawCommands.length == 0) { - return new Rect(0, 0, 0, 0); - } - int left = Integer.MAX_VALUE; - int top = Integer.MAX_VALUE; - int right = Integer.MIN_VALUE; - int bottom = Integer.MIN_VALUE; - for (int i = 0; i < childCount; i++) { - // This is technically a dupe, since the DrawView has its bounds, but leaving in to handle if - // the View is animating or rebelling against the DrawView bounds for some reason. - View child = getChildAt(i); - left = Math.min(left, child.getLeft()); - top = Math.min(top, child.getTop()); - right = Math.max(right, child.getRight()); - bottom = Math.max(bottom, child.getBottom()); - } - - for (DrawCommand mDrawCommand : mDrawCommands) { - if (!(mDrawCommand instanceof AbstractDrawCommand)) { - continue; - } - AbstractDrawCommand drawCommand = (AbstractDrawCommand) mDrawCommand; - left = Math.min(left, Math.round(drawCommand.getLeft())); - top = Math.min(top, Math.round(drawCommand.getTop())); - right = Math.max(right, Math.round(drawCommand.getRight())); - bottom = Math.max(bottom, Math.round(drawCommand.getBottom())); - } - return new Rect(left, top, right, bottom); - } - - /** - * Searches for a virtual node region matching the specified x and y touch. Virtual in this case - * means simply that the node region represents a command, rather than a native view. - * - * @param touchX The touch x coordinate. - * @param touchY The touch y coordinate. - * @return A virtual node region matching the specified touch, or null if no regions match. - */ - private @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY) { - if (mDrawCommandManager != null) { - return mDrawCommandManager.virtualNodeRegionWithinBounds(touchX, touchY); - } - for (int i = mNodeRegions.length - 1; i >= 0; --i) { - NodeRegion nodeRegion = mNodeRegions[i]; - if (!nodeRegion.mIsVirtual) { - // only interested in virtual nodes - continue; - } - if (nodeRegion.withinBounds(touchX, touchY)) { - return nodeRegion; - } - } - - return null; - } - - /** - * Searches for a node region matching the specified x and y touch. Will search regions which - * representing both commands and native views. - * - * @param touchX The touch x coordinate. - * @param touchY The touch y coordinate. - * @return A node region matching the specified touch, or null if no regions match. - */ - private @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY) { - if (mDrawCommandManager != null) { - return mDrawCommandManager.anyNodeRegionWithinBounds(touchX, touchY); - } - for (int i = mNodeRegions.length - 1; i >= 0; --i) { - NodeRegion nodeRegion = mNodeRegions[i]; - if (nodeRegion.withinBounds(touchX, touchY)) { - return nodeRegion; - } - } - - return null; - } - - private static void ensureViewHasNoParent(View view) { - ViewParent oldParent = view.getParent(); - if (oldParent != null) { - throw new RuntimeException( - "Cannot add view " + view + " to FlatViewGroup while it has a parent " + oldParent); - } - } - - /** - * Propagate attach to a list of listeners, passing a callback by which they can invalidate. - * - * @param listeners List of listeners to attach. - */ - private void dispatchOnAttached(AttachDetachListener[] listeners) { - int numListeners = listeners.length; - if (numListeners == 0) { - return; - } - - InvalidateCallback callback = getInvalidateCallback(); - for (AttachDetachListener listener : listeners) { - listener.onAttached(callback); - } - } - - /** - * Get an invalidate callback singleton for this view instance. - * - * @return Invalidate callback singleton. - */ - private InvalidateCallback getInvalidateCallback() { - if (mInvalidateCallback == null) { - mInvalidateCallback = new InvalidateCallback(this); - } - return mInvalidateCallback; - } - - /** - * Propagate detach to a list of listeners. - * - * @param listeners List of listeners to detach. - */ - private static void dispatchOnDetached(AttachDetachListener[] listeners) { - for (AttachDetachListener listener : listeners) { - listener.onDetached(); - } - } - - private ViewGroup.LayoutParams ensureLayoutParams(ViewGroup.LayoutParams lp) { - if (checkLayoutParams(lp)) { - return lp; - } - return generateDefaultLayoutParams(); - } - - @Override - public void updateClippingRect() { - if (mDrawCommandManager == null) { - // Don't update the clipping rect if we aren't clipping. - return; - } - if (mDrawCommandManager.updateClippingRect()) { - // Manager says something changed. - invalidate(); - } - } - - @Override - public void getClippingRect(Rect outClippingRect) { - if (mDrawCommandManager == null) { - // We could call outClippingRect.set(null) here, but throw in case the underlying React Native - // behaviour changes without us knowing. - throw new RuntimeException( - "Trying to get the clipping rect for a non-clipping FlatViewGroup"); - } - mDrawCommandManager.getClippingRect(outClippingRect); - } - - @Override - public void setRemoveClippedSubviews(boolean removeClippedSubviews) { - boolean currentlyClipping = getRemoveClippedSubviews(); - if (removeClippedSubviews == currentlyClipping) { - // We aren't changing state, so don't do anything. - return; - } - if (currentlyClipping) { - // Trying to go from a clipping to a non-clipping state, not currently supported by Nodes. - // If this is an issue, let us know, but currently there does not seem to be a good case for - // supporting this. - throw new RuntimeException( - "Trying to transition FlatViewGroup from clipping to non-clipping state"); - } - mDrawCommandManager = DrawCommandManager.getVerticalClippingInstance(this, mDrawCommands); - mDrawCommands = DrawCommand.EMPTY_ARRAY; - // We don't need an invalidate here because this can't cause new views to come onscreen, since - // everything was unclipped. - } - - @Override - public boolean getRemoveClippedSubviews() { - return mDrawCommandManager != null; - } - - @Override - public @Nullable Rect getHitSlopRect() { - return mHitSlopRect; - } - - /* package */ void setHitSlopRect(@Nullable Rect rect) { - mHitSlopRect = rect; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewManager.java deleted file mode 100644 index 8969b6724..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewManager.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; - -abstract class FlatViewManager extends ViewGroupManager { - - @Override - protected FlatViewGroup createViewInstance(ThemedReactContext reactContext) { - return new FlatViewGroup(reactContext); - } - - @Override - public void setBackgroundColor(FlatViewGroup view, int backgroundColor) { - // suppress - } - - @Override - public void removeAllViews(FlatViewGroup parent) { - parent.removeAllViewsInLayout(); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FontStylingSpan.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FontStylingSpan.java deleted file mode 100644 index d7490984d..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FontStylingSpan.java +++ /dev/null @@ -1,207 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.text.TextPaint; -import android.text.style.MetricAffectingSpan; - -/* package */ final class FontStylingSpan extends MetricAffectingSpan { - - /* package */ static final FontStylingSpan INSTANCE = new FontStylingSpan( - Color.BLACK /* mTextColor */, - 0 /* mBackgroundColor */, - -1 /* mFontSize */, - -1 /* mFontStyle */, - -1 /* mFontWeight */, - false /* mHasUnderline */, - false /* mHasStrikeThrough */, - null /* mFontFamily */, - true /* mFrozen */); - - // text property - private double mTextColor; - private int mBackgroundColor; - private boolean mHasUnderline; - private boolean mHasStrikeThrough; - - // font properties - private int mFontSize; - private int mFontStyle; - private int mFontWeight; - private @Nullable String mFontFamily; - - // whether or not mutation is allowed. - private boolean mFrozen; - - FontStylingSpan() { - } - - private FontStylingSpan( - double textColor, - int backgroundColor, - int fontSize, - int fontStyle, - int fontWeight, - boolean hasUnderline, - boolean hasStrikeThrough, - @Nullable String fontFamily, - boolean frozen) { - mTextColor = textColor; - mBackgroundColor = backgroundColor; - mFontSize = fontSize; - mFontStyle = fontStyle; - mFontWeight = fontWeight; - mHasUnderline = hasUnderline; - mHasStrikeThrough = hasStrikeThrough; - mFontFamily = fontFamily; - mFrozen = frozen; - } - - /* package */ FontStylingSpan mutableCopy() { - return new FontStylingSpan( - mTextColor, - mBackgroundColor, - mFontSize, - mFontStyle, - mFontWeight, - mHasUnderline, - mHasStrikeThrough, - mFontFamily, - false); - } - - /* package */ boolean isFrozen() { - return mFrozen; - } - - /* package */ void freeze() { - mFrozen = true; - } - - /* package */ double getTextColor() { - return mTextColor; - } - - /* package */ void setTextColor(double textColor) { - mTextColor = textColor; - } - - /* package */ int getBackgroundColor() { - return mBackgroundColor; - } - - /* package */ void setBackgroundColor(int backgroundColor) { - mBackgroundColor = backgroundColor; - } - - /* package */ int getFontSize() { - return mFontSize; - } - - /* package */ void setFontSize(int fontSize) { - mFontSize = fontSize; - } - - /* package */ int getFontStyle() { - return mFontStyle; - } - - /* package */ void setFontStyle(int fontStyle) { - mFontStyle = fontStyle; - } - - /* package */ int getFontWeight() { - return mFontWeight; - } - - /* package */ void setFontWeight(int fontWeight) { - mFontWeight = fontWeight; - } - - /* package */ @Nullable String getFontFamily() { - return mFontFamily; - } - - /* package */ void setFontFamily(@Nullable String fontFamily) { - mFontFamily = fontFamily; - } - - /* package */ boolean hasUnderline() { - return mHasUnderline; - } - - /* package */ void setHasUnderline(boolean hasUnderline) { - mHasUnderline = hasUnderline; - } - - /* package */ boolean hasStrikeThrough() { - return mHasStrikeThrough; - } - - /* package */ void setHasStrikeThrough(boolean hasStrikeThrough) { - mHasStrikeThrough = hasStrikeThrough; - } - - @Override - public void updateDrawState(TextPaint ds) { - if (!Double.isNaN(mTextColor)) { - ds.setColor((int) mTextColor); - } - - ds.bgColor = mBackgroundColor; - ds.setUnderlineText(mHasUnderline); - ds.setStrikeThruText(mHasStrikeThrough); - updateMeasureState(ds); - } - - @Override - public void updateMeasureState(TextPaint ds) { - if (mFontSize != -1) { - ds.setTextSize(mFontSize); - } - - updateTypeface(ds); - } - - private int getNewStyle(int oldStyle) { - int newStyle = oldStyle; - if (mFontStyle != -1) { - newStyle = (newStyle & ~Typeface.ITALIC) | mFontStyle; - } - - if (mFontWeight != -1) { - newStyle = (newStyle & ~Typeface.BOLD) | mFontWeight; - } - - return newStyle; - } - - private void updateTypeface(TextPaint ds) { - Typeface typeface = ds.getTypeface(); - - int oldStyle = (typeface == null) ? 0 : typeface.getStyle(); - int newStyle = getNewStyle(oldStyle); - - if (oldStyle == newStyle && mFontFamily == null) { - // nothing to do - return; - } - - if (mFontFamily != null) { - typeface = TypefaceCache.getTypeface(mFontFamily, newStyle); - } else { - typeface = TypefaceCache.getTypeface(typeface, newStyle); - } - - ds.setTypeface(typeface); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/HitSlopNodeRegion.java b/ReactAndroid/src/main/java/com/facebook/react/flat/HitSlopNodeRegion.java deleted file mode 100644 index 09bb2f90c..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/HitSlopNodeRegion.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Rect; - -/** - * NodeRegion that has a hit slop. - */ -/* package */ final class HitSlopNodeRegion extends NodeRegion { - - private final Rect mHitSlop; - - HitSlopNodeRegion( - Rect hitSlop, - float left, - float top, - float right, - float bottom, - int tag, - boolean isVirtual) { - super(left, top, right, bottom, tag, isVirtual); - mHitSlop = hitSlop; - } - - @Override - /* package */ float getTouchableLeft() { - return getLeft() - mHitSlop.left; - } - - @Override - /* package */ float getTouchableTop() { - return getTop() - mHitSlop.top; - } - - @Override - /* package */ float getTouchableRight() { - return getRight() + mHitSlop.right; - } - - @Override - /* package */ float getTouchableBottom() { - return getBottom() + mHitSlop.bottom; - } - - @Override - /* package */ boolean withinBounds(float touchX, float touchY) { - return getTouchableLeft() <= touchX && touchX < getTouchableRight() && - getTouchableTop() <= touchY && touchY < getTouchableBottom(); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/HorizontalDrawCommandManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/HorizontalDrawCommandManager.java deleted file mode 100644 index 9621c9b3e..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/HorizontalDrawCommandManager.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import java.util.Arrays; - -import android.util.SparseIntArray; - -/** - * {@link DrawCommandManager} with horizontal clipping (The view scrolls left and right). - */ -/* package */ final class HorizontalDrawCommandManager extends ClippingDrawCommandManager { - - /* package */ HorizontalDrawCommandManager( - FlatViewGroup flatViewGroup, - DrawCommand[] drawCommands) { - super(flatViewGroup, drawCommands); - } - - @Override - int commandStartIndex() { - int start = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.left); - // We don't care whether we matched or not, but positive indices are helpful. The binary search - // returns ~index in the case that it isn't a match, so reverse that here. - return start < 0 ? ~start : start; - } - - @Override - int commandStopIndex(int start) { - int stop = Arrays.binarySearch( - mCommandMinTop, - start, - mCommandMinTop.length, - mClippingRect.right); - // We don't care whether we matched or not, but positive indices are helpful. The binary search - // returns ~index in the case that it isn't a match, so reverse that here. - return stop < 0 ? ~stop : stop; - } - - @Override - int regionStopIndex(float touchX, float touchY) { - int stop = Arrays.binarySearch(mRegionMinTop, touchX + 0.0001f); - // We don't care whether we matched or not, but positive indices are helpful. The binary search - // returns ~index in the case that it isn't a match, so reverse that here. - return stop < 0 ? ~stop : stop; - } - - @Override - boolean regionAboveTouch(int index, float touchX, float touchY) { - return mRegionMaxBottom[index] < touchX; - } - - /** - * Populates the max and min arrays for a given set of node regions. - * - * This should never be called from the UI thread, as the reason it exists is to do work off the - * UI thread. - * - * @param regions The regions that will eventually be mounted. - * @param maxRight At each index i, the maximum right value of all regions at or below i. - * @param minLeft At each index i, the minimum left value of all regions at or below i. - */ - public static void fillMaxMinArrays(NodeRegion[] regions, float[] maxRight, float[] minLeft) { - float last = 0; - for (int i = 0; i < regions.length; i++) { - last = Math.max(last, regions[i].getTouchableRight()); - maxRight[i] = last; - } - for (int i = regions.length - 1; i >= 0; i--) { - last = Math.min(last, regions[i].getTouchableLeft()); - minLeft[i] = last; - } - } - - /** - * Populates the max and min arrays for a given set of draw commands. Also populates a mapping of - * react tags to their index position in the command array. - * - * This should never be called from the UI thread, as the reason it exists is to do work off the - * UI thread. - * - * @param commands The draw commands that will eventually be mounted. - * @param maxRight At each index i, the maximum right value of all draw commands at or below i. - * @param minLeft At each index i, the minimum left value of all draw commands at or below i. - * @param drawViewIndexMap Mapping of ids to index position within the draw command array. - */ - public static void fillMaxMinArrays( - DrawCommand[] commands, - float[] maxRight, - float[] minLeft, - SparseIntArray drawViewIndexMap) { - float last = 0; - // Loop through the DrawCommands, keeping track of the maximum we've seen if we only iterated - // through items up to this position. - for (int i = 0; i < commands.length; i++) { - if (commands[i] instanceof DrawView) { - DrawView drawView = (DrawView) commands[i]; - // These will generally be roughly sorted by id, so try to insert at the end if possible. - drawViewIndexMap.append(drawView.reactTag, i); - last = Math.max(last, drawView.mLogicalRight); - } else { - last = Math.max(last, commands[i].getRight()); - } - maxRight[i] = last; - } - // Intentionally leave last as it was, since it's at the maximum bottom position we've seen so - // far, we can use it again. - - // Loop through backwards, keeping track of the minimum we've seen at this position. - for (int i = commands.length - 1; i >= 0; i--) { - if (commands[i] instanceof DrawView) { - last = Math.min(last, ((DrawView) commands[i]).mLogicalLeft); - } else { - last = Math.min(last, commands[i].getLeft()); - } - minLeft[i] = last; - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/InlineImageSpanWithPipeline.java b/ReactAndroid/src/main/java/com/facebook/react/flat/InlineImageSpanWithPipeline.java deleted file mode 100644 index 9f7aca9a7..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/InlineImageSpanWithPipeline.java +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.text.style.ReplacementSpan; - -import com.facebook.imagepipeline.request.ImageRequest; -import com.facebook.infer.annotation.Assertions; - -/* package */ final class InlineImageSpanWithPipeline extends ReplacementSpan - implements AttachDetachListener, BitmapUpdateListener { - - private static final RectF TMP_RECT = new RectF(); - - private @Nullable PipelineRequestHelper mRequestHelper; - private @Nullable FlatViewGroup.InvalidateCallback mCallback; - private float mWidth; - private float mHeight; - private boolean mFrozen; - - /* package */ InlineImageSpanWithPipeline() { - this(null, Float.NaN, Float.NaN); - } - - private InlineImageSpanWithPipeline( - @Nullable PipelineRequestHelper requestHelper, - float width, - float height) { - mRequestHelper = requestHelper; - mWidth = width; - mHeight = height; - } - - /* package */ InlineImageSpanWithPipeline mutableCopy() { - return new InlineImageSpanWithPipeline(mRequestHelper, mWidth, mHeight); - } - - /* package */ boolean hasImageRequest() { - return mRequestHelper != null; - } - - /** - * Assigns a new image request to the DrawImage, or null to clear the image request. - */ - /* package */ void setImageRequest(@Nullable ImageRequest imageRequest) { - if (imageRequest == null) { - mRequestHelper = null; - } else { - mRequestHelper = new PipelineRequestHelper(imageRequest); - } - } - - /* package */ float getWidth() { - return mWidth; - } - - /* package */ void setWidth(float width) { - mWidth = width; - } - - /* package */ float getHeight() { - return mHeight; - } - - /* package */ void setHeight(float height) { - mHeight = height; - } - - /* package */ void freeze() { - mFrozen = true; - } - - /* package */ boolean isFrozen() { - return mFrozen; - } - - @Override - public void onSecondaryAttach(Bitmap bitmap) { - // We don't know if width or height changed, so invalidate just in case. - Assertions.assumeNotNull(mCallback).invalidate(); - } - - @Override - public void onBitmapReady(Bitmap bitmap) { - // Bitmap is now ready, draw it. - Assertions.assumeNotNull(mCallback).invalidate(); - } - - @Override - public void onImageLoadEvent(int imageLoadEvent) { - // ignore - } - - @Override - public void onAttached(FlatViewGroup.InvalidateCallback callback) { - mCallback = callback; - - if (mRequestHelper != null) { - mRequestHelper.attach(this); - } - } - - @Override - public void onDetached() { - if (mRequestHelper != null) { - mRequestHelper.detach(); - - if (mRequestHelper.isDetached()) { - // optional - mCallback = null; - } - } - } - - @Override - public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { - if (fm != null) { - fm.ascent = -Math.round(mHeight); - fm.descent = 0; - - fm.top = fm.ascent; - fm.bottom = 0; - } - - return Math.round(mWidth); - } - - @Override - public void draw( - Canvas canvas, - CharSequence text, - int start, - int end, - float x, - int top, - int y, - int bottom, - Paint paint) { - if (mRequestHelper == null) { - return; - } - - Bitmap bitmap = mRequestHelper.getBitmap(); - if (bitmap == null) { - return; - } - - float bottomFloat = (float) bottom - paint.getFontMetricsInt().descent; - TMP_RECT.set(x, bottomFloat - mHeight, x + mWidth, bottomFloat); - - canvas.drawBitmap(bitmap, null, TMP_RECT, paint); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/MoveProxy.java b/ReactAndroid/src/main/java/com/facebook/react/flat/MoveProxy.java deleted file mode 100644 index 6f786b80f..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/MoveProxy.java +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.ReactShadowNode; -import com.facebook.react.uimanager.ReactShadowNodeImpl; -import javax.annotation.Nullable; - -/** - * Helper class that sorts moveFrom/moveTo arrays in lockstep. - */ -/* package */ final class MoveProxy { - - private @Nullable ReadableArray mMoveTo; - private int mSize; - private int[] mMapping = new int[8]; - private ReactShadowNode[] mChildren = new ReactShadowNodeImpl[4]; - - /** - * Returns size of underlying moveTo/moveFrom arrays - */ - public int size() { - return mSize; - } - - /** - * Assigns ith child that we want to move if moveFrom was sorted. - */ - public void setChildMoveFrom(int moveFromIndex, ReactShadowNode node) { - mChildren[moveFromToIndex(moveFromIndex)] = node; - } - - /** - * Returns ith child that we want to move if moveTo was sorted. - */ - public ReactShadowNode getChildMoveTo(int moveToIndex) { - return mChildren[moveToToIndex(moveToIndex)]; - } - - /** - * Returns index of the ith child that we want to move if moveFrom was sorted - */ - public int getMoveFrom(int moveFromIndex) { - return moveFromToValue(moveFromIndex); - } - - /** - * Returns index of the ith child that we want to move to if moveTo was sorted - */ - public int getMoveTo(int moveToIndex) { - return moveToToValue(moveToIndex); - } - - /** - * Initialize MoveProxy with given moveFrom and moveTo arrays. - */ - public void setup(ReadableArray moveFrom, ReadableArray moveTo) { - mMoveTo = moveTo; - - if (moveFrom == null) { - setSize(0); - return; - } - - int size = moveFrom.size(); - int requiredSpace = size + size; - if (mMapping.length < requiredSpace) { - mMapping = new int[requiredSpace]; - mChildren = new FlatShadowNode[size]; - } - - setSize(size); - - // Array contains data in the following way: - // [ k0, v0, k1, v1, k2, v2, ... ] - // - // where vi = moveFrom.getInt(ki) - - // We don't technically *need* to store vi, but they are accessed so often that it makes sense - // to cache it instead of calling ReadableArray.getInt() all the time. - - // Sorting algorithm will reorder ki/vi pairs in such a way that vi < v(i+1) - - // Code below is an insertion sort, adapted from DualPivotQuicksort.doSort() - - // At each step i, we got the following data: - - // [k0, v0, k1, v2, .. k(i-1), v(i-1), unused...] - // where v0 < v1 < v2 ... < v(i-1) - // - // This holds true for step i = 0 (array of size one is sorted) - // Again, k0 = 0, v0 = moveFrom.getInt(k0) - setKeyValue(0, 0, moveFrom.getInt(0)); - - // At each of the next steps, we grab a new key and walk back until we find first key that is - // less than current, shifting key/value pairs if they are larger than current key. - for (int i = 1; i < size; i++) { - // this is our next key - int current = moveFrom.getInt(i); - - // this loop will find correct position for it - int j; - - // At this point, array is like this: [ k0, v0, k1, v1, k2, v2, ..., k(i-1), v(i-1), ... ] - for (j = i - 1; j >= 0; j--) { - if (moveFromToValue(j) < current) { - break; - } - - // value at index j is < current value, shift that value and its key - setKeyValue(j + 1, moveFromToIndex(j), moveFromToValue(j)); - } - - setKeyValue(j + 1, i, current); - } - } - - /** - * Returns index of ith key in array. - */ - private static int k(int i) { - return i * 2; - } - - /** - * Returns index of ith value in array. - */ - private static int v(int i) { - return i * 2 + 1; - } - - private void setKeyValue(int index, int key, int value) { - mMapping[k(index)] = key; - mMapping[v(index)] = value; - } - - private int moveFromToIndex(int index) { - return mMapping[k(index)]; - } - - private int moveFromToValue(int index) { - return mMapping[v(index)]; - } - - private static int moveToToIndex(int index) { - return index; - } - - private int moveToToValue(int index) { - return Assertions.assumeNotNull(mMoveTo).getInt(index); - } - - private void setSize(int newSize) { - // reset references to null when shrinking to avoid memory leaks - for (int i = newSize; i < mSize; ++i) { - mChildren[i] = null; - } - - mSize = newSize; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/NativeViewWrapper.java b/ReactAndroid/src/main/java/com/facebook/react/flat/NativeViewWrapper.java deleted file mode 100644 index a930f7504..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/NativeViewWrapper.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.react.uimanager.ReactShadowNode; -import com.facebook.react.uimanager.ReactShadowNodeImpl; -import com.facebook.react.uimanager.ReactStylesDiffMap; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIViewOperationQueue; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.ViewManager; -import com.facebook.yoga.YogaMeasureFunction; -import com.facebook.yoga.YogaUnit; -import com.facebook.yoga.YogaValue; -import javax.annotation.Nullable; - -/* package */ final class NativeViewWrapper extends FlatShadowNode implements AndroidView { - - @Nullable - private final ReactShadowNode mReactShadowNode; - private final boolean mNeedsCustomLayoutForChildren; - private boolean mPaddingChanged = false; - private boolean mForceMountGrandChildrenToView; - - /* package */ NativeViewWrapper(ViewManager viewManager) { - ReactShadowNode reactShadowNode = viewManager.createShadowNodeInstance(); - if (reactShadowNode instanceof YogaMeasureFunction) { - mReactShadowNode = reactShadowNode; - setMeasureFunction((YogaMeasureFunction) reactShadowNode); - } else { - mReactShadowNode = null; - } - - if (viewManager instanceof ViewGroupManager) { - ViewGroupManager viewGroupManager = (ViewGroupManager) viewManager; - mNeedsCustomLayoutForChildren = viewGroupManager.needsCustomLayoutForChildren(); - mForceMountGrandChildrenToView = viewGroupManager.shouldPromoteGrandchildren(); - } else { - mNeedsCustomLayoutForChildren = false; - } - - forceMountToView(); - forceMountChildrenToView(); - } - - @Override - public boolean needsCustomLayoutForChildren() { - return mNeedsCustomLayoutForChildren; - } - - @Override - public boolean isPaddingChanged() { - return mPaddingChanged; - } - - @Override - public void resetPaddingChanged() { - mPaddingChanged = false; - } - - @Override - public void setBackgroundColor(int backgroundColor) { - // suppress, this is handled by a ViewManager - } - - @Override - public void setReactTag(int reactTag) { - super.setReactTag(reactTag); - if (mReactShadowNode != null) { - mReactShadowNode.setReactTag(reactTag); - } - } - - @Override - public void setThemedContext(ThemedReactContext themedContext) { - super.setThemedContext(themedContext); - - if (mReactShadowNode != null) { - mReactShadowNode.setThemedContext(themedContext); - } - } - - @Override - /* package*/ void handleUpdateProperties(ReactStylesDiffMap styles) { - if (mReactShadowNode != null) { - mReactShadowNode.updateProperties(styles); - } - } - - @Override - public void addChildAt(ReactShadowNodeImpl child, int i) { - super.addChildAt(child, i); - if (mForceMountGrandChildrenToView && child instanceof FlatShadowNode) { - ((FlatShadowNode) child).forceMountChildrenToView(); - } - } - - @Override - public void setPadding(int spacingType, float padding) { - YogaValue current = getStylePadding(spacingType); - if (current.unit != YogaUnit.POINT || current.value != padding) { - super.setPadding(spacingType, padding); - mPaddingChanged = true; - markUpdated(); - } - } - - @Override - public void setPaddingPercent(int spacingType, float percent) { - YogaValue current = getStylePadding(spacingType); - if (current.unit != YogaUnit.PERCENT || current.value != percent) { - super.setPadding(spacingType, percent); - mPaddingChanged = true; - markUpdated(); - } - } - - @Override - public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) { - if (mReactShadowNode != null && mReactShadowNode.hasUnseenUpdates()) { - mReactShadowNode.onCollectExtraUpdates(uiViewOperationQueue); - markUpdateSeen(); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/NodeRegion.java b/ReactAndroid/src/main/java/com/facebook/react/flat/NodeRegion.java deleted file mode 100644 index 41169f7f2..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/NodeRegion.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -/* package */ class NodeRegion { - /* package */ static final NodeRegion[] EMPTY_ARRAY = new NodeRegion[0]; - /* package */ static final NodeRegion EMPTY = new NodeRegion(0, 0, 0, 0, -1, false); - - private final float mLeft; - private final float mTop; - private final float mRight; - private final float mBottom; - /* package */ final int mTag; - /* package */ final boolean mIsVirtual; - - /* package */ NodeRegion( - float left, - float top, - float right, - float bottom, - int tag, - boolean isVirtual) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mTag = tag; - mIsVirtual = isVirtual; - } - - /* package */ final boolean matches( - float left, - float top, - float right, - float bottom, - boolean isVirtual) { - return left == mLeft && top == mTop && right == mRight && bottom == mBottom && - isVirtual == mIsVirtual; - } - - /** - * The left bound of the underlying node. - * - * @return The node bound. - */ - /* package */ final float getLeft() { - return mLeft; - } - - /** - * The top bound of the underlying node. - * - * @return The node bound. - */ - /* package */ final float getTop() { - return mTop; - } - - /** - * The right bound of the underlying node. - * - * @return The node bound. - */ - /* package */ final float getRight() { - return mRight; - } - - /** - * The bottom bound of the underlying node. - * - * @return The node bound. - */ - /* package */ final float getBottom() { - return mBottom; - } - - /** - * The left bound of the region for the purpose of touch. This is usually the bound of the - * underlying node, except in the case of hit slop. - * - * @return The touch bound. - */ - /* package */ float getTouchableLeft() { - return getLeft(); - } - - /** - * The top bound of the region for the purpose of touch. This is usually the bound of the - * underlying node, except in the case of hit slop. - * - * @return The touch bound. - */ - /* package */ float getTouchableTop() { - return getTop(); - } - - /** - * The right bound of the region for the purpose of touch. This is usually the bound of the - * underlying node, except in the case of hit slop. - * - * @return The touch bound. - */ - /* package */ float getTouchableRight() { - return getRight(); - } - - /** - * The bottom bound of the region for the purpose of touch. This is usually the bound of the - * underlying node, except in the case of hit slop. - * - * @return The touch bound. - */ - /* package */ float getTouchableBottom() { - return getBottom(); - } - - /* package */ boolean withinBounds(float touchX, float touchY) { - return mLeft <= touchX && touchX < mRight && mTop <= touchY && touchY < mBottom; - } - - /* package */ int getReactTag(float touchX, float touchY) { - return mTag; - } - - /* package */ boolean matchesTag(int tag) { - return mTag == tag; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/PipelineRequestHelper.java b/ReactAndroid/src/main/java/com/facebook/react/flat/PipelineRequestHelper.java deleted file mode 100644 index 77b4fb0dc..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/PipelineRequestHelper.java +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Bitmap; - -import com.facebook.common.executors.UiThreadImmediateExecutorService; -import com.facebook.common.references.CloseableReference; -import com.facebook.datasource.DataSource; -import com.facebook.datasource.DataSubscriber; -import com.facebook.imagepipeline.core.ImagePipeline; -import com.facebook.imagepipeline.core.ImagePipelineFactory; -import com.facebook.imagepipeline.image.CloseableBitmap; -import com.facebook.imagepipeline.image.CloseableImage; -import com.facebook.imagepipeline.request.ImageRequest; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.views.image.ImageLoadEvent; - -/** - * Helper class for DrawImage that helps manage fetch requests through ImagePipeline. - * - * Request states this class can be in: - * 1) mDataSource == null, mImageRef == null : request has not be started, was canceled or failed. - * 2) mDataSource != null, mImageRef == null : request is in progress. - * 3) mDataSource == null, mImageRef != null : request successfully finished. - * 4) mDataSource != null, mImageRef != null : invalid state (should never happen) - */ -/* package */ final class PipelineRequestHelper - implements DataSubscriber> { - - private final ImageRequest mImageRequest; - private @Nullable BitmapUpdateListener mBitmapUpdateListener; - private @Nullable DataSource> mDataSource; - private @Nullable CloseableReference mImageRef; - private int mAttachCounter; - - /* package */ PipelineRequestHelper(ImageRequest imageRequest) { - mImageRequest = imageRequest; - } - - /* package */ void attach(BitmapUpdateListener listener) { - mBitmapUpdateListener = listener; - - mAttachCounter++; - if (mAttachCounter != 1) { - // this is a secondary attach, ignore it, only updating Bitmap boundaries if needed. - Bitmap bitmap = getBitmap(); - if (bitmap != null) { - listener.onSecondaryAttach(bitmap); - } - return; - } - - listener.onImageLoadEvent(ImageLoadEvent.ON_LOAD_START); - - Assertions.assertCondition(mDataSource == null); - Assertions.assertCondition(mImageRef == null); - - // Submit the request - ImagePipeline imagePipeline = ImagePipelineFactory.getInstance().getImagePipeline(); - mDataSource = imagePipeline.fetchDecodedImage(mImageRequest, RCTImageView.getCallerContext()); - mDataSource.subscribe(this, UiThreadImmediateExecutorService.getInstance()); - } - - /** - * Returns whether detach() was primary, false otherwise. - */ - /* package */ void detach() { - --mAttachCounter; - if (mAttachCounter != 0) { - // this is a secondary detach, ignore it - return; - } - - if (mDataSource != null) { - mDataSource.close(); - mDataSource = null; - } - - if (mImageRef != null) { - mImageRef.close(); - mImageRef = null; - } - - mBitmapUpdateListener = null; - } - - /** - * Returns an unsafe bitmap reference. Do not assign the result of this method to anything other - * than a local variable, or it will no longer work with the reference count goes to zero. - */ - /* package */ @Nullable Bitmap getBitmap() { - if (mImageRef == null) { - return null; - } - - CloseableImage closeableImage = mImageRef.get(); - if (!(closeableImage instanceof CloseableBitmap)) { - mImageRef.close(); - mImageRef = null; - return null; - } - - return ((CloseableBitmap) closeableImage).getUnderlyingBitmap(); - } - - /* package */ boolean isDetached() { - return mAttachCounter == 0; - } - - @Override - public void onNewResult(DataSource> dataSource) { - if (!dataSource.isFinished()) { - // only interested in final image, no need to close the dataSource - return; - } - - try { - if (mDataSource != dataSource) { - // Shouldn't ever happen, but let's be safe (dataSource got closed by callback still fired?) - return; - } - - mDataSource = null; - - CloseableReference imageReference = dataSource.getResult(); - if (imageReference == null) { - // Shouldn't ever happen, but let's be safe (dataSource got closed by callback still fired?) - return; - } - - CloseableImage image = imageReference.get(); - if (!(image instanceof CloseableBitmap)) { - // only bitmaps are supported - imageReference.close(); - return; - } - - mImageRef = imageReference; - - Bitmap bitmap = getBitmap(); - if (bitmap == null) { - // Shouldn't ever happen, but let's be safe. - return; - } - - BitmapUpdateListener listener = Assertions.assumeNotNull(mBitmapUpdateListener); - listener.onBitmapReady(bitmap); - listener.onImageLoadEvent(ImageLoadEvent.ON_LOAD); - listener.onImageLoadEvent(ImageLoadEvent.ON_LOAD_END); - } finally { - dataSource.close(); - } - } - - @Override - public void onFailure(DataSource> dataSource) { - if (mDataSource == dataSource) { - Assertions.assumeNotNull(mBitmapUpdateListener).onImageLoadEvent(ImageLoadEvent.ON_ERROR); - Assertions.assumeNotNull(mBitmapUpdateListener).onImageLoadEvent(ImageLoadEvent.ON_LOAD_END); - mDataSource = null; - } - - dataSource.close(); - } - - @Override - public void onCancellation(DataSource> dataSource) { - if (mDataSource == dataSource) { - mDataSource = null; - } - - dataSource.close(); - } - - @Override - public void onProgressUpdate(DataSource> dataSource) { - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageView.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageView.java deleted file mode 100644 index 7b74a130b..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageView.java +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import com.facebook.drawee.drawable.ScalingUtils.ScaleType; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.Spacing; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.views.image.ImageResizeMode; - -/** - * RCTImageView is a top-level node for Image. It can display either a remote image - * (source must start with http:// or https://) or a local resource (a BitmapDrawable). - */ -/* package */ class RCTImageView extends FlatShadowNode { - - static Object sCallerContext = RCTImageView.class; - - /** - * Assigns a CallerContext to execute network requests with. - */ - /* package */ static void setCallerContext(Object callerContext) { - sCallerContext = callerContext; - } - - /* package */ static Object getCallerContext() { - return sCallerContext; - } - - private T mDrawImage; - - /* package */ RCTImageView(T drawImage) { - mDrawImage = drawImage; - } - - @Override - protected void collectState( - StateBuilder stateBuilder, - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - super.collectState( - stateBuilder, - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom); - - if (mDrawImage.hasImageRequest()) { - mDrawImage = (T) mDrawImage.updateBoundsAndFreeze( - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom); - stateBuilder.addDrawCommand(mDrawImage); - stateBuilder.addAttachDetachListener(mDrawImage); - } - } - - @Override - boolean doesDraw() { - return mDrawImage.hasImageRequest() || super.doesDraw(); - } - - @ReactProp(name = "shouldNotifyLoadEvents") - public void setShouldNotifyLoadEvents(boolean shouldNotifyLoadEvents) { - getMutableDrawImage().setReactTag(shouldNotifyLoadEvents ? getReactTag() : 0); - } - - @ReactProp(name = "src") - public void setSource(@Nullable ReadableArray sources) { - getMutableDrawImage().setSource(getThemedContext(), sources); - } - - @ReactProp(name = "tintColor") - public void setTintColor(int tintColor) { - getMutableDrawImage().setTintColor(tintColor); - } - - @ReactProp(name = ViewProps.RESIZE_MODE) - public void setResizeMode(@Nullable String resizeMode) { - ScaleType scaleType = ImageResizeMode.toScaleType(resizeMode); - if (mDrawImage.getScaleType() != scaleType) { - getMutableDrawImage().setScaleType(scaleType); - } - } - - @ReactProp(name = "borderColor", customType = "Color") - public void setBorderColor(int borderColor) { - if (mDrawImage.getBorderColor() != borderColor) { - getMutableDrawImage().setBorderColor(borderColor); - } - } - - @Override - public void setBorder(int spacingType, float borderWidth) { - super.setBorder(spacingType, borderWidth); - - if (spacingType == Spacing.ALL && mDrawImage.getBorderWidth() != borderWidth) { - getMutableDrawImage().setBorderWidth(borderWidth); - } - } - - @ReactProp(name = "borderRadius") - public void setBorderRadius(float borderRadius) { - if (mDrawImage.getBorderRadius() != borderRadius) { - getMutableDrawImage().setBorderRadius(PixelUtil.toPixelFromDIP(borderRadius)); - } - } - - @ReactProp(name = "fadeDuration") - public void setFadeDuration(int durationMs) { - getMutableDrawImage().setFadeDuration(durationMs); - } - - @ReactProp(name = "progressiveRenderingEnabled") - public void setProgressiveRenderingEnabled(boolean enabled) { - getMutableDrawImage().setProgressiveRenderingEnabled(enabled); - } - - private T getMutableDrawImage() { - if (mDrawImage.isFrozen()) { - mDrawImage = (T) mDrawImage.mutableCopy(); - invalidate(); - } - - return mDrawImage; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageViewManager.java deleted file mode 100644 index 6d7dcd494..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageViewManager.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; -import com.facebook.react.views.image.GlobalImageLoadListener; -import javax.annotation.Nullable; - -public final class RCTImageViewManager extends FlatViewManager { - - /* package */ static final String REACT_CLASS = "RCTImageView"; - - private @Nullable AbstractDraweeControllerBuilder mDraweeControllerBuilder; - private @Nullable GlobalImageLoadListener mGlobalImageLoadListener; - private final @Nullable Object mCallerContext; - - public RCTImageViewManager() { - this(null, null); - } - - public RCTImageViewManager( - AbstractDraweeControllerBuilder draweeControllerBuilder, - Object callerContext) { - this(draweeControllerBuilder, null, callerContext); - } - - public RCTImageViewManager( - AbstractDraweeControllerBuilder draweeControllerBuilder, - @Nullable GlobalImageLoadListener globalImageLoadListener, - Object callerContext) { - mDraweeControllerBuilder = draweeControllerBuilder; - mGlobalImageLoadListener = globalImageLoadListener; - mCallerContext = callerContext; - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - public RCTImageView createShadowNodeInstance() { - return new RCTImageView(new DrawImageWithDrawee(mGlobalImageLoadListener)); - } - - @Override - public Class getShadowNodeClass() { - return RCTImageView.class; - } - - public AbstractDraweeControllerBuilder getDraweeControllerBuilder() { - if (mDraweeControllerBuilder == null) { - mDraweeControllerBuilder = Fresco.newDraweeControllerBuilder(); - } - return mDraweeControllerBuilder; - } - - public Object getCallerContext() { - return mCallerContext; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTModalHostManager.java deleted file mode 100644 index 1b49f5753..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTModalHostManager.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.react.uimanager.LayoutShadowNode; -import com.facebook.react.views.modal.ReactModalHostManager; - -public class RCTModalHostManager extends ReactModalHostManager { - - /* package */ static final String REACT_CLASS = ReactModalHostManager.REACT_CLASS; - - @Override - public LayoutShadowNode createShadowNodeInstance() { - return new FlatReactModalShadowNode(); - } - - @Override - public Class getShadowNodeClass() { - return FlatReactModalShadowNode.class; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTRawText.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTRawText.java deleted file mode 100644 index f0a8d1d33..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTRawText.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.text.Spannable; -import android.text.SpannableStringBuilder; - -import com.facebook.react.uimanager.annotations.ReactProp; - -/** - * RCTRawText is a FlatTextShadowNode that can only contain raw text (but not styling). - */ -/* package */ final class RCTRawText extends FlatTextShadowNode { - - private @Nullable String mText; - - @Override - protected void performCollectText(SpannableStringBuilder builder) { - if (mText != null) { - builder.append(mText); - } - } - - @Override - protected void performApplySpans( - SpannableStringBuilder builder, - int begin, - int end, - boolean isEditable) { - builder.setSpan( - this, - begin, - end, - Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - - @Override - protected void performCollectAttachDetachListeners(StateBuilder stateBuilder) { - // nothing to do - } - - @ReactProp(name = "text") - public void setText(@Nullable String text) { - mText = text; - notifyChanged(true); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTRawTextManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTRawTextManager.java deleted file mode 100644 index c1695c507..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTRawTextManager.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -/** - * ViewManager that creates instances of RCTRawText. - */ -public final class RCTRawTextManager extends VirtualViewManager { - - /* package */ static final String REACT_CLASS = "RCTRawText"; - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - public RCTRawText createShadowNodeInstance() { - return new RCTRawText(); - } - - @Override - public Class getShadowNodeClass() { - return RCTRawText.class; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTText.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTText.java deleted file mode 100644 index 683aabf6d..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTText.java +++ /dev/null @@ -1,360 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.support.v4.text.TextDirectionHeuristicsCompat; -import android.text.Layout; -import android.text.TextUtils; -import android.view.Gravity; - -import com.facebook.yoga.YogaDirection; -import com.facebook.yoga.YogaMeasureMode; -import com.facebook.yoga.YogaMeasureFunction; -import com.facebook.yoga.YogaNode; -import com.facebook.yoga.YogaMeasureOutput; -import com.facebook.fbui.textlayoutbuilder.TextLayoutBuilder; -import com.facebook.fbui.textlayoutbuilder.glyphwarmer.GlyphWarmerImpl; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.Spacing; -import com.facebook.react.uimanager.ViewDefaults; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; - -/** - * RCTText is a top-level node for text. It extends {@link RCTVirtualText} because it can contain - * styling information, but has the following differences: - * - * a) RCTText is not a virtual node, and can be measured and laid out. - * b) when no font size is specified, a font size of ViewDefaults#FONT_SIZE_SP is assumed. - */ -/* package */ final class RCTText extends RCTVirtualText implements YogaMeasureFunction { - - // index of left and right in the Layout.Alignment enum since the base values are @hide - private static final int ALIGNMENT_LEFT = 3; - private static final int ALIGNMENT_RIGHT = 4; - - // We set every value we use every time we use the layout builder, so we can get away with only - // using a single instance. - private static final TextLayoutBuilder sTextLayoutBuilder = - new TextLayoutBuilder() - .setShouldCacheLayout(false) - .setShouldWarmText(true) - .setGlyphWarmer(new GlyphWarmerImpl()); - - private @Nullable CharSequence mText; - private @Nullable DrawTextLayout mDrawCommand; - private float mSpacingMult = 1.0f; - private float mSpacingAdd = 0.0f; - private int mNumberOfLines = Integer.MAX_VALUE; - private int mAlignment = Gravity.NO_GRAVITY; - private boolean mIncludeFontPadding = true; - - public RCTText() { - setMeasureFunction(this); - getSpan().setFontSize(getDefaultFontSize()); - } - - @Override - public boolean isVirtual() { - return false; - } - - @Override - public boolean isVirtualAnchor() { - return true; - } - - @Override - public long measure( - YogaNode node, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode) { - - CharSequence text = getText(); - if (TextUtils.isEmpty(text)) { - // to indicate that we don't have anything to display - mText = null; - return YogaMeasureOutput.make(0, 0); - } else { - mText = text; - } - - Layout layout = createTextLayout( - (int) Math.ceil(width), - widthMode, - TextUtils.TruncateAt.END, - mIncludeFontPadding, - mNumberOfLines, - mNumberOfLines == 1, - text, - getFontSize(), - mSpacingAdd, - mSpacingMult, - getFontStyle(), - getAlignment()); - - if (mDrawCommand != null && !mDrawCommand.isFrozen()) { - mDrawCommand.setLayout(layout); - } else { - mDrawCommand = new DrawTextLayout(layout); - } - - return YogaMeasureOutput.make(mDrawCommand.getLayoutWidth(), mDrawCommand.getLayoutHeight()); - } - - @Override - protected void collectState( - StateBuilder stateBuilder, - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - - super.collectState( - stateBuilder, - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom); - - if (mText == null) { - // as an optimization, LayoutEngine may not call measure in certain cases, such as when the - // dimensions are already defined. in these cases, we should still draw the text. - if (bottom - top > 0 && right - left > 0) { - CharSequence text = getText(); - if (!TextUtils.isEmpty(text)) { - mText = text; - } - } - - if (mText == null) { - // nothing to draw (empty text). - return; - } - } - - boolean updateNodeRegion = false; - if (mDrawCommand == null) { - mDrawCommand = new DrawTextLayout(createTextLayout( - (int) Math.ceil(right - left), - YogaMeasureMode.EXACTLY, - TextUtils.TruncateAt.END, - mIncludeFontPadding, - mNumberOfLines, - mNumberOfLines == 1, - mText, - getFontSize(), - mSpacingAdd, - mSpacingMult, - getFontStyle(), - getAlignment())); - updateNodeRegion = true; - } - - left += getPadding(Spacing.LEFT); - top += getPadding(Spacing.TOP); - - // these are actual right/bottom coordinates where this DrawCommand will draw. - right = left + mDrawCommand.getLayoutWidth(); - bottom = top + mDrawCommand.getLayoutHeight(); - - mDrawCommand = (DrawTextLayout) mDrawCommand.updateBoundsAndFreeze( - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom); - stateBuilder.addDrawCommand(mDrawCommand); - - if (updateNodeRegion) { - NodeRegion nodeRegion = getNodeRegion(); - if (nodeRegion instanceof TextNodeRegion) { - ((TextNodeRegion) nodeRegion).setLayout(mDrawCommand.getLayout()); - } - } - - performCollectAttachDetachListeners(stateBuilder); - } - - @Override - boolean doesDraw() { - // assume text always draws - this is a performance optimization to avoid having to - // getText() when layout was skipped and when collectState wasn't yet called - return true; - } - - @ReactProp(name = ViewProps.LINE_HEIGHT, defaultDouble = Double.NaN) - public void setLineHeight(double lineHeight) { - if (Double.isNaN(lineHeight)) { - mSpacingMult = 1.0f; - mSpacingAdd = 0.0f; - } else { - mSpacingMult = 0.0f; - mSpacingAdd = PixelUtil.toPixelFromSP((float) lineHeight); - } - notifyChanged(true); - } - - @ReactProp(name = ViewProps.NUMBER_OF_LINES, defaultInt = Integer.MAX_VALUE) - public void setNumberOfLines(int numberOfLines) { - mNumberOfLines = numberOfLines; - notifyChanged(true); - } - - @ReactProp(name = ViewProps.INCLUDE_FONT_PADDING, defaultBoolean = true) - public void setIncludeFontPadding(boolean includepad) { - mIncludeFontPadding = includepad; - } - - @Override - /* package */ void updateNodeRegion( - float left, - float top, - float right, - float bottom, - boolean isVirtual) { - - NodeRegion nodeRegion = getNodeRegion(); - if (mDrawCommand == null) { - if (!nodeRegion.matches(left, top, right, bottom, isVirtual)) { - setNodeRegion(new TextNodeRegion(left, top, right, bottom, getReactTag(), isVirtual, null)); - } - return; - } - - Layout layout = null; - - if (nodeRegion instanceof TextNodeRegion) { - layout = ((TextNodeRegion) nodeRegion).getLayout(); - } - - Layout newLayout = mDrawCommand.getLayout(); - if (!nodeRegion.matches(left, top, right, bottom, isVirtual) || layout != newLayout) { - setNodeRegion( - new TextNodeRegion(left, top, right, bottom, getReactTag(), isVirtual, newLayout)); - } - } - - @Override - protected int getDefaultFontSize() { - // top-level should always specify font size. - return fontSizeFromSp(ViewDefaults.FONT_SIZE_SP); - } - - @Override - protected void notifyChanged(boolean shouldRemeasure) { - // Future patch: should only recreate Layout if shouldRemeasure is false - dirty(); - } - - @ReactProp(name = ViewProps.TEXT_ALIGN) - public void setTextAlign(@Nullable String textAlign) { - if (textAlign == null || "auto".equals(textAlign)) { - mAlignment = Gravity.NO_GRAVITY; - } else if ("left".equals(textAlign)) { - // left and right may yield potentially different results (relative to non-nodes) in cases - // when supportsRTL="true" in the manifest. - mAlignment = Gravity.LEFT; - } else if ("right".equals(textAlign)) { - mAlignment = Gravity.RIGHT; - } else if ("center".equals(textAlign)) { - mAlignment = Gravity.CENTER; - } else { - throw new JSApplicationIllegalArgumentException("Invalid textAlign: " + textAlign); - } - - notifyChanged(false); - } - - public Layout.Alignment getAlignment() { - boolean isRtl = getLayoutDirection() == YogaDirection.RTL; - switch (mAlignment) { - // Layout.Alignment.RIGHT and Layout.Alignment.LEFT are @hide :( - case Gravity.LEFT: - int index = isRtl ? ALIGNMENT_RIGHT : ALIGNMENT_LEFT; - return Layout.Alignment.values()[index]; - case Gravity.RIGHT: - index = isRtl ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT; - return Layout.Alignment.values()[index]; - case Gravity.CENTER: - return Layout.Alignment.ALIGN_CENTER; - case Gravity.NO_GRAVITY: - default: - return Layout.Alignment.ALIGN_NORMAL; - } - } - - private static Layout createTextLayout( - int width, - YogaMeasureMode widthMode, - TextUtils.TruncateAt ellipsize, - boolean shouldIncludeFontPadding, - int maxLines, - boolean isSingleLine, - CharSequence text, - int textSize, - float extraSpacing, - float spacingMultiplier, - int textStyle, - Layout.Alignment textAlignment) { - Layout newLayout; - - final @TextLayoutBuilder.MeasureMode int textMeasureMode; - switch (widthMode) { - case UNDEFINED: - textMeasureMode = TextLayoutBuilder.MEASURE_MODE_UNSPECIFIED; - break; - case EXACTLY: - textMeasureMode = TextLayoutBuilder.MEASURE_MODE_EXACTLY; - break; - case AT_MOST: - textMeasureMode = TextLayoutBuilder.MEASURE_MODE_AT_MOST; - break; - default: - throw new IllegalStateException("Unexpected size mode: " + widthMode); - } - - sTextLayoutBuilder - .setEllipsize(ellipsize) - .setMaxLines(maxLines) - .setSingleLine(isSingleLine) - .setText(text) - .setTextSize(textSize) - .setWidth(width, textMeasureMode); - - sTextLayoutBuilder.setTextStyle(textStyle); - - sTextLayoutBuilder.setTextDirection(TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR); - sTextLayoutBuilder.setIncludeFontPadding(shouldIncludeFontPadding); - sTextLayoutBuilder.setTextSpacingExtra(extraSpacing); - sTextLayoutBuilder.setTextSpacingMultiplier(spacingMultiplier); - sTextLayoutBuilder.setAlignment(textAlignment); - - newLayout = sTextLayoutBuilder.build(); - - sTextLayoutBuilder.setText(null); - - return newLayout; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInlineImage.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInlineImage.java deleted file mode 100644 index 728df41d5..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInlineImage.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.text.SpannableStringBuilder; -import android.text.Spanned; - -import com.facebook.imagepipeline.request.ImageRequestBuilder; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.views.imagehelper.ImageSource; - -/** - * RCTTextInlineImage - */ -/* package */ class RCTTextInlineImage extends FlatTextShadowNode { - - private InlineImageSpanWithPipeline mInlineImageSpan = new InlineImageSpanWithPipeline(); - - @Override - public void setStyleWidth(float width) { - super.setStyleWidth(width); - - if (mInlineImageSpan.getWidth() != width) { - getMutableSpan().setWidth(width); - notifyChanged(true); - } - } - - @Override - public void setStyleHeight(float height) { - super.setStyleHeight(height); - - if (mInlineImageSpan.getHeight() != height) { - getMutableSpan().setHeight(height); - notifyChanged(true); - } - } - - @Override - protected void performCollectText(SpannableStringBuilder builder) { - builder.append("I"); - } - - @Override - protected void performApplySpans( - SpannableStringBuilder builder, - int begin, - int end, - boolean isEditable) { - mInlineImageSpan.freeze(); - builder.setSpan( - mInlineImageSpan, - begin, - end, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - - @Override - protected void performCollectAttachDetachListeners(StateBuilder stateBuilder) { - // mInlineImageSpan should already be frozen so no need to freeze it again - stateBuilder.addAttachDetachListener(mInlineImageSpan); - } - - @ReactProp(name = "src") - public void setSource(@Nullable ReadableArray sources) { - final String source = - (sources == null || sources.size() == 0) ? null : sources.getMap(0).getString("uri"); - final ImageSource imageSource = source == null ? null : - new ImageSource(getThemedContext(), source); - getMutableSpan().setImageRequest(imageSource == null ? null : - ImageRequestBuilder.newBuilderWithSource(imageSource.getUri()).build()); - } - - private InlineImageSpanWithPipeline getMutableSpan() { - if (mInlineImageSpan.isFrozen()) { - mInlineImageSpan = mInlineImageSpan.mutableCopy(); - } - return mInlineImageSpan; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInlineImageManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInlineImageManager.java deleted file mode 100644 index f67037858..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInlineImageManager.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -/** - * ViewManager that creates instances of RCTTextInlineImage. - */ -public final class RCTTextInlineImageManager extends VirtualViewManager { - - /* package */ static final String REACT_CLASS = "RCTTextInlineImage"; - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - public RCTTextInlineImage createShadowNodeInstance() { - return new RCTTextInlineImage(); - } - - @Override - public Class getShadowNodeClass() { - return RCTTextInlineImage.class; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInput.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInput.java deleted file mode 100644 index 77d3cd9a3..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInput.java +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import static com.facebook.react.views.text.ReactRawTextShadowNode.PROP_TEXT; -import static com.facebook.react.views.text.ReactTextShadowNode.UNSET; - -import android.annotation.TargetApi; -import android.os.Build; -import android.text.SpannableStringBuilder; -import android.util.TypedValue; -import android.view.ViewGroup; -import android.widget.EditText; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.Spacing; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIViewOperationQueue; -import com.facebook.react.uimanager.ViewDefaults; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.views.text.ReactTextUpdate; -import com.facebook.react.views.view.MeasureUtil; -import com.facebook.yoga.YogaMeasureFunction; -import com.facebook.yoga.YogaMeasureMode; -import com.facebook.yoga.YogaMeasureOutput; -import com.facebook.yoga.YogaNode; -import javax.annotation.Nullable; - -public class RCTTextInput extends RCTVirtualText implements AndroidView, YogaMeasureFunction { - - @Nullable private String mText; - private int mJsEventCount = UNSET; - private boolean mPaddingChanged = false; - private int mNumberOfLines = UNSET; - private @Nullable EditText mEditText; - - public RCTTextInput() { - forceMountToView(); - setMeasureFunction(this); - } - - @Override - protected void notifyChanged(boolean shouldRemeasure) { - super.notifyChanged(shouldRemeasure); - // needed to trigger onCollectExtraUpdates - markUpdated(); - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - @Override - public void setThemedContext(ThemedReactContext themedContext) { - super.setThemedContext(themedContext); - - mEditText = new EditText(themedContext); - // This is needed to fix an android bug since 4.4.3 which will throw an NPE in measure, - // setting the layoutParams fixes it: https://code.google.com/p/android/issues/detail?id=75877 - mEditText.setLayoutParams( - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - - setDefaultPadding(Spacing.START, mEditText.getPaddingStart()); - setDefaultPadding(Spacing.TOP, mEditText.getPaddingTop()); - setDefaultPadding(Spacing.END, mEditText.getPaddingEnd()); - setDefaultPadding(Spacing.BOTTOM, mEditText.getPaddingBottom()); - mEditText.setPadding(0, 0, 0, 0); - } - - @Override - public long measure( - YogaNode node, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode) { - // measure() should never be called before setThemedContext() - EditText editText = Assertions.assertNotNull(mEditText); - - int fontSize = getFontSize(); - editText.setTextSize( - TypedValue.COMPLEX_UNIT_PX, - fontSize == UNSET ? - (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)) : fontSize); - - if (mNumberOfLines != UNSET) { - editText.setLines(mNumberOfLines); - } - - editText.measure( - MeasureUtil.getMeasureSpec(width, widthMode), - MeasureUtil.getMeasureSpec(height, heightMode)); - return YogaMeasureOutput.make(editText.getMeasuredWidth(), editText.getMeasuredHeight()); - } - - @Override - public boolean isVirtual() { - return false; - } - - @Override - public boolean isVirtualAnchor() { - return true; - } - - @Override - public void setBackgroundColor(int backgroundColor) { - // suppress, this is handled by a ViewManager - } - - @Override - public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) { - super.onCollectExtraUpdates(uiViewOperationQueue); - if (mJsEventCount != UNSET) { - ReactTextUpdate reactTextUpdate = - new ReactTextUpdate( - getText(), - mJsEventCount, - false, - getPadding(Spacing.START), - getPadding(Spacing.TOP), - getPadding(Spacing.END), - getPadding(Spacing.BOTTOM), - UNSET); - // TODO: the Float.NaN should be replaced with the real line height see D3592781 - uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate); - } - } - - @ReactProp(name = "mostRecentEventCount") - public void setMostRecentEventCount(int mostRecentEventCount) { - mJsEventCount = mostRecentEventCount; - } - - @ReactProp(name = ViewProps.NUMBER_OF_LINES, defaultInt = Integer.MAX_VALUE) - public void setNumberOfLines(int numberOfLines) { - mNumberOfLines = numberOfLines; - notifyChanged(true); - } - - @ReactProp(name = PROP_TEXT) - public void setText(@Nullable String text) { - mText = text; - notifyChanged(true); - } - - @Override - public void setPadding(int spacingType, float padding) { - super.setPadding(spacingType, padding); - mPaddingChanged = true; - dirty(); - } - - @Override - public boolean isPaddingChanged() { - return mPaddingChanged; - } - - @Override - public void resetPaddingChanged() { - mPaddingChanged = false; - } - - @Override - boolean shouldAllowEmptySpans() { - return true; - } - - @Override - boolean isEditable() { - return true; - } - - @Override - protected void performCollectText(SpannableStringBuilder builder) { - if (mText != null) { - builder.append(mText); - } - super.performCollectText(builder); - } - - @Override - public boolean needsCustomLayoutForChildren() { - return false; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInputManager.java deleted file mode 100644 index 62fe19960..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInputManager.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import com.facebook.react.views.textinput.ReactTextInputManager; - -public class RCTTextInputManager extends ReactTextInputManager { - - /* package */ static final String REACT_CLASS = ReactTextInputManager.REACT_CLASS; - - @Override - public RCTTextInput createShadowNodeInstance() { - return new RCTTextInput(); - } - - @Override - public Class getShadowNodeClass() { - return RCTTextInput.class; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextManager.java deleted file mode 100644 index 77ec1fb25..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextManager.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -/** - * ViewManager that creates instances of RCTText. - */ -public final class RCTTextManager extends FlatViewManager { - - /* package */ static final String REACT_CLASS = "RCTText"; - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - public RCTText createShadowNodeInstance() { - return new RCTText(); - } - - @Override - public Class getShadowNodeClass() { - return RCTText.class; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTView.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTView.java deleted file mode 100644 index 2beaa2230..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTView.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.graphics.Rect; - -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.Spacing; -import com.facebook.react.uimanager.ReactStylesDiffMap; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.annotations.ReactPropGroup; - -/** - * Node for a react View. - */ -/* package */ final class RCTView extends FlatShadowNode { - private static final int[] SPACING_TYPES = { - Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM, - }; - - private @Nullable DrawBorder mDrawBorder; - - boolean mRemoveClippedSubviews; - boolean mHorizontal; - - private @Nullable Rect mHitSlop; - - @Override - /* package */ void handleUpdateProperties(ReactStylesDiffMap styles) { - mRemoveClippedSubviews = mRemoveClippedSubviews || - (styles.hasKey(PROP_REMOVE_CLIPPED_SUBVIEWS) && - styles.getBoolean(PROP_REMOVE_CLIPPED_SUBVIEWS, false)); - - if (mRemoveClippedSubviews) { - mHorizontal = mHorizontal || - (styles.hasKey(PROP_HORIZONTAL) && styles.getBoolean(PROP_HORIZONTAL, false)); - } - - super.handleUpdateProperties(styles); - } - - @Override - protected void collectState( - StateBuilder stateBuilder, - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - super.collectState( - stateBuilder, - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom); - - if (mDrawBorder != null) { - mDrawBorder = (DrawBorder) mDrawBorder.updateBoundsAndFreeze( - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom); - stateBuilder.addDrawCommand(mDrawBorder); - } - } - - @Override - boolean doesDraw() { - return mDrawBorder != null || super.doesDraw(); - } - - @Override - public void setBackgroundColor(int backgroundColor) { - getMutableBorder().setBackgroundColor(backgroundColor); - } - - @Override - public void setBorderWidths(int index, float borderWidth) { - super.setBorderWidths(index, borderWidth); - - int type = SPACING_TYPES[index]; - getMutableBorder().setBorderWidth(type, PixelUtil.toPixelFromDIP(borderWidth)); - } - - @ReactProp(name = "nativeBackgroundAndroid") - public void setHotspot(@Nullable ReadableMap bg) { - if (bg != null) { - forceMountToView(); - } - } - - @ReactPropGroup(names = { - "borderColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderBottomColor" - }, customType = "Color", defaultDouble = Double.NaN) - public void setBorderColor(int index, double color) { - int type = SPACING_TYPES[index]; - if (Double.isNaN(color)) { - getMutableBorder().resetBorderColor(type); - } else { - getMutableBorder().setBorderColor(type, (int) color); - } - } - - @ReactProp(name = "borderRadius") - public void setBorderRadius(float borderRadius) { - mClipRadius = borderRadius; - if (mClipToBounds && borderRadius > DrawView.MINIMUM_ROUNDED_CLIPPING_VALUE) { - // mount to a view if we are overflow: hidden and are clipping, so that we can do one - // clipPath to clip all the children of this node (both DrawCommands and Views). - forceMountToView(); - } - getMutableBorder().setBorderRadius(PixelUtil.toPixelFromDIP(borderRadius)); - } - - @ReactProp(name = "borderStyle") - public void setBorderStyle(@Nullable String borderStyle) { - getMutableBorder().setBorderStyle(borderStyle); - } - - @ReactProp(name = "hitSlop") - public void setHitSlop(@Nullable ReadableMap hitSlop) { - if (hitSlop == null) { - mHitSlop = null; - } else { - mHitSlop = new Rect( - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("left")), - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("top")), - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("right")), - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("bottom"))); - } - } - - @ReactProp(name = "pointerEvents") - public void setPointerEvents(@Nullable String pointerEventsStr) { - forceMountToView(); - } - - @Override - /* package */ void updateNodeRegion( - float left, - float top, - float right, - float bottom, - boolean isVirtual) { - if (!getNodeRegion().matches(left, top, right, bottom, isVirtual)) { - setNodeRegion(mHitSlop == null ? - new NodeRegion(left, top, right, bottom, getReactTag(), isVirtual) : - new HitSlopNodeRegion(mHitSlop, left, top, right, bottom, getReactTag(), isVirtual)); - } - } - - private DrawBorder getMutableBorder() { - if (mDrawBorder == null) { - mDrawBorder = new DrawBorder(); - } else if (mDrawBorder.isFrozen()) { - mDrawBorder = (DrawBorder) mDrawBorder.mutableCopy(); - } - invalidate(); - return mDrawBorder; - } - - @Override - public boolean clipsSubviews() { - return mRemoveClippedSubviews; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTViewManager.java deleted file mode 100644 index 2e08dde5d..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTViewManager.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Rect; -import android.os.Build; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.PointerEvents; -import com.facebook.react.uimanager.ReactClippingViewGroupHelper; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.views.view.ReactDrawableHelper; - -import javax.annotation.Nullable; -import java.util.Map; - -/** - * ViewManager that creates instances of RCTView. - */ -public final class RCTViewManager extends FlatViewManager { - - /* package */ static final String REACT_CLASS = ViewProps.VIEW_CLASS_NAME; - - private static final int[] TMP_INT_ARRAY = new int[2]; - - private static final int CMD_HOTSPOT_UPDATE = 1; - private static final int CMD_SET_PRESSED = 2; - - @Override - public String getName() { - return REACT_CLASS; - } - - public Map getCommandsMap() { - return MapBuilder.of("hotspotUpdate", CMD_HOTSPOT_UPDATE, "setPressed", CMD_SET_PRESSED); - } - - @Override - public RCTView createShadowNodeInstance() { - return new RCTView(); - } - - @Override - public Class getShadowNodeClass() { - return RCTView.class; - } - - @ReactProp(name = "nativeBackgroundAndroid") - public void setHotspot(FlatViewGroup view, @Nullable ReadableMap bg) { - view.setHotspot(bg == null ? - null : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg)); - } - - @Override - public void receiveCommand( - FlatViewGroup view, - int commandId, - @Nullable ReadableArray args) { - switch (commandId) { - case CMD_HOTSPOT_UPDATE: { - if (args == null || args.size() != 2) { - throw new JSApplicationIllegalArgumentException( - "Illegal number of arguments for 'updateHotspot' command"); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - view.getLocationOnScreen(TMP_INT_ARRAY); - float x = PixelUtil.toPixelFromDIP(args.getDouble(0)) - TMP_INT_ARRAY[0]; - float y = PixelUtil.toPixelFromDIP(args.getDouble(1)) - TMP_INT_ARRAY[1]; - view.drawableHotspotChanged(x, y); - } - break; - } - case CMD_SET_PRESSED: { - if (args == null || args.size() != 1) { - throw new JSApplicationIllegalArgumentException( - "Illegal number of arguments for 'setPressed' command"); - } - view.setPressed(args.getBoolean(0)); - break; - } - } - } - - @ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING) - public void setNeedsOffscreenAlphaCompositing( - FlatViewGroup view, - boolean needsOffscreenAlphaCompositing) { - view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing); - } - - @ReactProp(name = "pointerEvents") - public void setPointerEvents(FlatViewGroup view, @Nullable String pointerEventsStr) { - view.setPointerEvents(parsePointerEvents(pointerEventsStr)); - } - - @ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS) - public void setRemoveClippedSubviews(FlatViewGroup view, boolean removeClippedSubviews) { - view.setRemoveClippedSubviews(removeClippedSubviews); - } - - private static PointerEvents parsePointerEvents(@Nullable String pointerEventsStr) { - if (pointerEventsStr != null) { - switch (pointerEventsStr) { - case "none": - return PointerEvents.NONE; - case "auto": - return PointerEvents.AUTO; - case "box-none": - return PointerEvents.BOX_NONE; - case "box-only": - return PointerEvents.BOX_ONLY; - } - } - // default or invalid - return PointerEvents.AUTO; - } - - @ReactProp(name = "hitSlop") - public void setHitSlop(FlatViewGroup view, @Nullable ReadableMap hitSlop) { - if (hitSlop == null) { - view.setHitSlopRect(null); - } else { - view.setHitSlopRect(new Rect( - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("left")), - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("top")), - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("right")), - (int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("bottom")) - )); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTViewPagerManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTViewPagerManager.java deleted file mode 100644 index 23b00fe19..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTViewPagerManager.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.view.View; -import com.facebook.react.views.viewpager.ReactViewPager; -import com.facebook.react.views.viewpager.ReactViewPagerManager; - -import java.util.List; - -public class RCTViewPagerManager extends ReactViewPagerManager { - - /* package */ static final String REACT_CLASS = ReactViewPagerManager.REACT_CLASS; - - @Override - public void addViews(ReactViewPager parent, List views) { - parent.setViews(views); - } - - @Override - public void removeAllViews(ReactViewPager parent) { - parent.removeAllViewsFromAdapter(); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTVirtualText.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTVirtualText.java deleted file mode 100644 index 1c0bcee79..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTVirtualText.java +++ /dev/null @@ -1,307 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.graphics.Typeface; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.ReactShadowNodeImpl; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import javax.annotation.Nullable; - -/** - * RCTVirtualText is a {@link FlatTextShadowNode} that can contain font styling information. - */ -/* package */ class RCTVirtualText extends FlatTextShadowNode { - - private static final String BOLD = "bold"; - private static final String ITALIC = "italic"; - private static final String NORMAL = "normal"; - - private static final String PROP_SHADOW_OFFSET = "textShadowOffset"; - private static final String PROP_SHADOW_RADIUS = "textShadowRadius"; - private static final String PROP_SHADOW_COLOR = "textShadowColor"; - private static final int DEFAULT_TEXT_SHADOW_COLOR = 0x55000000; - - private FontStylingSpan mFontStylingSpan = FontStylingSpan.INSTANCE; - private ShadowStyleSpan mShadowStyleSpan = ShadowStyleSpan.INSTANCE; - - @Override - public void addChildAt(ReactShadowNodeImpl child, int i) { - super.addChildAt(child, i); - notifyChanged(true); - } - - @Override - protected void performCollectText(SpannableStringBuilder builder) { - for (int i = 0, childCount = getChildCount(); i < childCount; ++i) { - FlatTextShadowNode child = (FlatTextShadowNode) getChildAt(i); - child.collectText(builder); - } - } - - @Override - protected void performApplySpans(SpannableStringBuilder builder, int begin, int end, boolean isEditable) { - mFontStylingSpan.freeze(); - - // All spans will automatically extend to the right of the text, but not the left - except - // for spans that start at the beginning of the text. - final int flag; - if (isEditable) { - flag = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE; - } else { - flag = begin == 0 ? - Spannable.SPAN_INCLUSIVE_INCLUSIVE : - Spannable.SPAN_EXCLUSIVE_INCLUSIVE; - } - - builder.setSpan( - mFontStylingSpan, - begin, - end, - flag); - - if (mShadowStyleSpan.getColor() != 0 && mShadowStyleSpan.getRadius() != 0) { - mShadowStyleSpan.freeze(); - - builder.setSpan( - mShadowStyleSpan, - begin, - end, - flag); - } - - for (int i = 0, childCount = getChildCount(); i < childCount; ++i) { - FlatTextShadowNode child = (FlatTextShadowNode) getChildAt(i); - child.applySpans(builder, isEditable); - } - } - - @Override - protected void performCollectAttachDetachListeners(StateBuilder stateBuilder) { - for (int i = 0, childCount = getChildCount(); i < childCount; ++i) { - FlatTextShadowNode child = (FlatTextShadowNode) getChildAt(i); - child.performCollectAttachDetachListeners(stateBuilder); - } - } - - @ReactProp(name = ViewProps.FONT_SIZE, defaultFloat = Float.NaN) - public void setFontSize(float fontSizeSp) { - final int fontSize; - if (Float.isNaN(fontSizeSp)) { - fontSize = getDefaultFontSize(); - } else { - fontSize = fontSizeFromSp(fontSizeSp); - } - - if (mFontStylingSpan.getFontSize() != fontSize) { - getSpan().setFontSize(fontSize); - notifyChanged(true); - } - } - - @ReactProp(name = ViewProps.COLOR, defaultDouble = Double.NaN) - public void setColor(double textColor) { - if (mFontStylingSpan.getTextColor() != textColor) { - getSpan().setTextColor(textColor); - notifyChanged(false); - } - } - - @Override - public void setBackgroundColor(int backgroundColor) { - if (isVirtual()) { - // for nested Text elements, we want to apply background color to the text only - // e.g. Hello , "World" will have red background color - if (mFontStylingSpan.getBackgroundColor() != backgroundColor) { - getSpan().setBackgroundColor(backgroundColor); - notifyChanged(false); - } - } else { - // for top-level Text element, background needs to be applied for the entire shadow node - // - // For example: Hello World - // "Hello World" may only occupy e.g. 200 pixels, but the node may be measured at e.g. 500px. - // In this case, we want background to be 500px wide as well, and this is exactly what - // FlatShadowNode does. - super.setBackgroundColor(backgroundColor); - } - } - - @ReactProp(name = ViewProps.FONT_FAMILY) - public void setFontFamily(@Nullable String fontFamily) { - if (!TextUtils.equals(mFontStylingSpan.getFontFamily(), fontFamily)) { - getSpan().setFontFamily(fontFamily); - notifyChanged(true); - } - } - - @ReactProp(name = ViewProps.FONT_WEIGHT) - public void setFontWeight(@Nullable String fontWeightString) { - final int fontWeight; - if (fontWeightString == null) { - fontWeight = -1; - } else if (BOLD.equals(fontWeightString)) { - fontWeight = Typeface.BOLD; - } else if (NORMAL.equals(fontWeightString)) { - fontWeight = Typeface.NORMAL; - } else { - int fontWeightNumeric = parseNumericFontWeight(fontWeightString); - if (fontWeightNumeric == -1) { - throw new RuntimeException("invalid font weight " + fontWeightString); - } - fontWeight = fontWeightNumeric >= 500 ? Typeface.BOLD : Typeface.NORMAL; - } - - if (mFontStylingSpan.getFontWeight() != fontWeight) { - getSpan().setFontWeight(fontWeight); - notifyChanged(true); - } - } - - @ReactProp(name = ViewProps.TEXT_DECORATION_LINE) - public void setTextDecorationLine(@Nullable String textDecorationLineString) { - boolean isUnderlineTextDecorationSet = false; - boolean isLineThroughTextDecorationSet = false; - if (textDecorationLineString != null) { - for (String textDecorationLineSubString : textDecorationLineString.split(" ")) { - if ("underline".equals(textDecorationLineSubString)) { - isUnderlineTextDecorationSet = true; - } else if ("line-through".equals(textDecorationLineSubString)) { - isLineThroughTextDecorationSet = true; - } - } - } - - if (isUnderlineTextDecorationSet != mFontStylingSpan.hasUnderline() || - isLineThroughTextDecorationSet != mFontStylingSpan.hasStrikeThrough()) { - FontStylingSpan span = getSpan(); - span.setHasUnderline(isUnderlineTextDecorationSet); - span.setHasStrikeThrough(isLineThroughTextDecorationSet); - notifyChanged(true); - } - } - - @ReactProp(name = ViewProps.FONT_STYLE) - public void setFontStyle(@Nullable String fontStyleString) { - final int fontStyle; - if (fontStyleString == null) { - fontStyle = -1; - } else if (ITALIC.equals(fontStyleString)) { - fontStyle = Typeface.ITALIC; - } else if (NORMAL.equals(fontStyleString)) { - fontStyle = Typeface.NORMAL; - } else { - throw new RuntimeException("invalid font style " + fontStyleString); - } - - if (mFontStylingSpan.getFontStyle() != fontStyle) { - getSpan().setFontStyle(fontStyle); - notifyChanged(true); - } - } - - @ReactProp(name = PROP_SHADOW_OFFSET) - public void setTextShadowOffset(@Nullable ReadableMap offsetMap) { - float dx = 0; - float dy = 0; - if (offsetMap != null) { - if (offsetMap.hasKey("width")) { - dx = PixelUtil.toPixelFromDIP(offsetMap.getDouble("width")); - } - if (offsetMap.hasKey("height")) { - dy = PixelUtil.toPixelFromDIP(offsetMap.getDouble("height")); - } - } - - if (!mShadowStyleSpan.offsetMatches(dx, dy)) { - getShadowSpan().setOffset(dx, dy); - notifyChanged(false); - } - } - - @ReactProp(name = PROP_SHADOW_RADIUS) - public void setTextShadowRadius(float textShadowRadius) { - textShadowRadius = PixelUtil.toPixelFromDIP(textShadowRadius); - if (mShadowStyleSpan.getRadius() != textShadowRadius) { - getShadowSpan().setRadius(textShadowRadius); - notifyChanged(false); - } - } - - @ReactProp(name = PROP_SHADOW_COLOR, defaultInt = DEFAULT_TEXT_SHADOW_COLOR, customType = "Color") - public void setTextShadowColor(int textShadowColor) { - if (mShadowStyleSpan.getColor() != textShadowColor) { - getShadowSpan().setColor(textShadowColor); - notifyChanged(false); - } - } - - /** - * Returns font size for this node. - * When called on RCTText, this value is never -1 (unset). - */ - protected final int getFontSize() { - return mFontStylingSpan.getFontSize(); - } - /** - * Returns font style for this node. - */ - protected final int getFontStyle() { - int style = mFontStylingSpan.getFontStyle(); - return style >= 0 ? style : Typeface.NORMAL; - } - - protected int getDefaultFontSize() { - return -1; - } - - /* package */ static int fontSizeFromSp(float sp) { - return (int) Math.ceil(PixelUtil.toPixelFromSP(sp)); - } - - protected final FontStylingSpan getSpan() { - if (mFontStylingSpan.isFrozen()) { - mFontStylingSpan = mFontStylingSpan.mutableCopy(); - } - return mFontStylingSpan; - } - - /** - * Returns a new SpannableStringBuilder that includes all the text and styling information to - * create the Layout. - */ - /* package */ final SpannableStringBuilder getText() { - SpannableStringBuilder sb = new SpannableStringBuilder(); - collectText(sb); - applySpans(sb, isEditable()); - return sb; - } - - private final ShadowStyleSpan getShadowSpan() { - if (mShadowStyleSpan.isFrozen()) { - mShadowStyleSpan = mShadowStyleSpan.mutableCopy(); - } - return mShadowStyleSpan; - } - - /** - * Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise - * return the weight. - */ - private static int parseNumericFontWeight(String fontWeightString) { - // This should be much faster than using regex to verify input and Integer.parseInt - return fontWeightString.length() == 3 && fontWeightString.endsWith("00") - && fontWeightString.charAt(0) <= '9' && fontWeightString.charAt(0) >= '1' ? - 100 * (fontWeightString.charAt(0) - '0') : -1; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTVirtualTextManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTVirtualTextManager.java deleted file mode 100644 index 0c1ff3fd9..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTVirtualTextManager.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -/** - * ViewManager that creates instances of RCTVirtualText. - */ -public final class RCTVirtualTextManager extends VirtualViewManager { - - /* package */ static final String REACT_CLASS = "RCTVirtualText"; - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - public RCTVirtualText createShadowNodeInstance() { - return new RCTVirtualText(); - } - - @Override - public Class getShadowNodeClass() { - return RCTVirtualText.class; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/README.md b/ReactAndroid/src/main/java/com/facebook/react/flat/README.md deleted file mode 100644 index f9fc580c0..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Nodes - -Nodes is an experimental, alternate version of -[UIImplementation](https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java) for ReactNative on Android. It has two main advantages over the existing `UIImplementation`: - -1. Support for `overflow:visible` on Android. -2. More efficient generation of view hierarchies. - -The intention is to ultimately replace the existing `UIImplementation` on -Android with Nodes (after all the issues are ironed out). - -## How to test - -In a subclass of `ReactNativeHost`, add this: - -```java -@Override -protected UIImplementationProvider getUIImplementationProvider() { - return new FlatUIImplementationProvider(); -} -``` - -## How it Works - -The existing -[UIImplementation](https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java) maps all non-layout tags to `View`s (resulting in an almost 1:1 mapping of tags -to Views, with the exception of some optimizations for layout only tags that -don't draw content). Nodes, on the other hand, maps react tags to a set of -`DrawCommand`s. In other words, an `` tag will often be mapped to a -`Drawable` instead of an `ImageView` and a `` tag will be mapped to a -`Layout` instead of a `TextView`. This helps flatten the resulting `View` -hierarchy. - -There are situations where `DrawCommand`s are promoted to `View`s: - -1. Existing Android components that are wrapped by React Native (for example, -`ViewPager`, `ScrollView`, etc). -2. When using a `View` is more optimal (for example, `opacity`, to avoid -unnecessary invalidations). -3. To facilitate the implementation of certain features (accessibility, -transforms, etc). - -This means that existing custom `ViewManager`s should continue to work as they -did with the existing `UIImplementation`. - -## Limitations and Known Issues - -- `LayoutAnimation`s are not yet supported -- `zIndex` is not yet supported diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/ShadowStyleSpan.java b/ReactAndroid/src/main/java/com/facebook/react/flat/ShadowStyleSpan.java deleted file mode 100644 index a0435b409..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/ShadowStyleSpan.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.text.TextPaint; -import android.text.style.CharacterStyle; - -/* package */ final class ShadowStyleSpan extends CharacterStyle { - - /* package */ static final ShadowStyleSpan INSTANCE = new ShadowStyleSpan(0, 0, 0, 0, true); - - private float mDx; - private float mDy; - private float mRadius; - private int mColor; - private boolean mFrozen; - - private ShadowStyleSpan(float dx, float dy, float radius, int color, boolean frozen) { - mDx = dx; - mDy = dy; - mRadius = radius; - mColor = color; - mFrozen = frozen; - } - - public boolean offsetMatches(float dx, float dy) { - return mDx == dx && mDy == dy; - } - - public void setOffset(float dx, float dy) { - mDx = dx; - mDy = dy; - } - - public float getRadius() { - return mRadius; - } - - public void setRadius(float radius) { - mRadius = radius; - } - - public int getColor() { - return mColor; - } - - public void setColor(int color) { - mColor = color; - } - - /* package */ ShadowStyleSpan mutableCopy() { - return new ShadowStyleSpan(mDx, mDy, mRadius, mColor, false); - } - - /* package */ boolean isFrozen() { - return mFrozen; - } - - /* package */ void freeze() { - mFrozen = true; - } - - @Override - public void updateDrawState(TextPaint textPaint) { - textPaint.setShadowLayer(mRadius, mDx, mDy, mColor); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java deleted file mode 100644 index 301a48858..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java +++ /dev/null @@ -1,729 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import java.util.ArrayList; - -import android.util.SparseIntArray; - -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.OnLayoutEvent; -import com.facebook.react.uimanager.Spacing; -import com.facebook.react.uimanager.ReactShadowNode; -import com.facebook.react.uimanager.ReactStylesDiffMap; -import com.facebook.react.uimanager.UIViewOperationQueue; -import com.facebook.react.uimanager.events.EventDispatcher; - -/** - * Shadow node hierarchy by itself cannot display UI, it is only a representation of what UI should - * be from JavaScript perspective. StateBuilder is a helper class that walks the shadow node tree - * and collects information into an operation queue that is run on the UI thread and applied to the - * non-shadow hierarchy of Views that Android can finally display. - */ -/* package */ final class StateBuilder { - /* package */ static final float[] EMPTY_FLOAT_ARRAY = new float[0]; - /* package */ static final SparseIntArray EMPTY_SPARSE_INT = new SparseIntArray(); - - private static final boolean SKIP_UP_TO_DATE_NODES = true; - - // Optimization to avoid re-allocating zero length arrays. - private static final int[] EMPTY_INT_ARRAY = new int[0]; - - private final FlatUIViewOperationQueue mOperationsQueue; - - private final ElementsList mDrawCommands = - new ElementsList<>(DrawCommand.EMPTY_ARRAY); - private final ElementsList mAttachDetachListeners = - new ElementsList<>(AttachDetachListener.EMPTY_ARRAY); - private final ElementsList mNodeRegions = - new ElementsList<>(NodeRegion.EMPTY_ARRAY); - private final ElementsList mNativeChildren = - new ElementsList<>(FlatShadowNode.EMPTY_ARRAY); - - private final ArrayList mViewsToDetachAllChildrenFrom = new ArrayList<>(); - private final ArrayList mViewsToDetach = new ArrayList<>(); - private final ArrayList mViewsToDrop = new ArrayList<>(); - private final ArrayList mParentsForViewsToDrop = new ArrayList<>(); - private final ArrayList mOnLayoutEvents = new ArrayList<>(); - private final ArrayList mUpdateViewBoundsOperations = - new ArrayList<>(); - private final ArrayList mViewManagerCommands = - new ArrayList<>(); - - private @Nullable FlatUIViewOperationQueue.DetachAllChildrenFromViews mDetachAllChildrenFromViews; - - /* package */ StateBuilder(FlatUIViewOperationQueue operationsQueue) { - mOperationsQueue = operationsQueue; - } - - /* package */ FlatUIViewOperationQueue getOperationsQueue() { - return mOperationsQueue; - } - - /** - * Given a root of the laid-out shadow node hierarchy, walks the tree and generates arrays from - * element lists that are mounted in the UI thread to FlatViewGroups to handle drawing, touch, - * and other logic. - */ - /* package */ void applyUpdates(FlatShadowNode node) { - float width = node.getLayoutWidth(); - float height = node.getLayoutHeight(); - float left = node.getLayoutX(); - float top = node.getLayoutY(); - float right = left + width; - float bottom = top + height; - - collectStateForMountableNode( - node, - left, - top, - right, - bottom, - Float.NEGATIVE_INFINITY, - Float.NEGATIVE_INFINITY, - Float.POSITIVE_INFINITY, - Float.POSITIVE_INFINITY); - - updateViewBounds(node, left, top, right, bottom); - } - - /** - * Run after the shadow node hierarchy is updated. Detaches all children from Views that are - * changing their native children, updates views, and dispatches commands before discarding any - * dropped views. - * - * @param eventDispatcher Dispatcher for onLayout events. - */ - void afterUpdateViewHierarchy(EventDispatcher eventDispatcher) { - if (mDetachAllChildrenFromViews != null) { - int[] viewsToDetachAllChildrenFrom = collectViewTags(mViewsToDetachAllChildrenFrom); - mViewsToDetachAllChildrenFrom.clear(); - - mDetachAllChildrenFromViews.setViewsToDetachAllChildrenFrom(viewsToDetachAllChildrenFrom); - mDetachAllChildrenFromViews = null; - } - - for (int i = 0, size = mUpdateViewBoundsOperations.size(); i != size; ++i) { - mOperationsQueue.enqueueFlatUIOperation(mUpdateViewBoundsOperations.get(i)); - } - mUpdateViewBoundsOperations.clear(); - - // Process view manager commands after bounds operations, so that any UI operations have already - // happened before we actually dispatch the view manager command. This prevents things like - // commands going to empty parents and views not yet being created. - for (int i = 0, size = mViewManagerCommands.size(); i != size; i++) { - mOperationsQueue.enqueueFlatUIOperation(mViewManagerCommands.get(i)); - } - mViewManagerCommands.clear(); - - // This could be more efficient if EventDispatcher had a batch mode - // to avoid multiple synchronized calls. - for (int i = 0, size = mOnLayoutEvents.size(); i != size; ++i) { - eventDispatcher.dispatchEvent(mOnLayoutEvents.get(i)); - } - mOnLayoutEvents.clear(); - - if (mViewsToDrop.size() > 0) { - mOperationsQueue.enqueueDropViews(mViewsToDrop, mParentsForViewsToDrop); - mViewsToDrop.clear(); - mParentsForViewsToDrop.clear(); - } - - mOperationsQueue.enqueueProcessLayoutRequests(); - } - - /* package */ void removeRootView(int rootViewTag) { - // Note root view tags with a negative value. - mViewsToDrop.add(-rootViewTag); - mParentsForViewsToDrop.add(-1); - } - - /** - * Adds a draw command to the element list for the current scope. Allows collectState within the - * shadow node to add commands. - * - * @param drawCommand The draw command to add. - */ - /* package */ void addDrawCommand(AbstractDrawCommand drawCommand) { - mDrawCommands.add(drawCommand); - } - - /** - * Adds a listener to the element list for the current scope. Allows collectState within the - * shadow node to add listeners. - * - * @param listener The listener to add - */ - /* package */ void addAttachDetachListener(AttachDetachListener listener) { - mAttachDetachListeners.add(listener); - } - - /** - * Adds a command for a view manager to the queue. We have to delay adding it to the operations - * queue until we have added our view moves, creations and updates. - * - * @param reactTag The react tag of the command target. - * @param commandId ID of the command. - * @param commandArgs Arguments for the command. - */ - /* package */ void enqueueViewManagerCommand( - int reactTag, - int commandId, - ReadableArray commandArgs) { - mViewManagerCommands.add( - mOperationsQueue.createViewManagerCommand(reactTag, commandId, commandArgs)); - } - - /** - * Create a backing view for a node, or update the backing view if it has already been created. - * - * @param node The node to create the backing view for. - * @param styles Styles for the view. - */ - /* package */ void enqueueCreateOrUpdateView( - FlatShadowNode node, - @Nullable ReactStylesDiffMap styles) { - if (node.isBackingViewCreated()) { - // If the View is already created, make sure to propagate the new styles. - mOperationsQueue.enqueueUpdateProperties( - node.getReactTag(), - node.getViewClass(), - styles); - } else { - mOperationsQueue.enqueueCreateView( - node.getThemedContext(), - node.getReactTag(), - node.getViewClass(), - styles); - - node.signalBackingViewIsCreated(); - } - } - - /** - * Create a backing view for a node if not already created. - * - * @param node The node to create the backing view for. - */ - /* package */ void ensureBackingViewIsCreated(FlatShadowNode node) { - if (node.isBackingViewCreated()) { - return; - } - - int tag = node.getReactTag(); - mOperationsQueue.enqueueCreateView(node.getThemedContext(), tag, node.getViewClass(), null); - - node.signalBackingViewIsCreated(); - } - - /** - * Enqueue dropping of the view for a node that has a backing view. Used in conjunction with - * remove the node from the shadow hierarchy. - * - * @param node The node to drop the backing view for. - */ - /* package */ void dropView(FlatShadowNode node, int parentReactTag) { - mViewsToDrop.add(node.getReactTag()); - mParentsForViewsToDrop.add(parentReactTag); - } - - /** - * Adds a node region to the element list for the current scope. Allows collectState to add - * regions. - * - * @param node The node to add a region for. - * @param left Bound of the region. - * @param top Bound of the region. - * @param right Bound of the region. - * @param bottom Bound of the region. - * @param isVirtual True if the region does not map to a native view. Used to determine touch - * targets. - */ - private void addNodeRegion( - FlatShadowNode node, - float left, - float top, - float right, - float bottom, - boolean isVirtual) { - if (left == right || top == bottom) { - // no point in adding an empty NodeRegion - return; - } - - node.updateNodeRegion(left, top, right, bottom, isVirtual); - if (node.doesDraw()) { - mNodeRegions.add(node.getNodeRegion()); - } - } - - /** - * Adds a native child to the element list for the current scope. Allows collectState to add - * native children. - * - * @param nativeChild The view-backed native child to add. - */ - private void addNativeChild(FlatShadowNode nativeChild) { - mNativeChildren.add(nativeChild); - } - - /** - * Updates boundaries of a View that a give nodes maps to. - */ - private void updateViewBounds( - FlatShadowNode node, - float left, - float top, - float right, - float bottom) { - int viewLeft = Math.round(left); - int viewTop = Math.round(top); - int viewRight = Math.round(right); - int viewBottom = Math.round(bottom); - if (node.getViewLeft() == viewLeft && node.getViewTop() == viewTop && - node.getViewRight() == viewRight && node.getViewBottom() == viewBottom) { - // nothing changed. - return; - } - - // this will optionally measure and layout the View this node maps to. - node.setViewBounds(viewLeft, viewTop, viewRight, viewBottom); - int tag = node.getReactTag(); - - mUpdateViewBoundsOperations.add( - mOperationsQueue.createUpdateViewBounds(tag, viewLeft, viewTop, viewRight, viewBottom)); - } - - /** - * Collects state (Draw commands, listeners, regions, native children) for a given node that will - * mount to a View. Returns true if this node or any of its descendants that mount to View - * generated any updates. - */ - private boolean collectStateForMountableNode( - FlatShadowNode node, - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom) { - boolean hasUpdates = node.hasNewLayout(); - - boolean expectingUpdate = hasUpdates || node.isUpdated() || node.hasUnseenUpdates() || - node.clipBoundsChanged(clipLeft, clipTop, clipRight, clipBottom); - if (SKIP_UP_TO_DATE_NODES && !expectingUpdate) { - return false; - } - - node.setClipBounds(clipLeft, clipTop, clipRight, clipBottom); - - mDrawCommands.start(node.getDrawCommands()); - mAttachDetachListeners.start(node.getAttachDetachListeners()); - mNodeRegions.start(node.getNodeRegions()); - mNativeChildren.start(node.getNativeChildren()); - - boolean isAndroidView = false; - boolean needsCustomLayoutForChildren = false; - if (node instanceof AndroidView) { - AndroidView androidView = (AndroidView) node; - updateViewPadding(androidView, node.getReactTag()); - - isAndroidView = true; - needsCustomLayoutForChildren = androidView.needsCustomLayoutForChildren(); - - // AndroidView might scroll (e.g. ScrollView) so we need to reset clip bounds here - // Otherwise, we might scroll clipped content. If AndroidView doesn't scroll, this is still - // harmless, because AndroidView will do its own clipping anyway. - clipLeft = Float.NEGATIVE_INFINITY; - clipTop = Float.NEGATIVE_INFINITY; - clipRight = Float.POSITIVE_INFINITY; - clipBottom = Float.POSITIVE_INFINITY; - } - - if (!isAndroidView && node.isVirtualAnchor()) { - // If RCTText is mounted to View, virtual children will not receive any touch events - // because they don't get added to nodeRegions, so nodeRegions will be empty and - // FlatViewGroup.reactTagForTouch() will always return RCTText's id. To fix the issue, - // manually add nodeRegion so it will have exactly one NodeRegion, and virtual nodes will - // be able to receive touch events. - addNodeRegion(node, left, top, right, bottom, true); - } - - boolean descendantUpdated = collectStateRecursively( - node, - left, - top, - right, - bottom, - clipLeft, - clipTop, - clipRight, - clipBottom, - isAndroidView, - needsCustomLayoutForChildren); - - boolean shouldUpdateMountState = false; - final DrawCommand[] drawCommands = mDrawCommands.finish(); - if (drawCommands != null) { - shouldUpdateMountState = true; - node.setDrawCommands(drawCommands); - } - - final AttachDetachListener[] listeners = mAttachDetachListeners.finish(); - if (listeners != null) { - shouldUpdateMountState = true; - node.setAttachDetachListeners(listeners); - } - - final NodeRegion[] nodeRegions = mNodeRegions.finish(); - if (nodeRegions != null) { - shouldUpdateMountState = true; - node.setNodeRegions(nodeRegions); - } else if (descendantUpdated) { - // one of the descendant's value for overflows container may have changed, so - // we still need to update ours. - node.updateOverflowsContainer(); - } - - // We need to finish the native children so that we can process clipping FlatViewGroup. - final FlatShadowNode[] nativeChildren = mNativeChildren.finish(); - if (shouldUpdateMountState) { - if (node.clipsSubviews()) { - // Node is a clipping FlatViewGroup, so lets do some calculations off the UI thread. - // DrawCommandManager has a better explanation of the data incoming from these calculations, - // and is where they are actually used. - float[] commandMaxBottom = EMPTY_FLOAT_ARRAY; - float[] commandMinTop = EMPTY_FLOAT_ARRAY; - SparseIntArray drawViewIndexMap = EMPTY_SPARSE_INT; - if (drawCommands != null) { - drawViewIndexMap = new SparseIntArray(); - - commandMaxBottom = new float[drawCommands.length]; - commandMinTop = new float[drawCommands.length]; - - if (node.isHorizontal()) { - HorizontalDrawCommandManager - .fillMaxMinArrays(drawCommands, commandMaxBottom, commandMinTop, drawViewIndexMap); - } else { - VerticalDrawCommandManager - .fillMaxMinArrays(drawCommands, commandMaxBottom, commandMinTop, drawViewIndexMap); - } - } - float[] regionMaxBottom = EMPTY_FLOAT_ARRAY; - float[] regionMinTop = EMPTY_FLOAT_ARRAY; - if (nodeRegions != null) { - regionMaxBottom = new float[nodeRegions.length]; - regionMinTop = new float[nodeRegions.length]; - - if (node.isHorizontal()) { - HorizontalDrawCommandManager - .fillMaxMinArrays(nodeRegions, regionMaxBottom, regionMinTop); - } else { - VerticalDrawCommandManager - .fillMaxMinArrays(nodeRegions, regionMaxBottom, regionMinTop); - } - } - - boolean willMountViews = nativeChildren != null; - mOperationsQueue.enqueueUpdateClippingMountState( - node.getReactTag(), - drawCommands, - drawViewIndexMap, - commandMaxBottom, - commandMinTop, - listeners, - nodeRegions, - regionMaxBottom, - regionMinTop, - willMountViews); - } else { - mOperationsQueue.enqueueUpdateMountState( - node.getReactTag(), - drawCommands, - listeners, - nodeRegions); - } - } - - if (node.hasUnseenUpdates()) { - node.onCollectExtraUpdates(mOperationsQueue); - node.markUpdateSeen(); - } - - if (nativeChildren != null) { - updateNativeChildren(node, node.getNativeChildren(), nativeChildren); - } - - boolean updated = shouldUpdateMountState || nativeChildren != null || descendantUpdated; - - if (!expectingUpdate && updated) { - throw new RuntimeException("Node " + node.getReactTag() + " updated unexpectedly."); - } - - return updated; - } - - /** - * Handles updating the children of a node when they change. Updates the shadow node and - * enqueues state updates that will eventually be run on the UI thread. - * - * @param node The node to update native children for. - * @param oldNativeChildren The previously mounted native children. - * @param newNativeChildren The newly mounted native children. - */ - private void updateNativeChildren( - FlatShadowNode node, - FlatShadowNode[] oldNativeChildren, - FlatShadowNode[] newNativeChildren) { - - node.setNativeChildren(newNativeChildren); - - if (mDetachAllChildrenFromViews == null) { - mDetachAllChildrenFromViews = mOperationsQueue.enqueueDetachAllChildrenFromViews(); - } - - if (oldNativeChildren.length != 0) { - mViewsToDetachAllChildrenFrom.add(node); - } - - int tag = node.getReactTag(); - int numViewsToAdd = newNativeChildren.length; - final int[] viewsToAdd; - if (numViewsToAdd == 0) { - viewsToAdd = EMPTY_INT_ARRAY; - } else { - viewsToAdd = new int[numViewsToAdd]; - int i = 0; - for (FlatShadowNode child : newNativeChildren) { - if (child.getNativeParentTag() == tag) { - viewsToAdd[i] = -child.getReactTag(); - } else { - viewsToAdd[i] = child.getReactTag(); - } - // all views we add are first start detached - child.setNativeParentTag(-1); - ++i; - } - } - - // Populate an array of views to detach. - // These views still have their native parent set as opposed to being reset to -1 - for (FlatShadowNode child : oldNativeChildren) { - if (child.getNativeParentTag() == tag) { - // View is attached to old parent and needs to be removed. - mViewsToDetach.add(child); - child.setNativeParentTag(-1); - } - } - - final int[] viewsToDetach = collectViewTags(mViewsToDetach); - mViewsToDetach.clear(); - - // restore correct parent tag - for (FlatShadowNode child : newNativeChildren) { - child.setNativeParentTag(tag); - } - - mOperationsQueue.enqueueUpdateViewGroup(tag, viewsToAdd, viewsToDetach); - } - - /** - * Recursively walks node tree from a given node and collects draw commands, listeners, node - * regions and native children. Calls collect state on the node, then processNodeAndCollectState - * for the recursion. - */ - private boolean collectStateRecursively( - FlatShadowNode node, - float left, - float top, - float right, - float bottom, - float clipLeft, - float clipTop, - float clipRight, - float clipBottom, - boolean isAndroidView, - boolean needsCustomLayoutForChildren) { - if (node.hasNewLayout()) { - node.markLayoutSeen(); - } - - float roundedLeft = roundToPixel(left); - float roundedTop = roundToPixel(top); - float roundedRight = roundToPixel(right); - float roundedBottom = roundToPixel(bottom); - - // notify JS about layout event if requested - if (node.shouldNotifyOnLayout()) { - OnLayoutEvent layoutEvent = node.obtainLayoutEvent( - Math.round(node.getLayoutX()), - Math.round(node.getLayoutY()), - (int) (roundedRight - roundedLeft), - (int) (roundedBottom - roundedTop)); - if (layoutEvent != null) { - mOnLayoutEvents.add(layoutEvent); - } - } - - if (node.clipToBounds()) { - clipLeft = Math.max(left, clipLeft); - clipTop = Math.max(top, clipTop); - clipRight = Math.min(right, clipRight); - clipBottom = Math.min(bottom, clipBottom); - } - - node.collectState( - this, - roundedLeft, - roundedTop, - roundedRight, - roundedBottom, - roundToPixel(clipLeft), - roundToPixel(clipTop), - roundToPixel(clipRight), - clipBottom); - - boolean updated = false; - for (int i = 0, childCount = node.getChildCount(); i != childCount; ++i) { - ReactShadowNode child = node.getChildAt(i); - if (child.isVirtual()) { - continue; - } - - updated |= processNodeAndCollectState( - (FlatShadowNode) child, - left, - top, - clipLeft, - clipTop, - clipRight, - clipBottom, - isAndroidView, - needsCustomLayoutForChildren); - } - - node.resetUpdated(); - - return updated; - } - - /** - * Collects state and enqueues View boundary updates for a given node tree. Returns true if - * this node or any of its descendants that mount to View generated any updates. - */ - private boolean processNodeAndCollectState( - FlatShadowNode node, - float parentLeft, - float parentTop, - float parentClipLeft, - float parentClipTop, - float parentClipRight, - float parentClipBottom, - boolean parentIsAndroidView, - boolean needsCustomLayout) { - float width = node.getLayoutWidth(); - float height = node.getLayoutHeight(); - - float left = parentLeft + node.getLayoutX(); - float top = parentTop + node.getLayoutY(); - float right = left + width; - float bottom = top + height; - - boolean mountsToView = node.mountsToView(); - - final boolean updated; - - if (!parentIsAndroidView) { - addNodeRegion(node, left, top, right, bottom, !mountsToView); - } - - if (mountsToView) { - ensureBackingViewIsCreated(node); - - addNativeChild(node); - updated = collectStateForMountableNode( - node, - 0, // left - left - 0, // top - top - right - left, - bottom - top, - parentClipLeft - left, - parentClipTop - top, - parentClipRight - left, - parentClipBottom - top); - - if (!parentIsAndroidView) { - mDrawCommands.add(node.collectDrawView( - left, - top, - right, - bottom, - parentClipLeft, - parentClipTop, - parentClipRight, - parentClipBottom)); - } - - if (!needsCustomLayout) { - updateViewBounds(node, left, top, right, bottom); - } - } else { - updated = collectStateRecursively( - node, - left, - top, - right, - bottom, - parentClipLeft, - parentClipTop, - parentClipRight, - parentClipBottom, - false, - false); - } - - return updated; - } - - private void updateViewPadding(AndroidView androidView, int reactTag) { - if (androidView.isPaddingChanged()) { - mOperationsQueue.enqueueSetPadding( - reactTag, - Math.round(androidView.getPadding(Spacing.LEFT)), - Math.round(androidView.getPadding(Spacing.TOP)), - Math.round(androidView.getPadding(Spacing.RIGHT)), - Math.round(androidView.getPadding(Spacing.BOTTOM))); - androidView.resetPaddingChanged(); - } - } - - private static int[] collectViewTags(ArrayList views) { - int numViews = views.size(); - if (numViews == 0) { - return EMPTY_INT_ARRAY; - } - - int[] viewTags = new int[numViews]; - for (int i = 0; i < numViews; ++i) { - viewTags[i] = views.get(i).getReactTag(); - } - - return viewTags; - } - - /** - * This is what Math.round() does, except it returns float. - */ - private static float roundToPixel(float pos) { - return (float) Math.floor(pos + 0.5f); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/TextNodeRegion.java b/ReactAndroid/src/main/java/com/facebook/react/flat/TextNodeRegion.java deleted file mode 100644 index 9440ebc08..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/TextNodeRegion.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import android.text.Layout; -import android.text.Spanned; - -/* package */ final class TextNodeRegion extends NodeRegion { - private @Nullable Layout mLayout; - - /* package */ TextNodeRegion( - float left, - float top, - float right, - float bottom, - int tag, - boolean isVirtual, - @Nullable Layout layout) { - super(left, top, right, bottom, tag, isVirtual); - mLayout = layout; - } - - public void setLayout(Layout layout) { - mLayout = layout; - } - - /* package */ @Nullable Layout getLayout() { - return mLayout; - } - - /* package */ int getReactTag(float touchX, float touchY) { - if (mLayout != null) { - CharSequence text = mLayout.getText(); - if (text instanceof Spanned) { - int y = Math.round(touchY - getTop()); - if (y >= mLayout.getLineTop(0) && y < mLayout.getLineBottom(mLayout.getLineCount() - 1)) { - float x = Math.round(touchX - getLeft()); - int line = mLayout.getLineForVertical(y); - - if (mLayout.getLineLeft(line) <= x && x <= mLayout.getLineRight(line)) { - int off = mLayout.getOffsetForHorizontal(line, x); - - Spanned spanned = (Spanned) text; - RCTRawText[] link = spanned.getSpans(off, off, RCTRawText.class); - - if (link.length != 0) { - return link[0].getReactTag(); - } - } - } - } - } - - return super.getReactTag(touchX, touchY); - } - - @Override - boolean matchesTag(int tag) { - if (super.matchesTag(tag)) { - return true; - } - - if (mLayout != null) { - CharSequence text = mLayout.getText(); - if (text instanceof Spanned) { - Spanned spannedText = (Spanned) text; - RCTRawText[] spans = spannedText.getSpans(0, text.length(), RCTRawText.class); - for (RCTRawText span : spans) { - if (span.getReactTag() == tag) { - return true; - } - } - } - } - return false; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/TypefaceCache.java b/ReactAndroid/src/main/java/com/facebook/react/flat/TypefaceCache.java deleted file mode 100644 index c684af4ad..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/TypefaceCache.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import javax.annotation.Nullable; - -import java.util.HashMap; - -import android.content.res.AssetManager; -import android.graphics.Typeface; - -import com.facebook.infer.annotation.Assertions; - -/** - * TypefaceCache provides methods to resolve typeface from font family, or existing typeface - * with a different style. - */ -/* package */ final class TypefaceCache { - - private static final int MAX_STYLES = 4; // NORMAL = 0, BOLD = 1, ITALIC = 2, BOLD_ITALIC = 3 - - private static final HashMap FONTFAMILY_CACHE = new HashMap<>(); - private static final HashMap TYPEFACE_CACHE = new HashMap<>(); - - private static final String[] EXTENSIONS = { - "", - "_bold", - "_italic", - "_bold_italic"}; - private static final String[] FILE_EXTENSIONS = {".ttf", ".otf"}; - private static final String FONTS_ASSET_PATH = "fonts/"; - - @Nullable private static AssetManager sAssetManager = null; - - public static void setAssetManager(AssetManager assetManager) { - sAssetManager = assetManager; - } - - /** - * Returns a Typeface for a given a FontFamily and style. - */ - public static Typeface getTypeface(String fontFamily, int style) { - Typeface[] cache = FONTFAMILY_CACHE.get(fontFamily); - if (cache == null) { - // cache is empty, create one. - cache = new Typeface[MAX_STYLES]; - FONTFAMILY_CACHE.put(fontFamily, cache); - } else if (cache[style] != null) { - // return cached value. - return cache[style]; - } - - Typeface typeface = createTypeface(fontFamily, style); - cache[style] = typeface; - TYPEFACE_CACHE.put(typeface, cache); - return typeface; - } - - private static Typeface createTypeface(String fontFamilyName, int style) { - String extension = EXTENSIONS[style]; - StringBuilder fileNameBuffer = new StringBuilder(32) - .append(FONTS_ASSET_PATH) - .append(fontFamilyName) - .append(extension); - int length = fileNameBuffer.length(); - for (String fileExtension : FILE_EXTENSIONS) { - String fileName = fileNameBuffer.append(fileExtension).toString(); - try { - return Typeface.createFromAsset(sAssetManager, fileName); - } catch (RuntimeException e) { - // unfortunately Typeface.createFromAsset throws an exception instead of returning null - // if the typeface doesn't exist - fileNameBuffer.setLength(length); - } - } - return Assertions.assumeNotNull(Typeface.create(fontFamilyName, style)); - } - - /** - * Returns a derivative of a given Typeface with a different style. - */ - public static Typeface getTypeface(Typeface typeface, int style) { - if (typeface == null) { - return Typeface.defaultFromStyle(style); - } - - Typeface[] cache = TYPEFACE_CACHE.get(typeface); - if (cache == null) { - // This should not happen because all Typefaces are coming from TypefaceCache, - // and thus should be registered in TYPEFACE_CACHE. - // If we get here, it's a bug and one of the 2 scenarios happened: - // a) TypefaceCache created a Typeface and didn't put it into TYPEFACE_CACHE. - // b) someone else created a Typeface bypassing TypefaceCache so it's not registered here. - // - // If it's not registered, we can just register it manually for consistency, and so that - // next time someone requests a un unknown Typeface, it's already cached and we don't create - // extra copies. - cache = new Typeface[MAX_STYLES]; - cache[typeface.getStyle()] = typeface; - } else if (cache[style] != null) { - // return cached value. - return cache[style]; - } - - typeface = Typeface.create(typeface, style); - cache[style] = typeface; - TYPEFACE_CACHE.put(typeface, cache); - return typeface; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/VerticalDrawCommandManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/VerticalDrawCommandManager.java deleted file mode 100644 index 9cf730fcb..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/VerticalDrawCommandManager.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import java.util.Arrays; - -import android.util.SparseIntArray; - -/** - * {@link DrawCommandManager} with vertical clipping (The view scrolls up and down). - */ -/* package */ final class VerticalDrawCommandManager extends ClippingDrawCommandManager { - - /* package */ VerticalDrawCommandManager( - FlatViewGroup flatViewGroup, - DrawCommand[] drawCommands) { - super(flatViewGroup, drawCommands); - } - - @Override - int commandStartIndex() { - int start = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.top); - // We don't care whether we matched or not, but positive indices are helpful. The binary search - // returns ~index in the case that it isn't a match, so reverse that here. - return start < 0 ? ~start : start; - } - - @Override - int commandStopIndex(int start) { - int stop = Arrays.binarySearch( - mCommandMinTop, - start, - mCommandMinTop.length, - mClippingRect.bottom); - // We don't care whether we matched or not, but positive indices are helpful. The binary search - // returns ~index in the case that it isn't a match, so reverse that here. - return stop < 0 ? ~stop : stop; - } - - @Override - int regionStopIndex(float touchX, float touchY) { - int stop = Arrays.binarySearch(mRegionMinTop, touchY + 0.0001f); - // We don't care whether we matched or not, but positive indices are helpful. The binary search - // returns ~index in the case that it isn't a match, so reverse that here. - return stop < 0 ? ~stop : stop; - } - - @Override - boolean regionAboveTouch(int index, float touchX, float touchY) { - return mRegionMaxBottom[index] < touchY; - } - - /** - * Populates the max and min arrays for a given set of node regions. - * - * This should never be called from the UI thread, as the reason it exists is to do work off the - * UI thread. - * - * @param regions The regions that will eventually be mounted. - * @param maxBottom At each index i, the maximum bottom value of all regions at or below i. - * @param minTop At each index i, the minimum top value of all regions at or below i. - */ - public static void fillMaxMinArrays(NodeRegion[] regions, float[] maxBottom, float[] minTop) { - float last = 0; - for (int i = 0; i < regions.length; i++) { - last = Math.max(last, regions[i].getTouchableBottom()); - maxBottom[i] = last; - } - for (int i = regions.length - 1; i >= 0; i--) { - last = Math.min(last, regions[i].getTouchableTop()); - minTop[i] = last; - } - } - - /** - * Populates the max and min arrays for a given set of draw commands. Also populates a mapping of - * react tags to their index position in the command array. - * - * This should never be called from the UI thread, as the reason it exists is to do work off the - * UI thread. - * - * @param commands The draw commands that will eventually be mounted. - * @param maxBottom At each index i, the maximum bottom value of all draw commands at or below i. - * @param minTop At each index i, the minimum top value of all draw commands at or below i. - * @param drawViewIndexMap Mapping of ids to index position within the draw command array. - */ - public static void fillMaxMinArrays( - DrawCommand[] commands, - float[] maxBottom, - float[] minTop, - SparseIntArray drawViewIndexMap) { - float last = 0; - // Loop through the DrawCommands, keeping track of the maximum we've seen if we only iterated - // through items up to this position. - for (int i = 0; i < commands.length; i++) { - if (commands[i] instanceof DrawView) { - DrawView drawView = (DrawView) commands[i]; - // These will generally be roughly sorted by id, so try to insert at the end if possible. - drawViewIndexMap.append(drawView.reactTag, i); - last = Math.max(last, drawView.mLogicalBottom); - } else { - last = Math.max(last, commands[i].getBottom()); - } - maxBottom[i] = last; - } - // Intentionally leave last as it was, since it's at the maximum bottom position we've seen so - // far, we can use it again. - - // Loop through backwards, keeping track of the minimum we've seen at this position. - for (int i = commands.length - 1; i >= 0; i--) { - if (commands[i] instanceof DrawView) { - last = Math.min(last, ((DrawView) commands[i]).mLogicalTop); - } else { - last = Math.min(last, commands[i].getTop()); - } - minTop[i] = last; - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/ViewResolver.java b/ReactAndroid/src/main/java/com/facebook/react/flat/ViewResolver.java deleted file mode 100644 index 1d742ec8f..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/ViewResolver.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.view.View; - -public interface ViewResolver { - public View getView(int tag); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/VirtualViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/VirtualViewManager.java deleted file mode 100644 index dc382fea1..000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/VirtualViewManager.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.flat; - -import android.view.View; - -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewManager; - -/** - * Base class to ViewManagers that don't map to a View. - */ -abstract class VirtualViewManager extends ViewManager { - @Override - protected View createViewInstance(ThemedReactContext reactContext) { - throw new RuntimeException(getName() + " doesn't map to a View"); - } - - @Override - public void updateExtraData(View root, Object extraData) { - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK index 93d4cab86..c7293b6cd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK @@ -19,7 +19,6 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/devsupport:devsupport"), - react_native_target("java/com/facebook/react/flat:flat"), react_native_target("java/com/facebook/react/module/model:model"), react_native_target("java/com/facebook/react/modules/accessibilityinfo:accessibilityinfo"), react_native_target("java/com/facebook/react/modules/appstate:appstate"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index aa8193d08..5e1ebd5c3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -7,23 +7,11 @@ package com.facebook.react.shell; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import com.facebook.react.LazyReactPackage; import com.facebook.react.animated.NativeAnimatedModule; import com.facebook.react.bridge.ModuleSpec; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.flat.FlatARTSurfaceViewManager; -import com.facebook.react.flat.RCTImageViewManager; -import com.facebook.react.flat.RCTModalHostManager; -import com.facebook.react.flat.RCTRawTextManager; -import com.facebook.react.flat.RCTTextInlineImageManager; -import com.facebook.react.flat.RCTTextInputManager; -import com.facebook.react.flat.RCTTextManager; -import com.facebook.react.flat.RCTViewManager; -import com.facebook.react.flat.RCTViewPagerManager; -import com.facebook.react.flat.RCTVirtualTextManager; import com.facebook.react.module.model.ReactModuleInfoProvider; import com.facebook.react.modules.accessibilityinfo.AccessibilityInfoModule; import com.facebook.react.modules.appstate.AppStateModule; @@ -332,33 +320,17 @@ public class MainReactPackage extends LazyReactPackage { viewManagers.add(new ReactWebViewManager()); viewManagers.add(new SwipeRefreshLayoutManager()); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(reactContext); - boolean useFlatUi = preferences.getBoolean("flat_uiimplementation", false); - if (useFlatUi) { - // Flat managers - viewManagers.add(new FlatARTSurfaceViewManager()); - viewManagers.add(new RCTTextInlineImageManager()); - viewManagers.add(new RCTImageViewManager()); - viewManagers.add(new RCTModalHostManager()); - viewManagers.add(new RCTRawTextManager()); - viewManagers.add(new RCTTextInputManager()); - viewManagers.add(new RCTTextManager()); - viewManagers.add(new RCTViewManager()); - viewManagers.add(new RCTViewPagerManager()); - viewManagers.add(new RCTVirtualTextManager()); - } else { - // Native equivalents - viewManagers.add(new ARTSurfaceViewManager()); - viewManagers.add(new FrescoBasedReactTextInlineImageViewManager()); - viewManagers.add(new ReactImageManager()); - viewManagers.add(new ReactModalHostManager()); - viewManagers.add(new ReactRawTextManager()); - viewManagers.add(new ReactTextInputManager()); - viewManagers.add(new ReactTextViewManager()); - viewManagers.add(new ReactViewManager()); - viewManagers.add(new ReactViewPagerManager()); - viewManagers.add(new ReactVirtualTextViewManager()); - } + // Native equivalents + viewManagers.add(new ARTSurfaceViewManager()); + viewManagers.add(new FrescoBasedReactTextInlineImageViewManager()); + viewManagers.add(new ReactImageManager()); + viewManagers.add(new ReactModalHostManager()); + viewManagers.add(new ReactRawTextManager()); + viewManagers.add(new ReactTextInputManager()); + viewManagers.add(new ReactTextViewManager()); + viewManagers.add(new ReactViewManager()); + viewManagers.add(new ReactViewPagerManager()); + viewManagers.add(new ReactVirtualTextViewManager()); return viewManagers; }