Use clipPath for rounding corners on JB/KK
We lose antialiasing, as well as 100% rounded corners on JB, but gain huge wins in memory and CPU.
Future work could fix the 100% rounded corners if we want to import a bunch of very complex path generation logic from BorderDrawable.
PiperOrigin-RevId: 241798960
Change-Id: Ibc149ffdb0d85a40a5c10ede307f40eeac105306
diff --git a/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java b/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java
index 7f2174b..ba4a2ae 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java
@@ -44,6 +44,7 @@
final Clock clock;
final PietStylesHelperFactory pietStylesHelperFactory;
final RoundedCornerMaskCache roundedCornerMaskCache;
+ final boolean useLegacyRoundedCornerImpl;
// Doesn't like passing "this" to the new ElementAdapterFactory; however, nothing in the factory's
// construction will reference the elementAdapterFactory member of this, so should be safe.
@@ -52,7 +53,8 @@
Context context,
Supplier</*@Nullable*/ ViewGroup> parentViewSupplier,
HostProviders hostProviders,
- Clock clock) {
+ Clock clock,
+ boolean useLegacyRoundedCornerImpl) {
this.context = context;
this.parentViewSupplier = parentViewSupplier;
this.hostProviders = hostProviders;
@@ -69,6 +71,8 @@
this.pietStylesHelperFactory = new PietStylesHelperFactory();
this.roundedCornerMaskCache = new RoundedCornerMaskCache();
+
+ this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
}
/** Testing-only constructor for mocking the internally-constructed objects. */
@@ -90,7 +94,8 @@
templateBinder,
clock,
new PietStylesHelperFactory(),
- new RoundedCornerMaskCache());
+ new RoundedCornerMaskCache(),
+ true);
}
/** Testing-only constructor for mocking the internally-constructed objects. */
@@ -104,7 +109,8 @@
TemplateBinder templateBinder,
Clock clock,
PietStylesHelperFactory pietStylesHelperFactory,
- RoundedCornerMaskCache maskCache) {
+ RoundedCornerMaskCache maskCache,
+ boolean useLegacyRoundedCornerImpl) {
this.context = context;
this.parentViewSupplier = parentViewSupplier;
this.hostProviders = hostProviders;
@@ -116,5 +122,6 @@
this.clock = clock;
this.pietStylesHelperFactory = pietStylesHelperFactory;
this.roundedCornerMaskCache = maskCache;
+ this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
}
}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java b/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java
index 2bd4c6a..e706f14 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java
@@ -174,7 +174,8 @@
}
FrameLayout wrapper =
- elementStyle.createWrapperView(context, parameters.roundedCornerMaskCache);
+ elementStyle.createWrapperView(
+ context, parameters.roundedCornerMaskCache, parameters.useLegacyRoundedCornerImpl);
wrapper.addView(getBaseView());
if (baseElement.getOverlaysCount() > 0) {
diff --git a/src/main/java/com/google/android/libraries/feed/piet/PietManager.java b/src/main/java/com/google/android/libraries/feed/piet/PietManager.java
index 23618fc..c166623 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/PietManager.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/PietManager.java
@@ -72,6 +72,7 @@
/*@MonotonicNonNull*/ private CustomElementProvider customElementProvider = null;
/*@MonotonicNonNull*/ private HostBindingProvider hostBindingProvider = null;
/*@MonotonicNonNull*/ private Clock clock = null;
+ private boolean useLegacyRoundedCornerImpl = false;
private Builder() {}
@@ -95,6 +96,15 @@
return this;
}
+ /**
+ * Use the rounded corner optimizations on JB/KK for better performance at the expense of
+ * antialiasing.
+ */
+ public Builder setUseLegacyRoundedCornerImpl(boolean useLegacyRoundedCornerImpl) {
+ this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
+ return this;
+ }
+
// AssetProvider-related setters
public Builder setImageLoader(ImageLoader imageLoader) {
this.imageLoader = imageLoader;
@@ -152,7 +162,12 @@
typefaceProvider);
return new PietManagerImpl(
- debugBehavior, assetProvider, customElementProvider, hostBindingProvider, clock);
+ debugBehavior,
+ assetProvider,
+ customElementProvider,
+ hostBindingProvider,
+ clock,
+ useLegacyRoundedCornerImpl);
}
}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java b/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java
index 4f20fe2..321f60b 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java
@@ -34,6 +34,7 @@
private final CustomElementProvider customElementProvider;
private final HostBindingProvider hostBindingProvider;
private final Clock clock;
+ private final boolean useLegacyRoundedCornerImpl;
@VisibleForTesting /*@Nullable*/ AdapterParameters adapterParameters = null;
PietManagerImpl(
@@ -41,12 +42,14 @@
AssetProvider assetProvider,
CustomElementProvider customElementProvider,
HostBindingProvider hostBindingProvider,
- Clock clock) {
+ Clock clock,
+ boolean useLegacyRoundedCornerImpl) {
this.debugBehavior = debugBehavior;
this.assetProvider = assetProvider;
this.customElementProvider = customElementProvider;
this.hostBindingProvider = hostBindingProvider;
this.clock = clock;
+ this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
}
@Override
@@ -73,7 +76,8 @@
context,
cardViewProducer,
new HostProviders(assetProvider, customElementProvider, hostBindingProvider),
- clock);
+ clock,
+ useLegacyRoundedCornerImpl);
}
return adapterParameters;
}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java b/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java
index c060e4c..dc41b40 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java
@@ -18,21 +18,24 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.support.v4.view.ViewCompat;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import com.google.android.libraries.feed.common.ui.LayoutUtils;
import com.google.android.libraries.feed.piet.host.AssetProvider;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
import com.google.android.libraries.feed.piet.ui.BorderDrawable;
import com.google.android.libraries.feed.piet.ui.GradientDrawable;
+import com.google.android.libraries.feed.piet.ui.LegacyRoundedCornerWrapperView;
import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
import com.google.android.libraries.feed.piet.ui.RoundedCornerViewHelper;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
import com.google.search.now.ui.piet.ErrorsProto.ErrorCode;
import com.google.search.now.ui.piet.GradientsProto.Fill;
import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
@@ -329,14 +332,13 @@
}
// Apply appearance styles
- view.setBackground(createBackground());
+ baseView.setBackground(createBackground());
if (style.getShadow().hasElevationShadow()) {
ViewCompat.setElevation(view, style.getShadow().getElevationShadow().getElevation());
} else {
ViewCompat.setElevation(view, 0.0f);
}
if (view != baseView) {
- baseView.setBackground(null);
ViewCompat.setElevation(baseView, 0.0f);
}
@@ -408,19 +410,34 @@
}
}
- FrameLayout createWrapperView(Context context, RoundedCornerMaskCache maskCache) {
+ FrameLayout createWrapperView(
+ Context context, RoundedCornerMaskCache maskCache, boolean useLegacyImpl) {
if (!hasRoundedCorners()) {
- return new FrameLayout(context);
+ FrameLayout view = new FrameLayout(context);
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ // The wrapper view gets elevation; set an outline so it can cast a shadow.
+ view.setOutlineProvider(ViewOutlineProvider.BOUNDS);
+ }
+ return view;
}
int radiusOverride =
getRoundedCorners().getUseHostRadiusOverride() ? assetProvider.getDefaultCornerRadius() : 0;
- return new RoundedCornerWrapperView(
- context,
- getRoundedCorners(),
- maskCache,
- assetProvider.isRtLSupplier(),
- radiusOverride,
- getBorders());
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP || !useLegacyImpl) {
+ return new BitmapMaskingRoundedCornerWrapperView(
+ context,
+ getRoundedCorners(),
+ maskCache,
+ assetProvider.isRtLSupplier(),
+ radiusOverride,
+ getBorders());
+ } else {
+ return new LegacyRoundedCornerWrapperView(
+ context,
+ getRoundedCorners(),
+ assetProvider.isRtLSupplier(),
+ radiusOverride,
+ getBorders());
+ }
}
/**
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/BitmapMaskingRoundedCornerWrapperView.java b/src/main/java/com/google/android/libraries/feed/piet/ui/BitmapMaskingRoundedCornerWrapperView.java
new file mode 100644
index 0000000..b530c98
--- /dev/null
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/BitmapMaskingRoundedCornerWrapperView.java
@@ -0,0 +1,254 @@
+// Copyright 2018 The Feed Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.android.libraries.feed.piet.ui;
+
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.ViewParent;
+import com.google.android.libraries.feed.common.functional.Supplier;
+import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.Corner;
+import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.RoundedCornerBitmaps;
+import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
+import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners.Corners;
+import com.google.search.now.ui.piet.StylesProto.Borders;
+
+/**
+ * Generic wrapper for {@link View} instances in Piet that require rounded corners.
+ *
+ * <p>This implementation should work in all cases for all SDKs, but has the worst performance in
+ * CPU and memory usage.
+ */
+public class BitmapMaskingRoundedCornerWrapperView extends RoundedCornerWrapperView {
+
+ private final Paint paint;
+ private final Paint maskPaint;
+ private final RoundedCornerMaskCache maskCache;
+ private final Canvas offscreenCanvas;
+
+ /*@Nullable*/ private RoundRectShape outlineShape = null;
+
+ // Masks for each of the corners of the view; null if that corner is not rounded.
+ /*@Nullable*/ private Bitmap cornerTL = null;
+ /*@Nullable*/ private Bitmap cornerTR = null;
+ /*@Nullable*/ private Bitmap cornerBL = null;
+ /*@Nullable*/ private Bitmap cornerBR = null;
+
+ // Keep track of current mask configuration so we can use cached values if nothing has changed.
+ private int lastRadius = -1;
+ private boolean lastRtL;
+
+ // Doesn't like the call to setOutlineProvider
+ @SuppressWarnings("initialization")
+ public BitmapMaskingRoundedCornerWrapperView(
+ Context context,
+ RoundedCorners roundedCorners,
+ RoundedCornerMaskCache maskCache,
+ Supplier<Boolean> isRtLSupplier,
+ int radiusOverride,
+ Borders borders) {
+ super(context, roundedCorners, isRtLSupplier, radiusOverride, borders);
+
+ this.maskCache = maskCache;
+ offscreenCanvas = new Canvas();
+ lastRtL = !isRtLSupplier.get(); // Flip this so we must update the layout on the first time.
+
+ this.paint = maskCache.getPaint();
+ this.maskPaint = maskCache.getMaskPaint();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (hasRoundedCorners()) {
+ super.setOutlineProvider(
+ new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ RoundRectShape localOutlineShape = outlineShape;
+ if (localOutlineShape == null
+ || localOutlineShape.getHeight() != view.getHeight()
+ || localOutlineShape.getWidth() != view.getWidth()) {
+ int radius = getRadius(view.getWidth(), view.getHeight());
+ float[] radii =
+ RoundedCornerViewHelper.createRoundedCornerMask(
+ radius, roundedCorners.getBitmask(), isRtLSupplier.get());
+ localOutlineShape = new RoundRectShape(radii, null, null);
+ localOutlineShape.resize(view.getWidth(), view.getHeight());
+ outlineShape = localOutlineShape;
+ }
+ localOutlineShape.getOutline(outline);
+ }
+ });
+ } else {
+ super.setOutlineProvider(ViewOutlineProvider.BOUNDS);
+ }
+ }
+
+ setWillNotDraw(false);
+ }
+
+ private void initCornerMasks(int radius, boolean isRtL) {
+ if (radius < 1) {
+ return;
+ }
+ RoundedCornerBitmaps masks = maskCache.getMasks(radius);
+
+ if ((shouldRoundCorner(Corners.TOP_START) && !isRtL)
+ || (shouldRoundCorner(Corners.TOP_END) && isRtL)) {
+ cornerTL = masks.get(Corner.TOP_LEFT);
+ } else {
+ cornerTL = null;
+ }
+
+ if ((shouldRoundCorner(Corners.TOP_END) && !isRtL)
+ || (shouldRoundCorner(Corners.TOP_START) && isRtL)) {
+ cornerTR = masks.get(Corner.TOP_RIGHT);
+ } else {
+ cornerTR = null;
+ }
+
+ if ((shouldRoundCorner(Corners.BOTTOM_START) && !isRtL)
+ || (shouldRoundCorner(Corners.BOTTOM_END) && isRtL)) {
+ cornerBL = masks.get(Corner.BOTTOM_LEFT);
+ } else {
+ cornerBL = null;
+ }
+
+ if ((shouldRoundCorner(Corners.BOTTOM_END) && !isRtL)
+ || (shouldRoundCorner(Corners.BOTTOM_START) && isRtL)) {
+ cornerBR = masks.get(Corner.BOTTOM_RIGHT);
+ } else {
+ cornerBR = null;
+ }
+ }
+
+ /**
+ * Creates corner masks (which cover the parts of the corners that should not be shown) and
+ * borders, as necessary. Both must be created after the radius is known. However, if the size of
+ * the view and the LtR remain the same, it will only create the masks and borders once.
+ */
+ private void setupCornerMasksAndBorders(int radius) {
+ if (!hasRoundedCorners() || radius == 0) {
+ return;
+ }
+ boolean isRtL = isRtLSupplier.get();
+ if (radius == lastRadius && isRtL == lastRtL) {
+ return;
+ }
+
+ initCornerMasks(radius, isRtL);
+ addBorders(radius);
+
+ lastRadius = radius;
+ lastRtL = isRtL;
+ }
+
+ /**
+ * Ensures that the wrapper view is invalidated when child views are invalidated. This method only
+ * exists in Android O+.
+ */
+ @Override
+ public void onDescendantInvalidated(View child, View target) {
+ super.onDescendantInvalidated(child, target);
+ if (hasRoundedCorners()) {
+ Rect targetRect = new Rect();
+ target.getDrawingRect(targetRect);
+ invalidate(targetRect);
+ }
+ }
+
+ /**
+ * Using as an indicator that the child view was invalidated. By overriding this method, we ensure
+ * that the wrapper view is invalidated when the child view is. This is only used in Android N-
+ * and is deprecated, but we must use it because onDescendantInvalidated only exists in O+.
+ */
+ @Override
+ public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
+ if (hasRoundedCorners()) {
+ invalidate(dirty);
+ }
+ return super.invalidateChildInParent(location, dirty);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (!changed || !hasRoundedCorners()) {
+ return;
+ }
+
+ int width = getWidth();
+ int height = getHeight();
+ if (width == 0 || height == 0) {
+ // The view is not visible; no further processing is needed.
+ return;
+ }
+
+ int radius = getRadius(width, height);
+
+ // Set up the corner masks and borders, both of which require knowing the radius.
+ // This should no-op if radius and isLtR have not changed.
+ setupCornerMasksAndBorders(radius);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (!hasRoundedCorners()) {
+ super.draw(canvas);
+ return;
+ }
+ int width = getWidth();
+ int height = getHeight();
+ if (width == 0 || height == 0) {
+ // The view is not visible, and offscreenBitmap creation will fail. Stop here.
+ return;
+ }
+
+ int radius = getRadius(width, height);
+
+ // Draw the view without rounded corners on the offscreen canvas.
+ Bitmap localOffscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ offscreenCanvas.setBitmap(localOffscreenBitmap);
+ super.draw(offscreenCanvas);
+
+ // Crop the corners off using masks
+ maskCorners(offscreenCanvas, width, height, radius, maskPaint);
+
+ // Draw the offscreen bitmap (view with rounded corners) to the target canvas.
+ canvas.drawBitmap(localOffscreenBitmap, 0f, 0f, paint);
+ }
+
+ /** Draws a mask on each corner that is rounded. */
+ private void maskCorners(Canvas canvas, int width, int height, int radius, Paint paint) {
+ if (cornerTL != null) {
+ canvas.drawBitmap(cornerTL, 0, 0, paint);
+ }
+ if (cornerTR != null) {
+ canvas.drawBitmap(cornerTR, width - radius, 0, paint);
+ }
+ if (cornerBL != null) {
+ canvas.drawBitmap(cornerBL, 0, height - radius, paint);
+ }
+ if (cornerBR != null) {
+ canvas.drawBitmap(cornerBR, width - radius, height - radius, paint);
+ }
+ }
+}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java b/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java
index 9bfbdc7..c337b26 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java
@@ -15,16 +15,10 @@
package com.google.android.libraries.feed.piet.ui;
import android.content.Context;
-import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
-import android.graphics.drawable.shapes.Shape;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
import com.google.android.libraries.feed.common.ui.LayoutUtils;
import com.google.search.now.ui.piet.StylesProto.Borders;
import com.google.search.now.ui.piet.StylesProto.Borders.Edges;
@@ -34,9 +28,6 @@
* not specified.
*/
public class BorderDrawable extends ShapeDrawable {
- private final float[] cornerRadii;
- private final int initialWidth;
- private final int initialHeight;
private final boolean hasLeftBorder;
private final boolean hasRightBorder;
private final boolean hasTopBorder;
@@ -56,9 +47,6 @@
public BorderDrawable(
Context context, Borders borders, float[] cornerRadii, boolean isRtL, int width, int height) {
super(new RoundRectShape(cornerRadii, null, null));
- this.cornerRadii = cornerRadii;
- this.initialWidth = width;
- this.initialHeight = height;
borderWidth = (int) LayoutUtils.dpToPx(borders.getWidth(), context);
@@ -109,147 +97,4 @@
public void setBounds(Rect bounds) {
setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
}
-
- @Override
- protected void onDraw(Shape shape, Canvas canvas, Paint paint) {
- if (Build.VERSION.SDK_INT < VERSION_CODES.KITKAT && hasLargeRadius()) {
- drawBorderWithPath(canvas, paint);
- } else {
- super.onDraw(shape, canvas, paint);
- }
- }
-
- /**
- * Checks if the radius is larger than half the width or height. Only applies to elements with
- * rounded corners.
- */
- private boolean hasLargeRadius() {
- if (initialWidth == 0 || initialHeight == 0) {
- return false;
- }
- int radius = 0;
- for (float cornerRadius : cornerRadii) {
- if (cornerRadius != 0) {
- radius = (int) cornerRadius;
- break;
- }
- }
- if (radius == 0) {
- return false;
- }
- return radius > (initialWidth / 2) || radius > (initialHeight / 2);
- }
-
- /**
- * Draws the border with a {@link Path} instead of a {@link RoundRectShape}. This is used because
- * in JellyBean, RoundRectShape clamps the rounded corner radius to half the length of the side of
- * the shape. When the adjacent corner is not rounded, we want to allow radii that are more than
- * 50% of that side.
- */
- private void drawBorderWithPath(Canvas canvas, Paint paint) {
- Path path = new Path();
- int height = initialHeight;
- int width = initialWidth;
- // This is necessary because of extra height/width added for borders.
- if (!hasTopBorder) {
- height += borderWidth;
- }
- if (!hasLeftBorder) {
- width += borderWidth;
- }
- if (hasTopBorder) {
- if (cornerRadii[0] != 0) {
- // Add second half of top left corner.
- float radius = cornerRadii[0];
- path.addArc(topLeftBoundingBox(radius), 225, 45);
- }
- if (cornerRadii[2] != 0) {
- // If the top right corner is rounded, add the top border and half the top right corner.
- float radius = cornerRadii[2];
- path.lineTo(width - radius, 0);
- path.addArc(topRightBoundingBox(width, radius), 270, 46);
- } else {
- // Add border across top.
- path.lineTo(width, 0);
- }
- } else {
- // If there is no top border, jump to the top right corner.
- path.moveTo(width, 0);
- }
-
- if (hasRightBorder) {
- if (cornerRadii[2] != 0) {
- // Add second half of top right corner.
- float radius = cornerRadii[2];
- path.addArc(topRightBoundingBox(width, radius), 315, 45);
- }
- if (cornerRadii[4] != 0) {
- // If the bottom right corner is rounded, add right border and half the bottom right corner.
- float radius = cornerRadii[4];
- path.lineTo(width, height - radius);
- path.addArc(bottomRightBoundingBox(width, height, radius), 0, 46);
- } else {
- // Add right border, no rounded corner at bottom.
- path.lineTo(width, height);
- }
- } else {
- // If there is no right border, jump to the bottom right corner.
- path.moveTo(width, height);
- }
-
- if (hasBottomBorder) {
- if (cornerRadii[4] != 0) {
- // Add second half of bottom right corner.
- float radius = cornerRadii[4];
- path.addArc(bottomRightBoundingBox(width, height, radius), 45, 45);
- }
- if (cornerRadii[6] != 0) {
- // Add bottom border with rounded corner at bottom left.
- float radius = cornerRadii[6];
- path.lineTo(radius, height);
- path.addArc(bottomLeftBoundingBox(height, radius), 90, 46);
- } else {
- path.lineTo(0, height);
- }
- } else {
- // If there is no bottom border, jump to the bottom left corner.
- path.moveTo(0, height);
- }
-
- if (hasLeftBorder) {
- if (cornerRadii[6] != 0) {
- // Add second half of bottom left corner.
- float radius = cornerRadii[6];
- path.addArc(bottomLeftBoundingBox(height, radius), 135, 45);
- }
- if (cornerRadii[0] != 0) {
- // Add left border with rounded corner at top left.
- float radius = cornerRadii[0];
- path.lineTo(0, radius);
- path.addArc(topLeftBoundingBox(radius), 180, 46);
- } else {
- // Add left border.
- path.lineTo(0, 0);
- }
- }
-
- // Actually draw the path that was just built. The paint defines the width/color of the border.
- canvas.drawPath(path, paint);
- }
-
- private RectF topLeftBoundingBox(float radius) {
- return new RectF(0, 0, radius * 2, radius * 2);
- }
-
- private RectF topRightBoundingBox(float width, float radius) {
- return new RectF(width - (radius * 2), 0, width, radius * 2);
- }
-
- private RectF bottomRightBoundingBox(float width, float height, float radius) {
- return new RectF(width - (radius * 2), height - (radius * 2), width, height);
- }
-
- private RectF bottomLeftBoundingBox(float height, float radius) {
- return new RectF(0, height - (radius * 2), radius * 2, height);
- }
}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/LegacyRoundedCornerWrapperView.java b/src/main/java/com/google/android/libraries/feed/piet/ui/LegacyRoundedCornerWrapperView.java
new file mode 100644
index 0000000..f29f6d2
--- /dev/null
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/LegacyRoundedCornerWrapperView.java
@@ -0,0 +1,98 @@
+// Copyright 2018 The Feed Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.android.libraries.feed.piet.ui;
+
+import static com.google.android.libraries.feed.common.Validators.checkState;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+import com.google.android.libraries.feed.common.functional.Supplier;
+import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
+import com.google.search.now.ui.piet.StylesProto.Borders;
+
+/**
+ * Wrapper for {@link View} instances in Piet that require rounded corners.
+ *
+ * <p>This class does not support antialiasing on rounded corners, but has much better memory and
+ * CPU performance on JB and KK (API 16 and 19)
+ */
+public class LegacyRoundedCornerWrapperView extends RoundedCornerWrapperView {
+
+ /*@Nullable*/ private Path clipPath = null;
+
+ // Doesn't like the call to setLayerType
+ @SuppressWarnings("initialization")
+ public LegacyRoundedCornerWrapperView(
+ Context context,
+ RoundedCorners roundedCorners,
+ Supplier<Boolean> isRtLSupplier,
+ int radiusOverride,
+ Borders borders) {
+ super(context, roundedCorners, isRtLSupplier, radiusOverride, borders);
+
+ checkState(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP);
+
+ if (VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN_MR2) {
+ // clipPath doesn't work with hardware rendering on < 18.
+ setLayerType(LAYER_TYPE_SOFTWARE, null);
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ int radius = getRadius(w, h);
+ float[] radii =
+ RoundedCornerViewHelper.createRoundedCornerMask(
+ radius, roundedCorners.getBitmask(), isRtLSupplier.get());
+
+ clipPath = new Path();
+ clipPath.addRoundRect(new RectF(0, 0, w, h), radii, Path.Direction.CW);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (clipPath != null) {
+ canvas.clipPath(clipPath);
+ }
+ super.dispatchDraw(canvas);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (!changed || !hasRoundedCorners()) {
+ return;
+ }
+
+ int width = getWidth();
+ int height = getHeight();
+ if (width == 0 || height == 0) {
+ // The view is not visible; no further processing is needed.
+ return;
+ }
+
+ int radius = getRadius(width, height);
+
+ addBorders(radius);
+ }
+}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java b/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java
index bb994bc..7b6c76e 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java
@@ -16,155 +16,43 @@
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.shapes.RoundRectShape;
-import android.os.Build;
import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.view.ViewParent;
import android.widget.FrameLayout;
import com.google.android.libraries.feed.common.functional.Supplier;
import com.google.android.libraries.feed.common.ui.LayoutUtils;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.Corner;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.RoundedCornerBitmaps;
import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners.Corners;
import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners.RadiusOptionsCase;
import com.google.search.now.ui.piet.StylesProto.Borders;
/** Wrapper for {@link View} instances in Piet that require rounded corners. */
-public class RoundedCornerWrapperView extends FrameLayout {
+public abstract class RoundedCornerWrapperView extends FrameLayout {
+ final int radiusOverride;
+ final RoundedCorners roundedCorners;
+ final Supplier<Boolean> isRtLSupplier;
+ final Context context;
+ final Borders borders;
- private final Paint paint;
- private final Paint maskPaint;
- private final int radiusOverride;
- private final RoundedCorners roundedCorners;
- private final Supplier<Boolean> isRtLSupplier;
- private final Context context;
- private final Borders borders;
- private final RoundedCornerMaskCache maskCache;
- private final Canvas offscreenCanvas;
-
- /*@Nullable*/ private RoundRectShape outlineShape = null;
- private int roundedCornerRadius;
-
- // Masks for each of the corners of the view; null if that corner is not rounded.
- /*@Nullable*/ private Bitmap cornerTL = null;
- /*@Nullable*/ private Bitmap cornerTR = null;
- /*@Nullable*/ private Bitmap cornerBL = null;
- /*@Nullable*/ private Bitmap cornerBR = null;
+ int roundedCornerRadius;
// Keep track of current mask configuration so we can use cached values if nothing has changed.
- private int lastRadius = -1;
- private int lastWidth = -1;
- private int lastHeight = -1;
- private boolean lastRtL;
+ int lastWidth = -1;
+ int lastHeight = -1;
- // Doesn't like the call to setOutlineProvider
- @SuppressWarnings("initialization")
- public RoundedCornerWrapperView(
+ RoundedCornerWrapperView(
Context context,
RoundedCorners roundedCorners,
- RoundedCornerMaskCache maskCache,
Supplier<Boolean> isRtLSupplier,
int radiusOverride,
Borders borders) {
super(context);
- this.maskCache = maskCache;
- this.isRtLSupplier = isRtLSupplier;
- this.roundedCorners = roundedCorners;
+
this.radiusOverride = radiusOverride;
+ this.roundedCorners = roundedCorners;
+ this.isRtLSupplier = isRtLSupplier;
+
this.context = context;
this.borders = borders;
- offscreenCanvas = new Canvas();
- lastRtL = !isRtLSupplier.get(); // Flip this so we must update the layout on the first time.
-
- this.paint = maskCache.getPaint();
- this.maskPaint = maskCache.getMaskPaint();
-
- if (hasRoundedCorners() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- super.setOutlineProvider(
- new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- RoundRectShape localOutlineShape = outlineShape;
- if (localOutlineShape == null
- || localOutlineShape.getHeight() != view.getHeight()
- || localOutlineShape.getWidth() != view.getWidth()) {
- int radius = getRadius(view.getWidth(), view.getHeight());
- float[] radii =
- RoundedCornerViewHelper.createRoundedCornerMask(
- radius, roundedCorners.getBitmask(), isRtLSupplier.get());
- localOutlineShape = new RoundRectShape(radii, null, null);
- localOutlineShape.resize(view.getWidth(), view.getHeight());
- outlineShape = localOutlineShape;
- }
- localOutlineShape.getOutline(outline);
- }
- });
- }
-
- setWillNotDraw(false);
- }
-
- private void initCornerMasks(int radius, boolean isRtL) {
- if (radius < 1) {
- return;
- }
- RoundedCornerBitmaps masks = maskCache.getMasks(radius);
-
- if ((shouldRoundCorner(Corners.TOP_START) && !isRtL)
- || (shouldRoundCorner(Corners.TOP_END) && isRtL)) {
- cornerTL = masks.get(Corner.TOP_LEFT);
- } else {
- cornerTL = null;
- }
-
- if ((shouldRoundCorner(Corners.TOP_END) && !isRtL)
- || (shouldRoundCorner(Corners.TOP_START) && isRtL)) {
- cornerTR = masks.get(Corner.TOP_RIGHT);
- } else {
- cornerTR = null;
- }
-
- if ((shouldRoundCorner(Corners.BOTTOM_START) && !isRtL)
- || (shouldRoundCorner(Corners.BOTTOM_END) && isRtL)) {
- cornerBL = masks.get(Corner.BOTTOM_LEFT);
- } else {
- cornerBL = null;
- }
-
- if ((shouldRoundCorner(Corners.BOTTOM_END) && !isRtL)
- || (shouldRoundCorner(Corners.BOTTOM_START) && isRtL)) {
- cornerBR = masks.get(Corner.BOTTOM_RIGHT);
- } else {
- cornerBR = null;
- }
- }
-
- /**
- * Creates corner masks (which cover the parts of the corners that should not be shown) and
- * borders, as necessary. Both must be created after the radius is known. However, if the size of
- * the view and the LtR remain the same, it will only create the masks and borders once.
- */
- private void setupCornerMasksAndBorders(int radius) {
- if (!hasRoundedCorners() || radius == 0) {
- return;
- }
- boolean isRtL = isRtLSupplier.get();
- if (radius == lastRadius && isRtL == lastRtL) {
- return;
- }
-
- initCornerMasks(radius, isRtL);
- addBorders(radius);
-
- lastRadius = radius;
- lastRtL = isRtL;
}
/**
@@ -175,7 +63,7 @@
* after layout happens, since we are overriding draw. Draw is guaranteed to happen after layout,
* so we can make sure that the borders are drawn with the appropriate measurements.
*/
- private void addBorders(int radius) {
+ void addBorders(int radius) {
if (borders.getWidth() <= 0) {
return;
}
@@ -191,81 +79,6 @@
}
/**
- * Ensures that the wrapper view is invalidated when child views are invalidated. This method only
- * exists in Android O+.
- */
- @Override
- public void onDescendantInvalidated(View child, View target) {
- super.onDescendantInvalidated(child, target);
- if (hasRoundedCorners()) {
- Rect targetRect = new Rect();
- target.getDrawingRect(targetRect);
- invalidate(targetRect);
- }
- }
-
- /**
- * Using as an indicator that the child view was invalidated. By overriding this method, we ensure
- * that the wrapper view is invalidated when the child view is. This is only used in Android N-
- * and is deprecated, but we must use it because onDescendantInvalidated only exists in O+.
- */
- @Override
- public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
- if (hasRoundedCorners()) {
- invalidate(dirty);
- }
- return super.invalidateChildInParent(location, dirty);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (!changed || !hasRoundedCorners()) {
- return;
- }
-
- int width = getWidth();
- int height = getHeight();
- if (width == 0 || height == 0) {
- // The view is not visible; no further processing is needed.
- return;
- }
-
- int radius = getRadius(width, height);
-
- // Set up the corner masks and borders, both of which require knowing the radius.
- // This should no-op if radius and isLtR have not changed.
- setupCornerMasksAndBorders(radius);
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (!hasRoundedCorners()) {
- super.draw(canvas);
- return;
- }
- int width = getWidth();
- int height = getHeight();
- if (width == 0 || height == 0) {
- // The view is not visible, and offscreenBitmap creation will fail. Stop here.
- return;
- }
-
- int radius = getRadius(width, height);
-
- // Draw the view without rounded corners on the offscreen canvas.
- Bitmap localOffscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- offscreenCanvas.setBitmap(localOffscreenBitmap);
- super.draw(offscreenCanvas);
-
- // Crop the corners off using masks
- maskCorners(offscreenCanvas, width, height, radius, maskPaint);
-
- // Draw the offscreen bitmap (view with rounded corners) to the target canvas.
- canvas.drawBitmap(localOffscreenBitmap, 0f, 0f, paint);
- }
-
- /**
* Returns the radius, which is only calculated if necessary. If the width and height are the same
* as the previous width and height, there's no need to re-calculate, and the previous radius is
* returned. The width and height are needed for radii calculated as a percentage of width or
@@ -335,22 +148,6 @@
return currentRadius;
}
- /** Draws a mask on each corner that is rounded. */
- private void maskCorners(Canvas canvas, int width, int height, int radius, Paint paint) {
- if (cornerTL != null) {
- canvas.drawBitmap(cornerTL, 0, 0, paint);
- }
- if (cornerTR != null) {
- canvas.drawBitmap(cornerTR, width - radius, 0, paint);
- }
- if (cornerBL != null) {
- canvas.drawBitmap(cornerBL, 0, height - radius, paint);
- }
- if (cornerBR != null) {
- canvas.drawBitmap(cornerBR, width - radius, height - radius, paint);
- }
- }
-
/** This should always be true; we should not be using this view when corners are not round. */
public boolean hasRoundedCorners() {
return RoundedCornerViewHelper.hasValidRoundedCorners(roundedCorners, radiusOverride);
@@ -366,7 +163,7 @@
|| (shouldRoundCorner(Corners.BOTTOM_START) && shouldRoundCorner(Corners.BOTTOM_END));
}
- private boolean shouldRoundCorner(Corners corner) {
+ boolean shouldRoundCorner(Corners corner) {
int bitmask = roundedCorners.getBitmask();
return (bitmask == 0) || (bitmask & corner.getNumber()) != 0;
}
diff --git a/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java
index b4dc32c..d9d022c 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java
@@ -78,7 +78,7 @@
.thenAnswer(invocation -> invocation.getArguments()[2]);
adapterParameters =
- new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+ new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
when(frameContext.makeStyleFor(any(StyleIdsStack.class)))
.thenReturn(adapterParameters.defaultStyleProvider);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java
index b8653d7..0c1205d 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java
@@ -41,6 +41,7 @@
import com.google.android.libraries.feed.piet.AdapterFactory.SingletonKeySupplier;
import com.google.android.libraries.feed.piet.host.ActionHandler;
import com.google.android.libraries.feed.piet.host.ActionHandler.ActionType;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
import com.google.android.libraries.feed.testing.shadows.ExtendedShadowView;
@@ -81,6 +82,7 @@
public class ElementAdapterTest {
private static final int WIDTH = 123;
private static final int HEIGHT = 456;
+ private static final boolean LEGACY_CORNERS_FLAG = false;
@Mock private FrameContext frameContext;
@Mock private HostProviders hostProviders;
@@ -100,13 +102,18 @@
context = Robolectric.buildActivity(Activity.class).get();
parameters =
new AdapterParameters(
- context, Suppliers.of((ViewGroup) null), hostProviders, new FakeClock());
+ context,
+ Suppliers.of((ViewGroup) null),
+ hostProviders,
+ new FakeClock(),
+ LEGACY_CORNERS_FLAG);
maskCache = parameters.roundedCornerMaskCache;
when(frameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(styleProvider);
when(frameContext.getActionHandler()).thenReturn(actionHandler);
when(styleProvider.hasRoundedCorners()).thenReturn(false);
when(styleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
- when(styleProvider.createWrapperView(context, maskCache)).thenReturn(new FrameLayout(context));
+ when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
+ .thenReturn(new FrameLayout(context));
view = new View(context);
adapter = new TestElementAdapter(context, parameters, view);
@@ -308,7 +315,8 @@
mockFactory,
mock(TemplateBinder.class),
new FakeClock());
- when(styleProvider.createWrapperView(context, parameters.roundedCornerMaskCache))
+ when(styleProvider.createWrapperView(
+ context, parameters.roundedCornerMaskCache, parameters.useLegacyRoundedCornerImpl))
.thenReturn(new FrameLayout(context));
adapter = new TestElementAdapter(context, parameters, view);
@@ -1274,7 +1282,7 @@
public void testRoundedCorners() {
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
RoundedCornerWrapperView roundedCornerWrapperView =
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context,
roundedCorners,
maskCache,
@@ -1282,7 +1290,8 @@
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
when(styleProvider.hasRoundedCorners()).thenReturn(true);
- when(styleProvider.createWrapperView(context, maskCache)).thenReturn(roundedCornerWrapperView);
+ when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
+ .thenReturn(roundedCornerWrapperView);
adapter.createAdapter(Element.getDefaultInstance(), frameContext);
@@ -1293,7 +1302,7 @@
public void testRoundedCornersWithVisibility() {
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
RoundedCornerWrapperView roundedCornerWrapperView =
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context,
roundedCorners,
maskCache,
@@ -1301,7 +1310,8 @@
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
when(styleProvider.hasRoundedCorners()).thenReturn(true);
- when(styleProvider.createWrapperView(context, maskCache)).thenReturn(roundedCornerWrapperView);
+ when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
+ .thenReturn(roundedCornerWrapperView);
VisibilityBindingRef visibilityBinding =
VisibilityBindingRef.newBuilder().setBindingId("visibility").build();
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java
index b00e9bb..cf840e9 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java
@@ -143,7 +143,7 @@
when(hostProviders.getAssetProvider()).thenReturn(assetProvider);
AdapterParameters adapterParameters =
- new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+ new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
when(frameContext.createTemplateContext(eq(DEFAULT_TEMPLATE), any(BindingContext.class)))
.thenReturn(frameContext);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java
index 99f2e55..9d176c4 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java
@@ -112,7 +112,7 @@
when(styleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
adapterParameters =
- new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+ new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
when(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
.thenReturn(adapterParameters.defaultStyleProvider);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java
index 5027c72..e30d8f3 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java
@@ -88,7 +88,8 @@
when(mockStyleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
adapterParameters =
- new AdapterParameters(context, Suppliers.of(null), mockHostProviders, new FakeClock());
+ new AdapterParameters(
+ context, Suppliers.of(null), mockHostProviders, new FakeClock(), false);
adapter = new ElementStackAdapter.KeySupplier().getAdapter(context, adapterParameters);
}
diff --git a/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java b/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java
index c83f3d8..3b03e32 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java
@@ -141,7 +141,8 @@
templateBinder,
new FakeClock(),
stylesHelpers,
- maskCache);
+ maskCache,
+ false);
when(elementAdapter.getView()).thenReturn(new LinearLayout(context));
when(templateAdapter.getView()).thenReturn(new LinearLayout(context));
doReturn(elementAdapter)
@@ -269,7 +270,11 @@
.build();
AdapterParameters adapterParameters =
new AdapterParameters(
- context, Suppliers.of(new LinearLayout(context)), hostProviders, new FakeClock());
+ context,
+ Suppliers.of(new LinearLayout(context)),
+ hostProviders,
+ new FakeClock(),
+ false);
PietSharedState pietSharedState =
PietSharedState.newBuilder()
.addTemplates(
@@ -332,7 +337,11 @@
public void testBindAndUnbind_resetsStylesWhenWrapperViewIsAdded() {
AdapterParameters adapterParameters =
new AdapterParameters(
- context, Suppliers.of(new LinearLayout(context)), hostProviders, new FakeClock());
+ context,
+ Suppliers.of(new LinearLayout(context)),
+ hostProviders,
+ new FakeClock(),
+ false);
PietSharedState pietSharedState =
PietSharedState.newBuilder()
.addStylesheets(
@@ -537,7 +546,11 @@
public void testCreateFrameContext_recyclesPietStylesHelper() {
AdapterParameters adapterParameters =
new AdapterParameters(
- context, Suppliers.of(new LinearLayout(context)), hostProviders, new FakeClock());
+ context,
+ Suppliers.of(new LinearLayout(context)),
+ hostProviders,
+ new FakeClock(),
+ false);
PietSharedState pietSharedState =
PietSharedState.newBuilder()
.addStylesheets(
@@ -607,7 +620,7 @@
pietSharedStates.add(pietSharedState);
AdapterParameters parameters =
- new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+ new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
FrameAdapterImpl frameAdapter =
new FrameAdapterImpl(context, parameters, actionHandler, eventLogger, debugBehavior);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java
index da23080..2e7ce98 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java
@@ -134,7 +134,7 @@
when(styleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
adapterParameters =
- new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+ new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
when(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
.thenReturn(adapterParameters.defaultStyleProvider);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java
index 7560d78..e7cb9db 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java
@@ -41,8 +41,8 @@
import com.google.android.libraries.feed.common.ui.LayoutUtils;
import com.google.android.libraries.feed.piet.PietStylesHelper.PietStylesHelperFactory;
import com.google.android.libraries.feed.piet.host.AssetProvider;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
import com.google.search.now.ui.piet.BindingRefsProto.ImageBindingRef;
import com.google.search.now.ui.piet.BindingRefsProto.StyleBindingRef;
import com.google.search.now.ui.piet.ElementsProto.BindingValue;
@@ -79,6 +79,7 @@
Image.newBuilder().addSources(ImageSource.newBuilder().setUrl("icanhas.chz")).build();
private static final Element DEFAULT_MODEL =
asElement(ImageElement.newBuilder().setImage(DEFAULT_IMAGE).build());
+ private static final boolean LEGACY_CORNERS_FLAG = false;
@Mock private ElementAdapterFactory adapterFactory;
@Mock private TemplateBinder templateBinder;
@@ -114,7 +115,8 @@
templateBinder,
clock,
new PietStylesHelperFactory(),
- maskCache);
+ maskCache,
+ LEGACY_CORNERS_FLAG);
when(frameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(styleProvider);
when(frameContext.filterImageSourcesByMediaQueryCondition(any(Image.class)))
@@ -124,9 +126,9 @@
when(styleProvider.hasRoundedCorners()).thenReturn(true);
when(styleProvider.getRoundedCorners()).thenReturn(CORNERS);
when(styleProvider.getScaleType()).thenReturn(ScaleType.FIT_CENTER);
- when(styleProvider.createWrapperView(context, maskCache))
+ when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
.thenReturn(
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context,
CORNERS,
maskCache,
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java b/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java
index caa22aa..616a25e 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java
@@ -61,7 +61,11 @@
context = Robolectric.buildActivity(Activity.class).get();
adapterParameters =
new AdapterParameters(
- null, null, new HostProviders(mock(AssetProvider.class), null, null), new FakeClock());
+ null,
+ null,
+ new HostProviders(mock(AssetProvider.class), null, null),
+ new FakeClock(),
+ false);
when(keySupplier.getAdapter(context, adapterParameters))
.thenReturn(adapter)
.thenReturn(adapter2);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java b/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java
index b517852..d68c9cd 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java
@@ -235,7 +235,8 @@
mockTemplateBinder,
new FakeClock(),
stylesHelpers,
- maskCache);
+ maskCache,
+ false);
pietManager.purgeRecyclerPools();
verify(mockFactory).purgeRecyclerPools();
verify(stylesHelpers).purge();
diff --git a/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java b/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java
index 5674777..a8c9304 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java
@@ -26,19 +26,23 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
+import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.libraries.feed.common.functional.Suppliers;
import com.google.android.libraries.feed.common.ui.LayoutUtils;
import com.google.android.libraries.feed.piet.host.AssetProvider;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
import com.google.android.libraries.feed.piet.ui.BorderDrawable;
import com.google.android.libraries.feed.piet.ui.GradientDrawable;
+import com.google.android.libraries.feed.piet.ui.LegacyRoundedCornerWrapperView;
import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
import com.google.search.now.ui.piet.GradientsProto.ColorStop;
@@ -399,8 +403,9 @@
.build(),
mockAssetProvider);
- assertThat(styleProvider.createWrapperView(context, maskCache))
- .isNotInstanceOf(RoundedCornerWrapperView.class);
+ FrameLayout wrapperView = styleProvider.createWrapperView(context, maskCache, false);
+ assertThat(wrapperView).isNotInstanceOf(RoundedCornerWrapperView.class);
+ assertThat(wrapperView.getOutlineProvider()).isEqualTo(ViewOutlineProvider.BOUNDS);
}
@Test
@@ -412,8 +417,37 @@
.build(),
mockAssetProvider);
- assertThat(styleProvider.createWrapperView(context, maskCache))
- .isInstanceOf(RoundedCornerWrapperView.class);
+ FrameLayout wrapperView = styleProvider.createWrapperView(context, maskCache, false);
+ assertThat(wrapperView).isInstanceOf(RoundedCornerWrapperView.class);
+ assertThat(wrapperView.getOutlineProvider()).isNotNull();
+ }
+
+ @Test
+ @Config(sdk = {Build.VERSION_CODES.KITKAT})
+ public void testCreateWrapperView_legacyButFlagOff() {
+ StyleProvider styleProvider =
+ new StyleProvider(
+ Style.newBuilder()
+ .setRoundedCorners(RoundedCorners.newBuilder().setRadius(9).setBitmask(4))
+ .build(),
+ mockAssetProvider);
+
+ assertThat(styleProvider.createWrapperView(context, maskCache, false))
+ .isInstanceOf(BitmapMaskingRoundedCornerWrapperView.class);
+ }
+
+ @Test
+ @Config(sdk = {Build.VERSION_CODES.KITKAT})
+ public void testCreateWrapperView_legacyWithFlagOn() {
+ StyleProvider styleProvider =
+ new StyleProvider(
+ Style.newBuilder()
+ .setRoundedCorners(RoundedCorners.newBuilder().setRadius(9).setBitmask(4))
+ .build(),
+ mockAssetProvider);
+
+ assertThat(styleProvider.createWrapperView(context, maskCache, true))
+ .isInstanceOf(LegacyRoundedCornerWrapperView.class);
}
@Test
@@ -455,32 +489,32 @@
StyleProvider styleProvider = new StyleProvider(style, mockAssetProvider);
styleProvider.applyElementStyles(adapter);
- ColorDrawable colorDrawable = (ColorDrawable) view.getBackground();
+ ColorDrawable colorDrawable = (ColorDrawable) baseView.getBackground();
assertThat(colorDrawable.getColor()).isEqualTo(color);
}
@Test
public void testElementStyles_noBackgroundInStyle() {
- view.setBackground(new ColorDrawable(0xffff0000));
+ baseView.setBackground(new ColorDrawable(0xffff0000));
new StyleProvider(Style.getDefaultInstance(), mockAssetProvider).applyElementStyles(adapter);
- assertThat(view.getBackground()).isNull();
+ assertThat(baseView.getBackground()).isNull();
}
@Test
public void testElementStyles_noColorInFill() {
- view.setBackground(new ColorDrawable(0xffff0000));
+ baseView.setBackground(new ColorDrawable(0xffff0000));
Style style = Style.newBuilder().setBackground(Fill.getDefaultInstance()).build();
new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
- assertThat(view.getBackground()).isNull();
+ assertThat(baseView.getBackground()).isNull();
}
@Test
public void testElementStyles_gradientBackground() {
- view.setBackground(new ColorDrawable(0xffff0000));
+ baseView.setBackground(new ColorDrawable(0xffff0000));
Style style =
Style.newBuilder()
@@ -505,13 +539,13 @@
new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
- Drawable background = view.getBackground();
+ Drawable background = baseView.getBackground();
assertThat(background).isInstanceOf(GradientDrawable.class);
}
@Test
public void testElementStyles_gradientBackground_badAngle_doesntCrash() {
- view.setBackground(new ColorDrawable(0xffff0000));
+ baseView.setBackground(new ColorDrawable(0xffff0000));
Style style =
Style.newBuilder()
@@ -530,13 +564,13 @@
new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
- Drawable background = view.getBackground();
+ Drawable background = baseView.getBackground();
assertThat(background).isInstanceOf(GradientDrawable.class);
}
@Test
public void testElementStyles_gradientBackground_badPercent_doesntCrash() {
- view.setBackground(new ColorDrawable(0xffff0000));
+ baseView.setBackground(new ColorDrawable(0xffff0000));
Style style =
Style.newBuilder()
@@ -555,7 +589,7 @@
new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
- Drawable background = view.getBackground();
+ Drawable background = baseView.getBackground();
assertThat(background).isInstanceOf(GradientDrawable.class);
}
@@ -677,8 +711,8 @@
assertThat(baseView.getElevation()).isWithin(0.1f).of(0.0f);
assertThat(view.getElevation()).isWithin(0.1f).of(5.0f);
- assertThat(baseView.getBackground()).isNull();
- assertThat(view.getBackground()).isInstanceOf(ColorDrawable.class);
+ assertThat(view.getBackground()).isNull();
+ assertThat(baseView.getBackground()).isInstanceOf(ColorDrawable.class);
}
@Test
diff --git a/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java b/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java
index 5cf87c2..e5f3b7c 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java
@@ -307,7 +307,8 @@
mockHostProviders,
new FrameLayout(context));
AdapterParameters adapterParameters =
- new AdapterParameters(context, Suppliers.of(null), mockHostProviders, new FakeClock());
+ new AdapterParameters(
+ context, Suppliers.of(null), mockHostProviders, new FakeClock(), false);
TemplateBinder templateBinder = adapterParameters.templateBinder;
// Create and bind adapter
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java b/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java
index bdcc77b..8182526 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java
@@ -61,7 +61,7 @@
@Test
public void testZeroDimensions_notRounded() {
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context,
RoundedCorners.getDefaultInstance(),
maskCache,
@@ -91,7 +91,7 @@
RoundedCorners roundedCorners =
RoundedCorners.newBuilder().setBitmask(7).setRadiusDp(10).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context,
roundedCorners,
maskCache,
@@ -124,7 +124,7 @@
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context, roundedCorners, maskCache, Suppliers.of(false), 0, borders);
view.layout(0, 0, 100, 100);
@@ -142,7 +142,7 @@
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context, roundedCorners, maskCache, Suppliers.of(false), 0, borders);
// Set a width and height on the view so that the only thing stopping borders from being created
@@ -162,10 +162,9 @@
int radiusOverride = 20;
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
radiusOverride,
Borders.getDefaultInstance());
@@ -183,10 +182,9 @@
int radiusToSet = 10;
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -204,10 +202,9 @@
RoundedCorners roundedCorners =
RoundedCorners.newBuilder().setRadiusPercentageOfHeight(radiusPercentageOfHeight).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -226,10 +223,9 @@
RoundedCorners roundedCorners =
RoundedCorners.newBuilder().setRadiusPercentageOfWidth(radiusPercentageOfWidth).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -247,10 +243,9 @@
int radiusToSet = 20;
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -273,10 +268,9 @@
.setRadiusDp(radiusToSet)
.build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -295,10 +289,9 @@
int radiusToSet = 20;
RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -321,10 +314,9 @@
.setRadiusDp(radiusToSet)
.build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -344,10 +336,9 @@
RoundedCorners roundedCorners =
RoundedCorners.newBuilder().setRadiusPercentageOfHeight(radiusPercentageOfHeight).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -369,10 +360,9 @@
.setRadiusPercentageOfHeight(radiusPercentageOfHeight)
.build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -395,10 +385,9 @@
.setRadiusPercentageOfHeight(radiusPercentageOfHeight)
.build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -418,10 +407,9 @@
RoundedCorners roundedCorners =
RoundedCorners.newBuilder().setRadiusPercentageOfWidth(radiusPercentageOfWidth).build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -444,10 +432,9 @@
.setRadiusPercentageOfWidth(radiusPercentageOfWidth)
.build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -470,10 +457,9 @@
.setRadiusPercentageOfWidth(radiusPercentageOfWidth)
.build();
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new CommonRoundedCornerWrapperView(
context,
roundedCorners,
- maskCache,
Suppliers.of(false),
/*radiusOverride= */ 0,
Borders.getDefaultInstance());
@@ -497,7 +483,7 @@
when(mockMaskCache.getMasks(16)).thenReturn(mockBitmaps);
when(mockBitmaps.get(anyInt())).thenReturn(maskBitmap);
RoundedCornerWrapperView view =
- new RoundedCornerWrapperView(
+ new BitmapMaskingRoundedCornerWrapperView(
context,
roundedCorners,
mockMaskCache,
@@ -513,4 +499,15 @@
verify(mockBitmaps).get(Corner.BOTTOM_LEFT);
verifyNoMoreInteractions(mockBitmaps);
}
+
+ static class CommonRoundedCornerWrapperView extends RoundedCornerWrapperView {
+ CommonRoundedCornerWrapperView(
+ Context context,
+ RoundedCorners roundedCorners,
+ Supplier<Boolean> isRtLSupplier,
+ int radiusOverride,
+ Borders borders) {
+ super(context, roundedCorners, isRtLSupplier, radiusOverride, borders);
+ }
+ }
}