Skip to content

Commit

Permalink
Outline Android version checks
Browse files Browse the repository at this point in the history
Summary: As per title - outlined Android version checks. This may help to avoid verification issues as described in [this post](https://medium.com/androiddevelopers/mitigating-soft-verification-issues-in-r8-and-d8-7e9e06827dfd). Redex team confirmed that outlining is a good practice.

Reviewed By: astreet

Differential Revision: D55683187

fbshipit-source-id: 791bb53341cd689ebaa11f69d6967ad5ac90511a
  • Loading branch information
zielinskimz authored and facebook-github-bot committed Apr 4, 2024
1 parent 902fefd commit 7d013e9
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 33 deletions.
12 changes: 11 additions & 1 deletion litho-core/src/main/java/com/facebook/litho/LithoNodeUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.SparseArray
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.core.view.ViewCompat
import com.facebook.litho.Component.MountType
import com.facebook.litho.MountSpecLithoRenderUnit.UpdateState
Expand Down Expand Up @@ -448,7 +450,7 @@ object LithoNodeUtils {
if (disableBgFgOutputs || !attrs.isHostSpec) {
attrs.background = result.node.background
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
attrs.foreground = lithoNode.foreground
AndroidMImpl.setForeground(attrs, lithoNode.foreground)
}
}
if (result.node.isPaddingSet) {
Expand All @@ -469,3 +471,11 @@ object LithoNodeUtils {
return attrs
}
}

@RequiresApi(Build.VERSION_CODES.M)
private object AndroidMImpl {
@DoNotInline
fun setForeground(attrs: ViewAttributes, foreground: Drawable?) {
attrs.foreground = foreground
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import androidx.annotation.ColorInt
import androidx.annotation.DoNotInline
import androidx.annotation.IdRes
import androidx.annotation.RequiresApi
import androidx.core.view.ViewCompat
import com.facebook.litho.LithoViewAttributesExtension.LithoViewAttributesState
import com.facebook.litho.LithoViewAttributesExtension.ViewAttributesInput
Expand Down Expand Up @@ -592,13 +594,13 @@ class LithoViewAttributesExtension private constructor() :

private fun setAmbientShadowColor(view: View, @ColorInt ambientShadowColor: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
view.outlineAmbientShadowColor = ambientShadowColor
AndroidPImpl.setAmbientShadowColor(view, ambientShadowColor)
}
}

private fun setSpotShadowColor(view: View, @ColorInt spotShadowColor: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
view.outlineSpotShadowColor = spotShadowColor
AndroidPImpl.setSpotShadowColor(view, spotShadowColor)
}
}

Expand All @@ -612,38 +614,38 @@ class LithoViewAttributesExtension private constructor() :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && ambientShadowColor != Color.BLACK) {
// Android documentation says black is the default:
// https://developer.android.com/reference/android/view/View#getOutlineAmbientShadowColor()
view.outlineAmbientShadowColor = Color.BLACK
AndroidPImpl.setAmbientShadowColor(view, Color.BLACK)
}
}

private fun unsetSpotShadowColor(view: View, @ColorInt spotShadowColor: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && spotShadowColor != Color.BLACK) {
// Android documentation says black is the default:
// https://developer.android.com/reference/android/view/View#getOutlineSpotShadowColor()
view.outlineSpotShadowColor = Color.BLACK
AndroidPImpl.setSpotShadowColor(view, Color.BLACK)
}
}

private fun setOutlineProvider(view: View, outlineProvider: ViewOutlineProvider?) {
if (outlineProvider != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (outlineProvider != null) {
view.outlineProvider = outlineProvider
}
}

private fun unsetOutlineProvider(view: View, outlineProvider: ViewOutlineProvider?) {
if (outlineProvider != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (outlineProvider != null) {
view.outlineProvider = ViewOutlineProvider.BACKGROUND
}
}

private fun setClipToOutline(view: View, clipToOutline: Boolean) {
if (clipToOutline && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (clipToOutline) {
view.clipToOutline = clipToOutline
}
}

private fun unsetClipToOutline(view: View, clipToOutline: Boolean) {
if (clipToOutline && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (clipToOutline) {
view.clipToOutline = false
}
}
Expand Down Expand Up @@ -880,9 +882,6 @@ class LithoViewAttributesExtension private constructor() :
if (stateListAnimator == null && stateListAnimatorRes == 0) {
return
}
check(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
("MountState has a ViewAttributes with stateListAnimator, however the current Android version doesn't support stateListAnimator on Views")
}
if (stateListAnimator == null) {
stateListAnimator =
AnimatorInflater.loadStateListAnimator(view.context, stateListAnimatorRes)
Expand All @@ -894,9 +893,6 @@ class LithoViewAttributesExtension private constructor() :
if (attributes.stateListAnimator == null && attributes.stateListAnimatorRes == 0) {
return
}
check(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
("MountState has a ViewAttributes with stateListAnimator, however the current Android version doesn't support stateListAnimator on Views")
}
view.stateListAnimator.jumpToCurrentState()
view.stateListAnimator = null
}
Expand All @@ -922,3 +918,16 @@ class LithoViewAttributesExtension private constructor() :
): Boolean = !equals(currentAttributes, nextAttributes)
}
}

@RequiresApi(Build.VERSION_CODES.P)
private object AndroidPImpl {
@DoNotInline
fun setAmbientShadowColor(view: View, @ColorInt ambientShadowColor: Int) {
view.outlineAmbientShadowColor = ambientShadowColor
}

@DoNotInline
fun setSpotShadowColor(view: View, @ColorInt spotShadowColor: Int) {
view.outlineSpotShadowColor = spotShadowColor
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.DoNotInline;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.view.ViewCompat;
import com.facebook.litho.ComponentContext;
import com.facebook.litho.ComponentLayout;
Expand Down Expand Up @@ -704,7 +707,7 @@ private static void initEditText(

if (cursorDrawableRes != -1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
editText.setTextCursorDrawable(cursorDrawableRes);
AndroidQImpl.setTextCursorDrawable(editText, cursorDrawableRes);
} else {
try {
// Uses reflection because there is no public API to change cursor color programmatically.
Expand Down Expand Up @@ -943,4 +946,14 @@ private static Layout.Alignment getAlignment(int gravity) {
}
return alignment;
}

@RequiresApi(Build.VERSION_CODES.Q)
private static class AndroidQImpl {
private AndroidQImpl() {}

@DoNotInline
static void setTextCursorDrawable(EditText editText, @DrawableRes int cursorDrawableRes) {
editText.setTextCursorDrawable(cursorDrawableRes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
import android.view.View;
import android.view.ViewConfiguration;
import androidx.annotation.ColorInt;
import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import com.facebook.fbui.textlayoutbuilder.util.LayoutMeasureUtil;
import com.facebook.litho.TextContent;
Expand Down Expand Up @@ -484,9 +486,7 @@ public void setTextColor(@ColorInt int textColor) {

public void setOutline(float outlineWidth, @ColorInt int outlineColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mOutlineWidth = outlineWidth;
mOutlineColor = outlineColor;
invalidateSelf();
AndroidQImpl.setOutline(this, outlineWidth, outlineColor);
}
}

Expand Down Expand Up @@ -873,4 +873,17 @@ public void run() {
|| longClickableSpan.onLongClick(longClickableSpanView);
}
}

@RequiresApi(Build.VERSION_CODES.Q)
private static class AndroidQImpl {

private AndroidQImpl() {}

@DoNotInline
static void setOutline(TextDrawable drawable, float outlineWidth, @ColorInt int outlineColor) {
drawable.mOutlineWidth = outlineWidth;
drawable.mOutlineColor = outlineColor;
drawable.invalidateSelf();
}
}
}
47 changes: 33 additions & 14 deletions litho-widget/src/main/java/com/facebook/litho/widget/TextSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import android.text.style.ImageSpan;
import android.view.View;
import androidx.annotation.Dimension;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
Expand Down Expand Up @@ -851,7 +852,7 @@ private static CharSequence truncateText(
int ellipsisOffset;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ellipsisOffset =
getEllipsisOffsetFromPaintAdvance(
AndroidMImpl.getEllipsisOffsetFromPaintAdvance(
newLayout, text, isRtl, ellipsizedLineNumber, ellipsisTarget);
} else {
ellipsisOffset = newLayout.getOffsetForHorizontal(ellipsizedLineNumber, ellipsisTarget);
Expand Down Expand Up @@ -884,9 +885,7 @@ private static CharSequence truncateText(
&& ellipsisOffset != 0
&& ellipsisOffset != text.length()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
BreakIterator iterator = BreakIterator.getCharacterInstance();
iterator.setText(text);
ellipsisOffset = iterator.preceding(ellipsisOffset);
ellipsisOffset = AndroidQImpl.breakIteratorGetPreceding(text, ellipsisOffset);
} else {
java.text.BreakIterator iterator = java.text.BreakIterator.getCharacterInstance();
iterator.setText(text.toString());
Expand All @@ -900,16 +899,6 @@ private static CharSequence truncateText(
}
}

@RequiresApi(api = Build.VERSION_CODES.M)
private static int getEllipsisOffsetFromPaintAdvance(
Layout layout, CharSequence text, boolean isRtl, int line, float advance) {
Paint paint = layout.getPaint();
int lineStart = layout.getLineStart(line);
int lineEnd = layout.getLineEnd(line);

return paint.getOffsetForAdvance(text, lineStart, lineEnd, lineStart, lineEnd, isRtl, advance);
}

/**
* @param layout A prepared text layout object
* @return The (zero-indexed) line number at which the text in this layout will be ellipsized, or
Expand Down Expand Up @@ -1301,4 +1290,34 @@ private static Alignment getLayoutAlignment(
}
return alignment;
}

@RequiresApi(Build.VERSION_CODES.M)
private static class AndroidMImpl {

private AndroidMImpl() {}

@DoNotInline
static int getEllipsisOffsetFromPaintAdvance(
Layout layout, CharSequence text, boolean isRtl, int line, float advance) {
Paint paint = layout.getPaint();
int lineStart = layout.getLineStart(line);
int lineEnd = layout.getLineEnd(line);

return paint.getOffsetForAdvance(
text, lineStart, lineEnd, lineStart, lineEnd, isRtl, advance);
}
}

@RequiresApi(Build.VERSION_CODES.Q)
private static class AndroidQImpl {

private AndroidQImpl() {}

@DoNotInline
static int breakIteratorGetPreceding(CharSequence text, int ellipsisOffset) {
BreakIterator iterator = BreakIterator.getCharacterInstance();
iterator.setText(text);
return iterator.preceding(ellipsisOffset);
}
}
}

0 comments on commit 7d013e9

Please sign in to comment.