diff options
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 69 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/ViewRootImplTest.java | 115 |
2 files changed, 95 insertions, 89 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index cd8a85a66c1a..bb56399102ff 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -149,6 +149,8 @@ import android.annotation.UiContext; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ResourcesManager; +import android.app.UiModeManager; +import android.app.UiModeManager.ForceInvertStateChangeListener; import android.app.WindowConfiguration; import android.app.compat.CompatChanges; import android.app.servertransaction.WindowStateTransactionItem; @@ -164,7 +166,6 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.database.ContentObserver; import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Color; @@ -210,7 +211,6 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.Vibrator; -import android.provider.Settings; import android.sysprop.DisplayProperties; import android.sysprop.ViewProperties; import android.text.TextUtils; @@ -463,10 +463,8 @@ public final class ViewRootImpl implements ViewParent, private CompatOnBackInvokedCallback mCompatOnBackInvokedCallback; @Nullable - private ContentObserver mForceInvertObserver; + private ForceInvertStateChangeListener mForceInvertStateChangeListener; - private static final int INVALID_VALUE = Integer.MIN_VALUE; - private int mForceInvertEnabled = INVALID_VALUE; /** * Callback for notifying about global configuration changes. */ @@ -543,6 +541,8 @@ public final class ViewRootImpl implements ViewParent, @UiContext public final Context mContext; + private UiModeManager mUiModeManager; + @UnsupportedAppUsage final IWindowSession mWindowSession; @NonNull Display mDisplay; @@ -1238,6 +1238,7 @@ public final class ViewRootImpl implements ViewParent, public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, WindowLayout windowLayout) { mContext = context; + mUiModeManager = context.getSystemService(UiModeManager.class); mWindowSession = session; mWindowLayout = windowLayout; mDisplay = display; @@ -1781,23 +1782,6 @@ public final class ViewRootImpl implements ViewParent, } } - private boolean isForceInvertEnabled() { - if (mForceInvertEnabled == INVALID_VALUE) { - reloadForceInvertEnabled(); - } - return mForceInvertEnabled == 1; - } - - private void reloadForceInvertEnabled() { - if (forceInvertColor()) { - mForceInvertEnabled = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, - /* def= */ 0, - UserHandle.myUserId()); - } - } - /** * Register any kind of listeners if setView was success. */ @@ -1829,21 +1813,11 @@ public final class ViewRootImpl implements ViewParent, mBasePackageName); if (forceInvertColor()) { - if (mForceInvertObserver == null) { - mForceInvertObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - reloadForceInvertEnabled(); - updateForceDarkMode(); - } - }; - mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED - ), - false, - mForceInvertObserver, - UserHandle.myUserId()); + if (mForceInvertStateChangeListener == null) { + mForceInvertStateChangeListener = + forceInvertState -> updateForceDarkMode(); + mUiModeManager.addForceInvertStateChangeListener(mExecutor, + mForceInvertStateChangeListener); } } } @@ -1861,9 +1835,10 @@ public final class ViewRootImpl implements ViewParent, .unregisterDisplayListener(mDisplayListener); if (forceInvertColor()) { - if (mForceInvertObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mForceInvertObserver); - mForceInvertObserver = null; + if (mForceInvertStateChangeListener != null) { + mUiModeManager.removeForceInvertStateChangeListener( + mForceInvertStateChangeListener); + mForceInvertStateChangeListener = null; } } @@ -2050,21 +2025,25 @@ public final class ViewRootImpl implements ViewParent, return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; } - /** Returns true if force dark should be enabled according to various settings */ + /** + * Determines the type of force dark to apply, considering force inversion, system night mode, + * and app-specific settings (including developer opt-outs). + * + * @return A {@link ForceDarkType.ForceDarkTypeDef} constant indicating the force dark type. + */ @VisibleForTesting public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() { if (forceInvertColor()) { // Force invert ignores all developer opt-outs. // We also ignore dark theme, since the app developer can override the user's preference - // for dark mode in configuration.uiMode. Instead, we assume that the force invert - // setting will be enabled at the same time dark theme is in the Settings app. - if (isForceInvertEnabled()) { + // for dark mode in configuration.uiMode. Instead, we assume that both force invert and + // the system's dark theme are enabled. + if (mUiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) { return ForceDarkType.FORCE_INVERT_COLOR_DARK; } } boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES; - if (useAutoDark) { boolean forceDarkAllowedDefault = SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index c40137f1bd34..a289df0441e5 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -16,6 +16,8 @@ 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; @@ -67,8 +69,10 @@ 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.ForceDarkType; +import android.graphics.ForceDarkType.ForceDarkTypeDef; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; @@ -93,9 +97,12 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.ShellIdentityUtils; +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; @@ -124,6 +131,8 @@ public class ViewRootImplTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public final Expect mExpect = Expect.create(); private ViewRootImpl mViewRootImpl; private View mView; @@ -1507,49 +1516,34 @@ public class ViewRootImplTest { } @Test - public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { - mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); - ShellIdentityUtils.invokeWithShellPermissions(() -> { - Settings.Secure.putInt( - sContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, - /* value= */ 0 - ); - var uiModeManager = sContext.getSystemService(UiModeManager.class); - uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); - }); - - sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) - ); - - assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE); - } - - @Test - public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() { - mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); - ShellIdentityUtils.invokeWithShellPermissions(() -> { - Settings.Secure.putInt( - sContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, - /* value= */ 1 - ); - var uiModeManager = sContext.getSystemService(UiModeManager.class); - uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); - }); - - sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) - ); - - assertThat(mViewRootImpl.determineForceDarkType()) - .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK); + @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR) + public void updateConfiguration_returnsExpectedForceDarkMode() { + 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); + + waitForSystemNightModeActivated(false); + + 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); } @Test + @EnableFlags(FLAG_FORCE_INVERT_COLOR) public void forceInvertOffForceDarkOff_forceDarkModeDisabled() { - mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); ShellIdentityUtils.invokeWithShellPermissions(() -> { Settings.Secure.putInt( sContext.getContentResolver(), @@ -1562,15 +1556,14 @@ public class ViewRootImplTest { }); sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) - ); + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())); assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE); } @Test + @EnableFlags(FLAG_FORCE_INVERT_COLOR) public void forceInvertOffForceDarkOn_forceDarkModeEnabled() { - mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); ShellIdentityUtils.invokeWithShellPermissions(() -> { Settings.Secure.putInt( sContext.getContentResolver(), @@ -1582,8 +1575,7 @@ public class ViewRootImplTest { }); sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) - ); + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())); assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK); } @@ -1790,4 +1782,39 @@ public class ViewRootImplTest { () -> view.getViewTreeObserver().removeOnDrawListener(listener)); } } + + private void waitForSystemNightModeActivated(boolean active) { + ShellIdentityUtils.invokeWithShellPermissions(() -> + sInstrumentation.runOnMainSync(() -> { + var uiModeManager = sContext.getSystemService(UiModeManager.class); + uiModeManager.setNightModeActivated(active); + })); + sInstrumentation.waitForIdleSync(); + } + + private void verifyForceDarkType(boolean isAppInNightMode, boolean isForceInvertEnabled, + @ForceInvertType int expectedForceInvertType, + @ForceDarkTypeDef int expectedForceDarkType) { + var uiModeManager = sContext.getSystemService(UiModeManager.class); + 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); + }); + + 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(); + } + + mExpect.that(mViewRootImpl.determineForceDarkType()).isEqualTo(expectedForceDarkType); + } } |