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

Commit 8a414be0 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "fix(edt): Add support for a package blocklist for expanded Dark theme" into main

parents 9eb93cbb 3782713b
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -1559,6 +1560,22 @@ public class UiModeManager {
        return sGlobals.getForceInvertState();
    }

    /**
     * Returns {@code true} if the provided context allows applying force invert, otherwise
     * {@code false}.
     *
     * @hide
     */
    public static boolean isForceInvertAllowed(Context context) {
        final String packageName = context.getPackageName();
        final String[] packageBlocklist = context.getResources().getStringArray(
                com.android.internal.R.array.config_forceInvertPackageBlocklist);
        if (packageName != null && Arrays.asList(packageBlocklist).contains(packageName)) {
            return false;
        }
        return true;
    }

    /**
     * Registers a {@link ForceInvertStateChangeListener} for the current user.
     *
+14 −17
Original line number Diff line number Diff line
@@ -2036,22 +2036,15 @@ public final class ViewRootImpl implements ViewParent,
                }
            }
            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 both
                // force invert and the system's dark theme are enabled.
            if (shouldApplyForceInvertDark()) {
                    // We will use HWUI color area detection to determine if it should actually be
                    // inverted. Checking light theme simply gives the developer a way to "opt-out"
                    // of force invert.
                    final boolean isLightTheme =
                            a.getBoolean(R.styleable.Theme_isLightTheme, false);
                    if (isLightTheme) {
                // Do not apply force invert dark theme to an app that already declares itself as
                // supporting dark theme (isLightTheme=false). This gives the developer a way to
                // opt out of allowing this behavior, while also guaranteeing that apps with a
                // properly configured dark theme are unaffected by force invert dark theme. For
                // self-declared light theme apps HWUI then performs its own "color area"
                // calculation to determine if the app actually renders with light colors.
                if (a.getBoolean(R.styleable.Theme_isLightTheme, false)) {
                    return ForceDarkType.FORCE_INVERT_COLOR_DARK;
                    } else {
                        return ForceDarkType.NONE;
                    }
                }
            }
@@ -2062,11 +2055,15 @@ public final class ViewRootImpl implements ViewParent,
    }
    private boolean shouldApplyForceInvertDark() {
        if (!forceInvertColor()) {
            return false;
        }
        final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
        if (uiModeManager == null) {
            return false;
        }
        return uiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK;
        return uiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK
                && UiModeManager.isForceInvertAllowed(mContext);
    }
    private void updateForceDarkMode() {
+7 −0
Original line number Diff line number Diff line
@@ -4909,6 +4909,13 @@
        -->
    </string-array>

    <string-array translatable="false" name="config_forceInvertPackageBlocklist">
        <!--
        <item>com.example.package.first</item>
        <item>com.example.package.second</item>
        -->
    </string-array>

    <!-- Warning: This API can be dangerous when not implemented properly. In particular,
         escrow token must NOT be retrievable from device storage. In other words, either
         escrow token is not stored on device or its ciphertext is stored on device while
+1 −0
Original line number Diff line number Diff line
@@ -3861,6 +3861,7 @@
  <java-symbol type="string" name="config_defaultAccessibilityNotificationSound" />
  <java-symbol type="string" name="accessibility_shortcut_spoken_feedback" />
  <java-symbol type="array" name="config_trustedAccessibilityServices" />
  <java-symbol type="array" name="config_forceInvertPackageBlocklist" />

  <java-symbol type="string" name="accessibility_select_shortcut_menu_title" />
  <java-symbol type="string" name="accessibility_edit_shortcut_menu_button_title" />
+40 −5
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.sysprop.ViewProperties;
import android.testing.TestableContext;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MergedConfiguration;
@@ -1529,6 +1530,27 @@ public class ViewRootImplTest {
                        == ForceDarkType.FORCE_INVERT_COLOR_DARK));
    }

    @Test
    @EnableFlags(FLAG_FORCE_INVERT_COLOR)
    public void determineForceDarkType_isBlocklistedPackage_returnsNone() throws Exception {
        TestableContext testableContext = new TestableContext(sContext);
        sInstrumentation.runOnMainSync(() -> mViewRootImpl =
                new ViewRootImpl(testableContext, testableContext.getDisplayNoVerify()));
        // Set up configurations for force invert color, but with this context belonging to a
        // blocklisted package.
        waitForSystemNightModeActivated(testableContext, true);
        enableForceInvertColor(testableContext, true);
        testableContext.getOrCreateTestableResources().addOverride(
                com.android.internal.R.array.config_forceInvertPackageBlocklist,
                new String[]{testableContext.getPackageName()});

        setUpViewAttributes(testableContext, /* isLightTheme= */ true, /* isForceDarkAllowed= */
                false);

        TestUtils.waitUntil("Waiting for ForceDarkType to be ready",
                () -> (mViewRootImpl.determineForceDarkType() == ForceDarkType.NONE));
    }

    @Test
    @EnableFlags(FLAG_FORCE_INVERT_COLOR)
    public void determineForceDarkType_notLightTheme_returnsNone() throws Exception {
@@ -1810,18 +1832,26 @@ public class ViewRootImplTest {
    }

    private void waitForSystemNightModeActivated(boolean active) {
        waitForSystemNightModeActivated(sContext, active);
    }

    private void waitForSystemNightModeActivated(Context context, boolean active) {
        ShellIdentityUtils.invokeWithShellPermissions(() ->
                sInstrumentation.runOnMainSync(() -> {
                    var uiModeManager = sContext.getSystemService(UiModeManager.class);
                    var uiModeManager = context.getSystemService(UiModeManager.class);
                    uiModeManager.setNightModeActivated(active);
                }));
        sInstrumentation.waitForIdleSync();
    }

    private void enableForceInvertColor(boolean enabled) {
        enableForceInvertColor(sContext, enabled);
    }

    private void enableForceInvertColor(Context context, boolean enabled) {
        ShellIdentityUtils.invokeWithShellPermissions(() -> {
            Settings.Secure.putInt(
                    sContext.getContentResolver(),
                    context.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
                    enabled ? 1 : 0
            );
@@ -1829,6 +1859,11 @@ public class ViewRootImplTest {
    }

    private void setUpViewAttributes(boolean isLightTheme, boolean isForceDarkAllowed) {
        setUpViewAttributes(sContext, isLightTheme, isForceDarkAllowed);
    }

    private void setUpViewAttributes(Context context, boolean isLightTheme,
            boolean isForceDarkAllowed) {
        ShellIdentityUtils.invokeWithShellPermissions(() -> {
            int themeId;
            if (isForceDarkAllowed) {
@@ -1844,17 +1879,17 @@ public class ViewRootImplTest {
                    themeId = R.style.ForceDarkAllowedFalse_Dark;
                }
            }
            sContext.setTheme(themeId);
            context.setTheme(themeId);
        });

        sInstrumentation.runOnMainSync(() -> {
            View view = new View(sContext);
            View view = new View(context);
            WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                    TYPE_APPLICATION_OVERLAY);
            layoutParams.token = new Binder();
            view.setLayoutParams(layoutParams);
            mViewRootImpl.setView(view, layoutParams, /* panelParentView= */ null);
            mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId());
            mViewRootImpl.updateConfiguration(context.getDisplayNoVerify().getDisplayId());
        });
    }
}