diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImageWithDrawee.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImageWithDrawee.java new file mode 100644 index 000000000..b1cf59541 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawImageWithDrawee.java @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.flat; + +import javax.annotation.Nullable; + +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; + +import com.facebook.drawee.drawable.ScalingUtils.ScaleType; +import com.facebook.drawee.generic.GenericDraweeHierarchy; +import com.facebook.drawee.generic.RoundingParams; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.views.image.ImageResizeMode; + +/** + * DrawImageWithDrawee is 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 { + + private @Nullable DraweeRequestHelper mRequestHelper; + private @Nullable PorterDuffColorFilter mColorFilter; + private ScaleType mScaleType = ImageResizeMode.defaultValue(); + private float mBorderWidth; + private float mBorderRadius; + private int mBorderColor; + + @Override + public boolean hasImageRequest() { + return mRequestHelper != null; + } + + @Override + public void setImageRequest(@Nullable ImageRequest imageRequest) { + if (imageRequest == null) { + mRequestHelper = null; + } else { + mRequestHelper = new DraweeRequestHelper(imageRequest); + } + } + + @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 onDraw(Canvas canvas) { + Assertions.assumeNotNull(mRequestHelper).getDrawable().draw(canvas); + } + + @Override + public void onAttached(FlatViewGroup.InvalidateCallback callback) { + GenericDraweeHierarchy hierarchy = Assertions.assumeNotNull(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.getTopLevelDrawable().setBounds( + Math.round(getLeft()), + Math.round(getTop()), + Math.round(getRight()), + Math.round(getBottom())); + + mRequestHelper.attach(callback); + } + + @Override + public void onDetached() { + Assertions.assumeNotNull(mRequestHelper).detach(); + } + + private boolean shouldDisplayBorder() { + return mBorderColor != 0 || mBorderRadius >= 0.5f; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DraweeRequestHelper.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DraweeRequestHelper.java new file mode 100644 index 000000000..1b1c93d26 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/DraweeRequestHelper.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.flat; + + +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + +import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; +import com.facebook.drawee.generic.GenericDraweeHierarchy; +import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; +import com.facebook.drawee.interfaces.DraweeController; +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) { + DraweeController controller = sControllerBuilder + .setImageRequest(imageRequest) + .setCallerContext(RCTImageView.getCallerContext()) + .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/FlatUIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java index 547d0031c..95093cc96 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java @@ -43,13 +43,6 @@ public class FlatUIImplementation extends UIImplementation { } }; - /** - * Temporary storage for elements that need to be moved within a parent. - * Only used inside #manageChildren() and always empty outside of it. - */ - private final ArrayList mNodesToMove = new ArrayList<>(); - private final StateBuilder mStateBuilder; - public static FlatUIImplementation createInstance( ReactApplicationContext reactContext, List viewManagers) { @@ -61,6 +54,7 @@ public class FlatUIImplementation extends UIImplementation { RCTImageView.setCallerContext(callerContext); } } + DraweeRequestHelper.setResources(reactContext.getResources()); TypefaceCache.setAssetManager(reactContext.getAssets()); @@ -78,18 +72,38 @@ public class FlatUIImplementation extends UIImplementation { FlatUIViewOperationQueue operationsQueue = new FlatUIViewOperationQueue( reactContext, nativeViewHierarchyManager); - return new FlatUIImplementation(viewManagerRegistry, operationsQueue); + return new FlatUIImplementation(reactImageManager, viewManagerRegistry, operationsQueue); } + /** + * Temporary storage for elements that need to be moved within a parent. + * Only used inside #manageChildren() and always empty outside of it. + */ + private final ArrayList mNodesToMove = new ArrayList<>(); + private final StateBuilder mStateBuilder; + private @Nullable ReactImageManager mReactImageManager; + private FlatUIImplementation( + @Nullable ReactImageManager reactImageManager, ViewManagerRegistry viewManagers, FlatUIViewOperationQueue operationsQueue) { super(viewManagers, operationsQueue); mStateBuilder = new StateBuilder(operationsQueue); + mReactImageManager = reactImageManager; } @Override protected ReactShadowNode createRootShadowNode() { + if (mReactImageManager != 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 initalized and DraweeControllerBuilder can be queried. This also happens + // relatively rarely to have any performance considerations. + DraweeRequestHelper.setDraweeControllerBuilder( + mReactImageManager.getDraweeControllerBuilder()); + mReactImageManager = null; + } + return new FlatRootShadowNode(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java index 6dca210ce..3bbb88da4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java @@ -154,7 +154,7 @@ import com.facebook.react.uimanager.ReactPointerEventsView; @Override protected boolean verifyDrawable(Drawable who) { - return who == mHotspot || super.verifyDrawable(who); + return true; } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageViewManager.java index eb7a38e22..f8a54bd26 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTImageViewManager.java @@ -11,6 +11,8 @@ package com.facebook.react.flat; /* package */ final class RCTImageViewManager extends FlatViewManager { + private static final boolean USE_IMAGEPIPELINE_DIRECTLY = false; + @Override public String getName() { return "RCTImageView"; @@ -18,7 +20,11 @@ package com.facebook.react.flat; @Override public RCTImageView createShadowNodeInstance() { - return new RCTImageView(new DrawImageWithPipeline()); + if (USE_IMAGEPIPELINE_DIRECTLY) { + return new RCTImageView(new DrawImageWithPipeline()); + } else { + return new RCTImageView(new DrawImageWithDrawee()); + } } @Override