summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tyler Freeman <fuego@google.com> 2024-02-16 22:03:52 +0000
committer Tyler Freeman <fuego@google.com> 2024-02-22 01:43:32 +0000
commiteca25fbcef13556225473d0bd69a9fd108a3caae (patch)
tree0f52d11c0e82b18bf48bbda6e487cc01719eb9c3
parent769510be8228916f4783c88cdf68f9369f0a07ac (diff)
fix(high contrast text): fix highlight/selection is obscured by high contrast text background rectangle
Since high contrast text draws a solid rectangle background behind the text, it covers up the highlights and selections. In this case we draw over the top of the text with a blend mode that ensures the text stays high-contrast. Fix: 323964406 Bug: 186567103 Flag: ACONFIG com.android.graphics.hwui.flags.high_contrast_text_small_text_rect TEAMFOOD Test: atest core/tests/coretests/src/android/text/LayoutTest.java Test: manual 1. adb shell setenforce 0 && adb shell setprop persist.device_config.aconfig_flags.accessibility.com.android.graphics.hwui.flags.high_contrast_text_small_text_rect true && adb shell stop && adb shell start 2. Settings -> Accessibility -> Display Size and Text 3. Turn on High Contrast Text 4. Select some text. The selection should be visible Change-Id: I3ea2835f2a8bdfaf646f140f3290837535080a5c
-rw-r--r--core/java/android/text/Layout.java92
-rw-r--r--core/java/android/widget/Editor.java18
-rw-r--r--core/tests/coretests/src/android/text/LayoutTest.java275
-rw-r--r--graphics/java/android/graphics/Canvas.java14
-rw-r--r--libs/hwui/hwui/DrawTextFunctor.h1
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp5
6 files changed, 394 insertions, 11 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 8e52af3fb57b..8dee4b19c6d3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,6 +16,7 @@
package android.text;
+import static com.android.graphics.hwui.flags.Flags.highContrastTextLuminance;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
@@ -28,7 +29,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.BlendMode;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -46,7 +49,9 @@ import android.text.style.ReplacementSpan;
import android.text.style.TabStopSpan;
import android.widget.TextView;
+import com.android.graphics.hwui.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -480,9 +485,23 @@ public abstract class Layout {
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
- cursorOffsetVertical, firstLine, lastLine);
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ drawBackground(canvas, firstLine, lastLine);
+ } else {
+ drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
drawText(canvas, firstLine, lastLine);
+
+ // Since high contrast text draws a solid rectangle background behind the text, it covers up
+ // the highlights and selections. In this case we draw over the top of the text with a
+ // blend mode that ensures the text stays high-contrast.
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
if (leftShift != 0) {
// Manually translate back to the original position because of b/324498002, using
// save/restore disappears the toggle switch drawables.
@@ -490,6 +509,19 @@ public abstract class Layout {
}
}
+ private static boolean shouldDrawHighlightsOnTop(Canvas canvas) {
+ return Flags.highContrastTextSmallTextRect() && canvas.isHighContrastTextEnabled();
+ }
+
+ private static Paint setToHighlightPaint(Paint p, BlendMode blendMode, Paint outPaint) {
+ if (p == null) return null;
+ outPaint.set(p);
+ outPaint.setBlendMode(blendMode);
+ // Yellow for maximum contrast
+ outPaint.setColor(Color.YELLOW);
+ return outPaint;
+ }
+
/**
* Draw text part of this layout.
*
@@ -542,11 +574,28 @@ public abstract class Layout {
int firstLine,
int lastLine) {
drawBackground(canvas, firstLine, lastLine);
+ drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
+ /**
+ * @hide public for Editor.java
+ */
+ public void drawHighlights(
+ @NonNull Canvas canvas,
+ @Nullable List<Path> highlightPaths,
+ @Nullable List<Paint> highlightPaints,
+ @Nullable Path selectionPath,
+ @Nullable Paint selectionPaint,
+ int cursorOffsetVertical,
+ int firstLine,
+ int lastLine) {
if (highlightPaths == null && highlightPaints == null) {
return;
}
if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
try {
+ BlendMode blendMode = determineHighContrastHighlightBlendMode(canvas);
if (highlightPaths != null) {
if (highlightPaints == null) {
throw new IllegalArgumentException(
@@ -559,7 +608,12 @@ public abstract class Layout {
}
for (int i = 0; i < highlightPaths.size(); ++i) {
final Path highlight = highlightPaths.get(i);
- final Paint highlightPaint = highlightPaints.get(i);
+ Paint highlightPaint = highlightPaints.get(i);
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ highlightPaint = setToHighlightPaint(highlightPaint, blendMode,
+ mWorkPlainPaint);
+ }
+
if (highlight != null) {
canvas.drawPath(highlight, highlightPaint);
}
@@ -567,6 +621,10 @@ public abstract class Layout {
}
if (selectionPath != null) {
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ selectionPaint = setToHighlightPaint(selectionPaint, blendMode,
+ mWorkPlainPaint);
+ }
canvas.drawPath(selectionPath, selectionPaint);
}
} finally {
@@ -574,6 +632,31 @@ public abstract class Layout {
}
}
+ @Nullable
+ private BlendMode determineHighContrastHighlightBlendMode(Canvas canvas) {
+ if (!shouldDrawHighlightsOnTop(canvas)) {
+ return null;
+ }
+
+ return isHighContrastTextDark() ? BlendMode.MULTIPLY : BlendMode.DIFFERENCE;
+ }
+
+ private boolean isHighContrastTextDark() {
+ // High-contrast text mode
+ // Determine if the text is black-on-white or white-on-black, so we know what blendmode will
+ // give the highest contrast and most realistic text color.
+ // This equation should match the one in libs/hwui/hwui/DrawTextFunctor.h
+ if (highContrastTextLuminance()) {
+ var lab = new double[3];
+ ColorUtils.colorToLAB(mPaint.getColor(), lab);
+ return lab[0] < 0.5;
+ } else {
+ var color = mPaint.getColor();
+ int channelSum = Color.red(color) + Color.green(color) + Color.blue(color);
+ return channelSum < (128 * 3);
+ }
+ }
+
private boolean isJustificationRequired(int lineNum) {
if (mJustificationMode == JUSTIFICATION_MODE_NONE) return false;
final int lineEnd = getLineEnd(lineNum);
@@ -3396,7 +3479,8 @@ public abstract class Layout {
private CharSequence mText;
@UnsupportedAppUsage
private TextPaint mPaint;
- private TextPaint mWorkPaint = new TextPaint();
+ private final TextPaint mWorkPaint = new TextPaint();
+ private final Paint mWorkPlainPaint = new Paint();
private int mWidth;
private Alignment mAlignment = Alignment.ALIGN_NORMAL;
private float mSpacingMult;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 57d268ced6f4..139ebc38706e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -19,6 +19,8 @@ package android.widget;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
+import static com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect;
+
import android.R;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -2151,8 +2153,15 @@ public class Editor {
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
- selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ boolean shouldDrawHighlightsOnTop = highContrastTextSmallTextRect()
+ && canvas.isHighContrastTextEnabled();
+
+ if (!shouldDrawHighlightsOnTop) {
+ layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ } else {
+ layout.drawBackground(canvas, firstLine, lastLine);
+ }
if (layout instanceof DynamicLayout) {
if (mTextRenderNodes == null) {
@@ -2226,6 +2235,11 @@ public class Editor {
// Boring layout is used for empty and hint text
layout.drawText(canvas, firstLine, lastLine);
}
+
+ if (shouldDrawHighlightsOnTop) {
+ layout.drawHighlights(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ }
}
private int drawHardwareAcceleratedInner(Canvas canvas, Layout layout, Path highlight,
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 5649e7118fd5..f60eff69690a 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -16,6 +16,11 @@
package android.text;
+import static com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -26,11 +31,16 @@ import static org.junit.Assert.fail;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.Layout.Alignment;
import android.text.style.StrikethroughSpan;
@@ -38,6 +48,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +60,9 @@ import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LayoutTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final int LINE_COUNT = 5;
private static final int LINE_HEIGHT = 12;
private static final int LINE_DESCENT = 4;
@@ -638,22 +652,268 @@ public class LayoutTest {
}
}
- private final class MockCanvas extends Canvas {
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testDrawSelectionAndHighlight_drawsHighContrastSelectionAndHighlight() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 2;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn on top of text")
+ .that(highlightsFound).isEqualTo(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(2);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testDrawHighlight_drawsHighContrastHighlight() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn on top of text")
+ .that(highlightsFound).isEqualTo(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextDisabledByDefault_testDrawHighlight_drawsNormalHighlightBehind() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ assertThat(drawCommand.paint.getBlendMode()).isNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn behind text")
+ .that(highlightsFound).isGreaterThan(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabledButFlagOff_testDrawHighlight_drawsNormalHighlightBehind() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
- class DrawCommand {
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ assertThat(drawCommand.paint.getBlendMode()).isNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn behind text")
+ .that(highlightsFound).isGreaterThan(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ public void mockCanvasHighContrastOverridesCorrectly() {
+ var canvas = new MockCanvas(100, 100);
+
+ assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ canvas.setHighContrastTextEnabled(true);
+ assertThat(canvas.isHighContrastTextEnabled()).isTrue();
+ canvas.setHighContrastTextEnabled(false);
+ assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ }
+
+ private static final class MockCanvas extends Canvas {
+
+ static class DrawCommand {
public final String text;
public final float x;
public final float y;
+ public final Path path;
+ public final Paint paint;
- DrawCommand(String text, float x, float y) {
+ DrawCommand(String text, float x, float y, Paint paint) {
this.text = text;
this.x = x;
this.y = y;
+ this.paint = paint;
+ path = null;
+ }
+
+ DrawCommand(Path path, Paint paint) {
+ this.path = path;
+ this.paint = paint;
+ y = 0;
+ x = 0;
+ text = null;
}
}
List<DrawCommand> mDrawCommands;
+ private Boolean mIsHighContrastTextOverride = null;
+
+ public void setHighContrastTextEnabled(boolean enabled) {
+ mIsHighContrastTextOverride = enabled;
+ }
+
+ @Override
+ public boolean isHighContrastTextEnabled() {
+ return mIsHighContrastTextOverride == null ? super.isHighContrastTextEnabled()
+ : mIsHighContrastTextOverride;
+ }
+
MockCanvas(int width, int height) {
super();
mDrawCommands = new ArrayList<>();
@@ -666,7 +926,7 @@ public class LayoutTest {
@Override
public void drawText(String text, int start, int end, float x, float y, Paint p) {
- mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y));
+ mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y, p));
}
@Override
@@ -676,7 +936,7 @@ public class LayoutTest {
@Override
public void drawText(char[] text, int index, int count, float x, float y, Paint p) {
- mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y));
+ mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y, p));
}
@Override
@@ -691,6 +951,11 @@ public class LayoutTest {
drawText(text, index, count, x, y, paint);
}
+ @Override
+ public void drawPath(Path path, Paint p) {
+ mDrawCommands.add(new DrawCommand(path, p));
+ }
+
List<DrawCommand> getDrawCommands() {
return mDrawCommands;
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index f3bb21719890..b6ce9b64323b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -153,6 +153,18 @@ public class Canvas extends BaseCanvas {
}
/**
+ * Indicates whether this Canvas is drawing high contrast text.
+ *
+ * @see android.view.accessibility.AccessibilityManager#isHighTextContrastEnabled()
+ * @return True if high contrast text is enabled, false otherwise.
+ *
+ * @hide
+ */
+ public boolean isHighContrastTextEnabled() {
+ return nIsHighContrastText(mNativeCanvasWrapper);
+ }
+
+ /**
* Specify a bitmap for the canvas to draw into. All canvas state such as
* layers, filters, and the save/restore stack are reset. Additionally,
* the canvas' target density is updated to match that of the bitmap.
@@ -1452,6 +1464,8 @@ public class Canvas extends BaseCanvas {
@CriticalNative
private static native boolean nIsOpaque(long canvasHandle);
@CriticalNative
+ private static native boolean nIsHighContrastText(long canvasHandle);
+ @CriticalNative
private static native int nGetWidth(long canvasHandle);
@CriticalNative
private static native int nGetHeight(long canvasHandle);
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 02bf0d8d5e95..1fcb6920db14 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -92,6 +92,7 @@ public:
// high contrast draw path
int color = paint.getColor();
bool darken;
+ // This equation should match the one in core/java/android/text/Layout.java
if (flags::high_contrast_text_luminance()) {
uirenderer::Lab lab = uirenderer::sRGBToLab(color);
darken = lab.L <= 50;
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 1fc34d633370..9b63a46822ac 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -88,6 +88,10 @@ static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHand
get_canvas(canvasHandle)->setBitmap(bitmap);
}
+static jboolean isHighContrastText(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+ return get_canvas(canvasHandle)->isHighContrastText() ? JNI_TRUE : JNI_FALSE;
+}
+
static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
}
@@ -792,6 +796,7 @@ static const JNINativeMethod gMethods[] = {
// ------------ @CriticalNative ----------------
{"nIsOpaque", "(J)Z", (void*)CanvasJNI::isOpaque},
+ {"nIsHighContrastText", "(J)Z", (void*)CanvasJNI::isHighContrastText},
{"nGetWidth", "(J)I", (void*)CanvasJNI::getWidth},
{"nGetHeight", "(J)I", (void*)CanvasJNI::getHeight},
{"nSave", "(JI)I", (void*)CanvasJNI::save},