diff options
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 19 | ||||
| -rw-r--r-- | core/java/com/android/internal/util/ContrastColorUtil.java | 9 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/ViewRootImplTest.java | 141 |
3 files changed, 119 insertions, 50 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4e3ff9063179..ec9f1cf8afd6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -189,6 +189,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.RenderNode; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.hardware.SyncFence; @@ -292,6 +293,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneFallbackEventHandler; import com.android.internal.protolog.ProtoLog; +import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.FastPrintWriter; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; @@ -2078,12 +2080,21 @@ public final class ViewRootImpl implements ViewParent, // preference for dark mode in configuration.uiMode. Instead, we assume that both // force invert and the system's dark theme are enabled. if (shouldApplyForceInvertDark()) { - final boolean isLightTheme = - a.getBoolean(R.styleable.Theme_isLightTheme, false); - // TODO: b/372558459 - Also check the background ColorDrawable color lightness // TODO: b/368725782 - Use hwui color area detection instead of / in // addition to these heuristics. - if (isLightTheme) { + final boolean isLightTheme = + a.getBoolean(R.styleable.Theme_isLightTheme, false); + final boolean isBackgroundColorLight; + if (mView != null && mView.getBackground() + instanceof ColorDrawable colorDrawable) { + isBackgroundColorLight = + !ContrastColorUtil.isColorDarkLab(colorDrawable.getColor()); + } else { + // Treat unknown as light, so that only isLightTheme is used to determine + // force dark treatment. + isBackgroundColorLight = true; + } + if (isLightTheme && isBackgroundColorLight) { return ForceDarkType.FORCE_INVERT_COLOR_DARK; } else { return ForceDarkType.NONE; diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java index 0fd139188665..c68f107951ac 100644 --- a/core/java/com/android/internal/util/ContrastColorUtil.java +++ b/core/java/com/android/internal/util/ContrastColorUtil.java @@ -41,8 +41,6 @@ import android.text.style.TextAppearanceSpan; import android.util.Log; import android.util.Pair; -import com.android.internal.annotations.VisibleForTesting; - import java.util.Arrays; import java.util.WeakHashMap; @@ -381,6 +379,13 @@ public class ContrastColorUtil { return calculateLuminance(color) <= 0.17912878474; } + /** Like {@link #isColorDark(int)} but converts to LAB before checking the L component. */ + public static boolean isColorDarkLab(int color) { + final double[] result = ColorUtilsFromCompat.getTempDouble3Array(); + ColorUtilsFromCompat.colorToLAB(color, result); + return result[0] < 50; + } + private int processColor(int color) { return Color.argb(Color.alpha(color), 255 - Color.red(color), diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 5774109e1451..1b7805c351db 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -16,8 +16,6 @@ package android.view; -import static android.app.UiModeManager.MODE_NIGHT_NO; -import static android.app.UiModeManager.MODE_NIGHT_YES; import static android.util.SequenceUtils.getInitSeq; import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING; import static android.view.InputDevice.SOURCE_ROTARY_ENCODER; @@ -69,10 +67,9 @@ import static org.junit.Assume.assumeTrue; import android.annotation.NonNull; import android.app.Instrumentation; import android.app.UiModeManager; -import android.app.UiModeManager.ForceInvertType; import android.content.Context; +import android.graphics.Color; import android.graphics.ForceDarkType; -import android.graphics.ForceDarkType.ForceDarkTypeDef; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; @@ -101,8 +98,6 @@ import com.android.compatibility.common.util.TestUtils; import com.android.cts.input.BlockingQueueEventVerifier; import com.android.window.flags.Flags; -import com.google.common.truth.Expect; - import org.hamcrest.Matcher; import org.junit.After; import org.junit.AfterClass; @@ -131,8 +126,6 @@ public class ViewRootImplTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - @Rule - public final Expect mExpect = Expect.create(); private ViewRootImpl mViewRootImpl; private View mView; @@ -1516,29 +1509,83 @@ public class ViewRootImplTest { } @Test - @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR) - public void updateConfiguration_returnsExpectedForceDarkMode() { + @EnableFlags(FLAG_FORCE_INVERT_COLOR) + public void determineForceDarkType_systemLightMode_returnsNone() throws Exception { + waitForSystemNightModeActivated(false); + + TestUtils.waitUntil("Waiting for ForceDarkType to be ready", + () -> (mViewRootImpl.determineForceDarkType() == ForceDarkType.NONE)); + + } + + @Test + @EnableFlags(FLAG_FORCE_INVERT_COLOR) + public void determineForceDarkType_systemNightModeAndDisableForceInvertColor_returnsNone() + throws Exception { waitForSystemNightModeActivated(true); - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK); - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); + enableForceInvertColor(false); - waitForSystemNightModeActivated(false); + TestUtils.waitUntil("Waiting for ForceDarkType to be ready", + () -> (mViewRootImpl.determineForceDarkType() == ForceDarkType.NONE)); + } + + @Test + @EnableFlags(FLAG_FORCE_INVERT_COLOR) + public void + determineForceDarkType_isLightThemeAndIsLightBackground_returnsForceInvertColorDark() + throws Exception { + // Set up configurations for force invert color + waitForSystemNightModeActivated(true); + enableForceInvertColor(true); + + setUpViewAttributes(/* isLightTheme= */ true, /* isLightBackground = */ true); - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); + TestUtils.waitUntil("Waiting for ForceDarkType to be ready", + () -> (mViewRootImpl.determineForceDarkType() + == ForceDarkType.FORCE_INVERT_COLOR_DARK)); + } + + @Test + @EnableFlags(FLAG_FORCE_INVERT_COLOR) + public void determineForceDarkType_isLightThemeAndNotLightBackground_returnsNone() + throws Exception { + // Set up configurations for force invert color + waitForSystemNightModeActivated(true); + enableForceInvertColor(true); + + setUpViewAttributes(/* isLightTheme= */ true, /* isLightBackground = */ false); + + TestUtils.waitUntil("Waiting for ForceDarkType to be ready", + () -> (mViewRootImpl.determineForceDarkType() == ForceDarkType.NONE)); + } + + @Test + @EnableFlags(FLAG_FORCE_INVERT_COLOR) + public void determineForceDarkType_notLightThemeAndIsLightBackground_returnsNone() + throws Exception { + // Set up configurations for force invert color + waitForSystemNightModeActivated(true); + enableForceInvertColor(true); + + setUpViewAttributes(/* isLightTheme= */ false, /* isLightBackground = */ true); + + TestUtils.waitUntil("Waiting for ForceDarkType to be ready", + () -> (mViewRootImpl.determineForceDarkType() == ForceDarkType.NONE)); + } + + @Test + @EnableFlags(FLAG_FORCE_INVERT_COLOR) + public void determineForceDarkType_notLightThemeAndNotLightBackground_returnsNone() + throws Exception { + // Set up configurations for force invert color + waitForSystemNightModeActivated(true); + enableForceInvertColor(true); + + setUpViewAttributes(/* isLightTheme= */ false, /* isLightBackground = */ false); + + TestUtils.waitUntil("Waiting for ForceDarkType to be ready", + () -> (mViewRootImpl.determineForceDarkType() == ForceDarkType.NONE)); } @Test @@ -1792,29 +1839,35 @@ public class ViewRootImplTest { sInstrumentation.waitForIdleSync(); } - private void verifyForceDarkType(boolean isAppInNightMode, boolean isForceInvertEnabled, - @ForceInvertType int expectedForceInvertType, - @ForceDarkTypeDef int expectedForceDarkType) { - var uiModeManager = sContext.getSystemService(UiModeManager.class); + private void enableForceInvertColor(boolean enabled) { ShellIdentityUtils.invokeWithShellPermissions(() -> { - uiModeManager.setApplicationNightMode( - isAppInNightMode ? MODE_NIGHT_YES : MODE_NIGHT_NO); Settings.Secure.putInt( sContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, - isForceInvertEnabled ? 1 : 0); + enabled ? 1 : 0 + ); }); + } - sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())); - try { - TestUtils.waitUntil("Waiting for force invert state changed", - () -> (uiModeManager.getForceInvertState() == expectedForceInvertType)); - } catch (Exception e) { - Log.e(TAG, "Unexpected error trying to apply force invert state. " + e); - e.printStackTrace(); - } + private void setUpViewAttributes(boolean isLightTheme, boolean isLightBackground) { + ShellIdentityUtils.invokeWithShellPermissions(() -> { + sContext.setTheme(isLightTheme ? android.R.style.Theme_DeviceDefault_Light + : android.R.style.Theme_DeviceDefault); + }); - mExpect.that(mViewRootImpl.determineForceDarkType()).isEqualTo(expectedForceDarkType); + sInstrumentation.runOnMainSync(() -> { + View view = new View(sContext); + WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( + TYPE_APPLICATION_OVERLAY); + layoutParams.token = new Binder(); + view.setLayoutParams(layoutParams); + if (isLightBackground) { + view.setBackgroundColor(Color.WHITE); + } else { + view.setBackgroundColor(Color.BLACK); + } + mViewRootImpl.setView(view, layoutParams, /* panelParentView= */ null); + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()); + }); } } |