diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ccf002a8e1d..863ae70c39b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2785,6 +2785,7 @@
+
adjust color
turn screen dark, turn screen light
+
+ color contrast
@@ -12017,6 +12019,15 @@
Cancel
+
+ Contrast
+
+ Standard
+
+ Medium
+
+ High
+
"This app can only be opened in 1 window"
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index f3ac926305a..55a63d6da8b 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -515,6 +515,12 @@
android:title="@string/transparent_navigation_bar"
android:summary="@string/transparent_navigation_bar_summary" />
+
+
context.getString(R.string.contrast_high)
+ CONTRAST_LEVEL_MEDIUM -> context.getString(R.string.contrast_medium)
+ else -> context.getString(R.string.contrast_standard)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/theme/ContrastPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/theme/ContrastPreferenceControllerTest.java
new file mode 100644
index 00000000000..dbd33728b85
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/theme/ContrastPreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.theme;
+
+import static android.app.UiModeManager.ContrastUtils;
+import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH;
+import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM;
+import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD;
+import static android.provider.Settings.Secure.CONTRAST_LEVEL;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.stream.Stream;
+
+@RunWith(RobolectricTestRunner.class)
+public class ContrastPreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ private ContrastPreferenceController mController;
+
+ @Mock
+ private UiModeManager mMockUiModeManager;
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+ mController = new ContrastPreferenceController(mContext, mMockUiModeManager);
+ }
+
+ @Test
+ public void controllerIsAvailable() {
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void testHandlePreferenceTreeClick() {
+ Preference preference = new Preference(mContext);
+ preference.setKey(ContrastPreferenceController.KEY);
+ assertThat(mController.handlePreferenceTreeClick(preference)).isTrue();
+
+ Preference otherPreference = new Preference(mContext);
+ otherPreference.setKey("wrong key");
+ assertThat(mController.handlePreferenceTreeClick(otherPreference)).isFalse();
+ }
+
+ @Test
+ public void controllerSummary() {
+ float initialContrast = mContext.getSystemService(UiModeManager.class).getContrast();
+ try {
+ allContrastValues().forEach(contrastLevel -> {
+ float contrast = ContrastUtils.fromContrastLevel(contrastLevel);
+ clearInvocations(mMockUiModeManager);
+ when(mMockUiModeManager.getContrast()).thenReturn(contrast);
+ String summary = mController.getSummary().toString();
+ verify(mMockUiModeManager).getContrast();
+ assertThat(summary).isEqualTo(mController.getSummary(contrastLevel));
+ });
+ } finally {
+ putContrastInSettings(initialContrast);
+ }
+ }
+
+ private static Stream allContrastValues() {
+ return Stream.of(CONTRAST_LEVEL_STANDARD, CONTRAST_LEVEL_MEDIUM, CONTRAST_LEVEL_HIGH);
+ }
+
+ private void putContrastInSettings(float contrast) {
+ Settings.Secure.putFloat(mContext.getContentResolver(), CONTRAST_LEVEL, contrast);
+ }
+}