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

Commit 4d60aaed authored by Candice Lo's avatar Candice Lo Committed by Android (Google) Code Review
Browse files

Merge "Update MMAD settings to radio buttons" into main

parents 8a887616 21055243
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -5857,10 +5857,16 @@
        screen magnification on app transitions</string>
    <!-- Title for the accessibility preference to power button to end a call. [CHAR LIMIT=35] -->
    <string name="accessibility_power_button_ends_call_prerefence_title">Power button ends call</string>
    <!-- Title for the dark theme group category -->
    <string name="dark_theme_version_category">Mode</string>
    <!-- Title for the accessibility preference for applying the original dark theme. [CHAR LIMIT=35] -->
    <string name="accessibility_standard_dark_theme_title">Standard</string>
    <!-- Summary for the accessibility preference for applying the original dark theme. [CHAR LIMIT=100] -->
    <string name="accessibility_standard_dark_theme_summary">Applies Dark theme across your device and supported apps</string>
    <!-- Title for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=35] -->
    <string name="accessibility_force_invert_title">Make more apps dark</string>
    <string name="accessibility_expanded_dark_theme_title">Expanded</string>
    <!-- Summary for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=100] -->
    <string name="accessibility_force_invert_summary">Expands dark theme to more apps. May not work with all apps.</string>
    <string name="accessibility_expanded_dark_theme_summary">Automatically applies Dark theme to more apps for improved accessibility</string>
    <!-- Title for the accessibility preference for disabling animations. [CHAR LIMIT=35] -->
    <string name="accessibility_disable_animations">Remove animations</string>
    <!-- Summary for the accessibility preference for disabling animations. [CHAR LIMIT=60] -->
+17 −7
Original line number Diff line number Diff line
@@ -36,15 +36,25 @@
        settings:controller="com.android.settings.display.TwilightLocationPreferenceController"/>

    <PreferenceCategory
        android:key="display_category"
        android:title="@string/accessibility_screen_option">
        android:key="dark_theme_group"
        android:title="@string/dark_theme_version_category"
        settings:controller="com.android.settings.accessibility.ForceInvertPreferenceController">
        <com.android.settingslib.widget.SelectorWithWidgetPreference
            android:key="standard_dark_theme"
            android:title="@string/accessibility_standard_dark_theme_title"
            android:summary="@string/accessibility_standard_dark_theme_summary"
            settings:searchable="true"/>

        <SwitchPreferenceCompat
            android:key="toggle_force_invert"
            android:summary="@string/accessibility_force_invert_summary"
            android:title="@string/accessibility_force_invert_title"
            settings:controller="com.android.settings.accessibility.ToggleForceInvertPreferenceController"/>
        <com.android.settingslib.widget.SelectorWithWidgetPreference
            android:key="expanded_dark_theme"
            android:title="@string/accessibility_expanded_dark_theme_title"
            android:summary="@string/accessibility_expanded_dark_theme_summary"
            settings:searchable="true"/>
    </PreferenceCategory>

    <PreferenceCategory
        android:key="display_category"
        android:title="@string/accessibility_screen_option">
        <DropDownPreference
            android:key="dark_ui_auto_mode"
            android:title="@string/dark_ui_auto_mode_title"
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -20,50 +20,84 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;

import android.content.Context;
import android.content.res.Configuration;
import android.provider.Settings;
import android.view.accessibility.Flags;

import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.widget.SelectorWithWidgetPreference;

/** A toggle preference controller for force invert (force dark). */
public class ToggleForceInvertPreferenceController extends TogglePreferenceController {
public class ForceInvertPreferenceController extends BasePreferenceController
        implements SelectorWithWidgetPreference.OnClickListener {

    public ToggleForceInvertPreferenceController(Context context, String preferenceKey) {
    @VisibleForTesting
    static final String STANDARD_DARK_THEME_KEY = "standard_dark_theme";
    @VisibleForTesting
    static final String EXPANDED_DARK_THEME_KEY = "expanded_dark_theme";

    @Nullable
    private SelectorWithWidgetPreference mStandardDarkThemePreference;
    @Nullable
    private SelectorWithWidgetPreference mExpandedDarkThemePreference;

    public ForceInvertPreferenceController(
            @NonNull Context context, @NonNull String preferenceKey) {
        super(context, preferenceKey);
    }

    @Override
    public boolean isChecked() {
        return Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, OFF) != OFF;
    public int getAvailabilityStatus() {
        return Flags.forceInvertColor() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
    }

    @Override
    public boolean setChecked(boolean isChecked) {
        return Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, isChecked ? ON : OFF);
    public int getSliceHighlightMenuRes() {
        return R.string.menu_key_accessibility;
    }

    @Override
    public void updateState(@NonNull Preference preference) {
        super.updateState(preference);
        final boolean isDarkModeActivated = (mContext.getResources().getConfiguration().uiMode
                & Configuration.UI_MODE_NIGHT_YES) != 0;
        preference.setEnabled(isDarkModeActivated);
    public void onRadioButtonClicked(@NonNull SelectorWithWidgetPreference preference) {
        boolean isForceInvertEnabled = preference.getKey().equals(EXPANDED_DARK_THEME_KEY);
        if (mExpandedDarkThemePreference == null
                || isForceInvertEnabled == mExpandedDarkThemePreference.isChecked()) {
            // User selects the same preference as before, we perform an early return to avoid
            // unnecessary UI updates and IO operations.
            return;
        }
        updateSelectorPreferenceStatus(isForceInvertEnabled);
        Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
                isForceInvertEnabled ? ON : OFF);
    }

    @Override
    public int getAvailabilityStatus() {
        return Flags.forceInvertColor() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
    public void displayPreference(@NonNull PreferenceScreen screen) {
        super.displayPreference(screen);
        // initialize the status of the radio buttons
        PreferenceCategory preferenceCategory = screen.findPreference(getPreferenceKey());
        mStandardDarkThemePreference =
                preferenceCategory.findPreference(STANDARD_DARK_THEME_KEY);
        mExpandedDarkThemePreference =
                preferenceCategory.findPreference(EXPANDED_DARK_THEME_KEY);
        mStandardDarkThemePreference.setOnClickListener(this);
        mExpandedDarkThemePreference.setOnClickListener(this);
        boolean isForceInvertEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, OFF) != OFF;
        updateSelectorPreferenceStatus(isForceInvertEnabled);
    }

    @Override
    public int getSliceHighlightMenuRes() {
        return R.string.menu_key_accessibility;
    private void updateSelectorPreferenceStatus(boolean isForceInvertEnabled) {
        if (mStandardDarkThemePreference == null || mExpandedDarkThemePreference == null) {
            return;
        }
        mStandardDarkThemePreference.setChecked(!isForceInvertEnabled);
        mExpandedDarkThemePreference.setChecked(isForceInvertEnabled);
    }
}
+150 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -23,107 +23,124 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.ON;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.res.Configuration;
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.provider.Settings;

import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;

import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.widget.SelectorWithWidgetPreference;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;

/** Tests for {@link ToggleForceInvertPreferenceController}. */
/** Tests for {@link ForceInvertPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
public class ToggleForceInvertPreferenceControllerTest {
public class ForceInvertPreferenceControllerTest {

    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
    @Rule
    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Mock
    private PreferenceScreen mScreen;
    @Mock
    private PreferenceCategory mPreferenceCategory;

    private final Context mContext = ApplicationProvider.getApplicationContext();
    private ToggleForceInvertPreferenceController mController;
    private ForceInvertPreferenceController mController;
    private SelectorWithWidgetPreference mStandardDarkThemePreference;
    private SelectorWithWidgetPreference mExpandedDarkThemePreference;

    @Before
    public void setUp() {
        mController = new ToggleForceInvertPreferenceController(mContext, "toggle_force_invert");
        mController = new ForceInvertPreferenceController(mContext, "dark_theme_group");
        mStandardDarkThemePreference = new SelectorWithWidgetPreference(mContext);
        mStandardDarkThemePreference
                .setKey(ForceInvertPreferenceController.STANDARD_DARK_THEME_KEY);
        mExpandedDarkThemePreference = new SelectorWithWidgetPreference(mContext);
        mExpandedDarkThemePreference
                .setKey(ForceInvertPreferenceController.EXPANDED_DARK_THEME_KEY);
        when(mScreen.findPreference(mController.getPreferenceKey()))
                .thenReturn(mPreferenceCategory);
        when(mPreferenceCategory
                .findPreference(ForceInvertPreferenceController.STANDARD_DARK_THEME_KEY))
                .thenReturn(mStandardDarkThemePreference);
        when(mPreferenceCategory
                .findPreference(ForceInvertPreferenceController.EXPANDED_DARK_THEME_KEY))
                .thenReturn(mExpandedDarkThemePreference);
    }

    @Test
    @RequiresFlagsDisabled(FLAG_FORCE_INVERT_COLOR)
    public void flagOff_getAvailabilityStatus_shouldReturnUnsupported() {
    public void getAvailabilityStatus_flagOff_shouldReturnUnsupported() {
        assertThat(mController.getAvailabilityStatus())
                .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
    }

    @Test
    @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
    public void flagOn_getAvailabilityStatus_shouldReturnAvailable() {
    public void getAvailabilityStatus_flagOn_shouldReturnAvailable() {
        assertThat(mController.getAvailabilityStatus())
                .isEqualTo(BasePreferenceController.AVAILABLE);
    }

    @Test
    public void updateState_darkModeOn_preferenceEnabled() {
        Configuration config = mContext.getResources().getConfiguration();
        config.uiMode = Configuration.UI_MODE_NIGHT_YES;
        mContext.getResources().updateConfiguration(config, null);
    public void settingOff_reflectsCorrectValue() {
        setEnabled(false);

        Preference preference = mock(Preference.class);
        mController.updateState(preference);
        mController.displayPreference(mScreen);

        verify(preference).setEnabled(true);
        assertThat(mStandardDarkThemePreference.isChecked()).isTrue();
        assertThat(mExpandedDarkThemePreference.isChecked()).isFalse();
    }

    @Test
    public void updateState_darkModeOff_preferenceDisabled() {
        Configuration config = mContext.getResources().getConfiguration();
        config.uiMode = Configuration.UI_MODE_NIGHT_NO;
        mContext.getResources().updateConfiguration(config, null);
    public void settingOn_reflectsCorrectValue() {
        setEnabled(true);

        Preference preference = mock(Preference.class);
        mController.updateState(preference);
        mController.displayPreference(mScreen);

        verify(preference).setEnabled(false);
        assertThat(mStandardDarkThemePreference.isChecked()).isFalse();
        assertThat(mExpandedDarkThemePreference.isChecked()).isTrue();
    }

    @Test
    public void settingOff_reflectsCorrectValue() {
        setEnabled(false);
        assertThat(mController.isChecked()).isFalse();
    }
    public void onRadioButtonClicked_standardDarkTheme_settingChanges() {
        mController.displayPreference(mScreen);
        mController.onRadioButtonClicked(mStandardDarkThemePreference);

    @Test
    public void settingOn_reflectsCorrectValue() {
        setEnabled(true);
        assertThat(mController.isChecked()).isTrue();
        assertThat(mStandardDarkThemePreference.isChecked()).isTrue();
        assertThat(mExpandedDarkThemePreference.isChecked()).isFalse();
        boolean isForceInvertEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* def=*/ -1) == ON;
        assertThat(isForceInvertEnabled).isFalse();
    }

    @Test
    public void onCheck_settingChanges() {
        setEnabled(false);

        mController.setChecked(true);
        assertThat(isEnabled()).isTrue();

        mController.setChecked(false);
        assertThat(isEnabled()).isFalse();
    }
    public void onRadioButtonClicked_expandedDarkTheme_settingChanges() {
        mController.displayPreference(mScreen);
        mController.onRadioButtonClicked(mExpandedDarkThemePreference);

    private boolean isEnabled() {
        return Settings.Secure.getInt(mContext.getContentResolver(),
        assertThat(mStandardDarkThemePreference.isChecked()).isFalse();
        assertThat(mExpandedDarkThemePreference.isChecked()).isTrue();
        boolean isForceInvertEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* def=*/ -1) == ON;
        assertThat(isForceInvertEnabled).isTrue();
    }

    private void setEnabled(boolean enabled) {
Loading