Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa283a3d authored by Tyler Freeman's avatar Tyler Freeman Committed by Android (Google) Code Review
Browse files

Merge changes I425b401c,I149c782c into main

* changes:
  feat(force invert): force force-dark if force invert is enabled
  chore(force invert): add feature flag for force invert and dark theme everywhere
parents 863f415e 0a016bba
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ aconfig_srcjars = [
    ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
    ":android.security.flags-aconfig-java{.generated_srcjars}",
    ":android.view.flags-aconfig-java{.generated_srcjars}",
    ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
    ":camera_platform_flags_core_java_lib{.generated_srcjars}",
    ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
    ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
@@ -240,6 +241,24 @@ java_aconfig_library {
    defaults: ["framework-minus-apex-aconfig-java-defaults"],
}

// View.accessibility
aconfig_declarations {
    name: "android.view.accessibility.flags-aconfig",
    package: "android.view.accessibility",
    srcs: ["core/java/android/view/accessibility/flags/*.aconfig"],
}

java_aconfig_library {
    name: "android.view.accessibility.flags-aconfig-java",
    aconfig_declarations: "android.view.accessibility.flags-aconfig",
    defaults: ["framework-minus-apex-aconfig-java-defaults"],
}

cc_aconfig_library {
    name: "aconfig_view_accessibility_flags_c_lib",
    aconfig_declarations: "android.view.accessibility.flags-aconfig",
}

// Widget
aconfig_declarations {
    name: "android.widget.flags-aconfig",
+24 −3
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_B
import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.accessibility.Flags.forceInvertColor;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;

@@ -154,6 +155,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
@@ -1744,8 +1746,23 @@ public final class ViewRootImpl implements ViewParent,
        return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
    }

    private void updateForceDarkMode() {
        if (mAttachInfo.mThreadedRenderer == null) return;
    /** Returns true if force dark should be enabled according to various settings */
    @VisibleForTesting
    public boolean isForceDarkEnabled() {
        if (forceInvertColor()) {
            boolean isForceInvertEnabled = Settings.Secure.getIntForUser(
                    mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
                    /* def= */ 0,
                    UserHandle.myUserId()) == 1;
            // 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) {
                return true;
            }
        }

        boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;

@@ -1757,8 +1774,12 @@ public final class ViewRootImpl implements ViewParent,
                    && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault);
            a.recycle();
        }
        return useAutoDark;
    }

        if (mAttachInfo.mThreadedRenderer.setForceDark(useAutoDark)) {
    private void updateForceDarkMode() {
        if (mAttachInfo.mThreadedRenderer == null) return;
        if (mAttachInfo.mThreadedRenderer.setForceDark(isForceDarkEnabled())) {
            // TODO: Don't require regenerating all display lists to apply this setting
            invalidateWorld(mView);
        }
+8 −0
Original line number Diff line number Diff line
package: "android.view.accessibility"

flag {
    namespace: "accessibility"
    name: "force_invert_color"
    description: "Enable force force-dark for smart inversion and dark theme everywhere"
    bug: "239594271"
}
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -44,12 +44,14 @@ android_test {
        "frameworks-core-util-lib",
        "mockwebserver",
        "guava",
        "android.view.accessibility.flags-aconfig-java",
        "androidx.core_core",
        "androidx.core_core-ktx",
        "androidx.test.espresso.core",
        "androidx.test.ext.junit",
        "androidx.test.runner",
        "androidx.test.rules",
        "flag-junit",
        "junit-params",
        "kotlin-test",
        "mockito-target-minus-junit4",
+121 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view;

import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -38,12 +39,18 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;

import android.app.Instrumentation;
import android.app.UiModeManager;
import android.content.Context;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.SystemProperties;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;

@@ -52,9 +59,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.compatibility.common.util.ShellIdentityUtils;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@@ -71,6 +82,10 @@ import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ViewRootImplTest {
    private static final String TAG = "ViewRootImplTest";

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private ViewRootImpl mViewRootImpl;
    private volatile boolean mKeyReceived = false;
@@ -101,6 +116,18 @@ public class ViewRootImplTest {
                mViewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify()));
    }

    @After
    public void teardown() {
        ShellIdentityUtils.invokeWithShellPermissions(() -> {
            Settings.Secure.resetToDefaults(sContext.getContentResolver(), TAG);

            var uiModeManager = sContext.getSystemService(UiModeManager.class);
            uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);

            setForceDarkSysProp(false);
        });
    }

    @Test
    public void adjustLayoutParamsForCompatibility_layoutFullscreen() {
        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
@@ -400,6 +427,100 @@ public class ViewRootImplTest {
        assertThat(result).isFalse();
    }

    @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.isForceDarkEnabled()).isFalse();
    }

    @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.isForceDarkEnabled()).isTrue();
    }

    @Test
    public void forceInvertOffForceDarkOff_forceDarkModeDisabled() {
        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
        ShellIdentityUtils.invokeWithShellPermissions(() -> {
            Settings.Secure.putInt(
                    sContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
                    /* value= */ 0
            );

            // TODO(b/297556388): figure out how to set this without getting blocked by SELinux
            assumeTrue(setForceDarkSysProp(true));
        });

        sInstrumentation.runOnMainSync(() ->
                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
        );

        assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse();
    }

    @Test
    public void forceInvertOffForceDarkOn_forceDarkModeEnabled() {
        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
        ShellIdentityUtils.invokeWithShellPermissions(() -> {
            Settings.Secure.putInt(
                    sContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
                    /* value= */ 0
            );

            assumeTrue(setForceDarkSysProp(true));
        });

        sInstrumentation.runOnMainSync(() ->
                mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
        );

        assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue();
    }

    private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
        try {
            SystemProperties.set(
                    ThreadedRenderer.DEBUG_FORCE_DARK,
                    Boolean.toString(isForceDarkEnabled)
            );
            return true;
        } catch (Exception e) {
            Log.e(TAG, "Failed to set force_dark sysprop", e);
            return false;
        }
    }

    class KeyView extends View {
        KeyView(Context context) {
            super(context);