diff --git a/res/values/strings.xml b/res/values/strings.xml
index 572851ed06e..c179eba8ae9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5465,6 +5465,8 @@
Grayscale
Intensity
+
+ Unavailable for grayscale mode or when color correction is disabled
Green weak, deuteranomaly
diff --git a/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceController.java b/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceController.java
index 7dcd6612ac4..29971854e6b 100644
--- a/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceController.java
+++ b/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceController.java
@@ -17,26 +17,50 @@ package com.android.settings.accessibility;
import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.server.accessibility.Flags;
+import com.android.settings.R;
import com.android.settings.core.SliderPreferenceController;
import com.android.settings.widget.SeekBarPreference;
/**
* The controller of the seekbar preference for the saturation level of color correction.
*/
-public class DaltonizerSaturationSeekbarPreferenceController extends SliderPreferenceController {
+public class DaltonizerSaturationSeekbarPreferenceController
+ extends SliderPreferenceController
+ implements DefaultLifecycleObserver {
private static final int DEFAULT_SATURATION_LEVEL = 7;
private static final int SATURATION_MAX = 10;
- private static final int SATURATION_MIN = 0;
+ private static final int SATURATION_MIN = 1;
private int mSliderPosition;
private final ContentResolver mContentResolver;
+ @Nullable
+ private SeekBarPreference mPreference;
+
+ public final ContentObserver mContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (mPreference != null) {
+ updateState(mPreference);
+ }
+ }
+ };
+
public DaltonizerSaturationSeekbarPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
@@ -49,10 +73,33 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
// TODO: Observer color correction on/off and enable/disable based on secure settings.
}
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner) {
+ if (!isAvailable()) return;
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
+ true,
+ mContentObserver
+ );
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
+ true,
+ mContentObserver
+ );
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner) {
+ if (!isAvailable()) return;
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ mPreference = null;
+ }
+
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
SeekBarPreference preference = screen.findPreference(getPreferenceKey());
+ mPreference = preference;
preference.setMax(getMax());
preference.setMin(getMin());
preference.setProgress(mSliderPosition);
@@ -62,7 +109,7 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
@Override
public int getAvailabilityStatus() {
if (Flags.enableColorCorrectionSaturation()) {
- return AVAILABLE;
+ return shouldSeekBarEnabled() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
return CONDITIONALLY_UNAVAILABLE;
}
@@ -85,6 +132,21 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
return true;
}
+ @Override
+ public void updateState(Preference preference) {
+ if (preference == null) {
+ return;
+ }
+
+ var shouldSeekbarEnabled = shouldSeekBarEnabled();
+ // setSummary not working yet on SeekBarPreference.
+ String summary = shouldSeekbarEnabled
+ ? ""
+ : mContext.getString(R.string.daltonizer_saturation_unavailable_summary);
+ preference.setSummary(summary);
+ preference.setEnabled(shouldSeekbarEnabled);
+ }
+
@Override
public int getMax() {
return SATURATION_MAX;
@@ -94,4 +156,16 @@ public class DaltonizerSaturationSeekbarPreferenceController extends SliderPrefe
public int getMin() {
return SATURATION_MIN;
}
+
+ private boolean shouldSeekBarEnabled() {
+ int enabled = Settings.Secure.getInt(
+ mContentResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0);
+ int mode = Settings.Secure.getInt(
+ mContentResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, -1);
+
+ // enabled == 0 is disabled and also default.
+ // mode == 0 is gray scale where saturation level isn't applicable.
+ // mode == -1 is disabled and also default.
+ return enabled != 0 && mode != -1 && mode != 0;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceControllerTest.java
index 98ed4422dc8..5fd11f910fa 100644
--- a/tests/robotests/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/DaltonizerSaturationSeekbarPreferenceControllerTest.java
@@ -16,38 +16,39 @@
package com.android.settings.accessibility;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
import android.content.ContentResolver;
import android.content.Context;
+import android.os.Looper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.server.accessibility.Flags;
import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
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.robolectric.RobolectricTestRunner;
/** Tests for {@link DaltonizerSaturationSeekbarPreferenceController}. */
@@ -60,8 +61,9 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
private int mOriginalSaturationLevel = -1;
private PreferenceScreen mScreen;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
- @Mock
private SeekBarPreference mPreference;
@Rule
@@ -69,7 +71,6 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
Context context = ApplicationProvider.getApplicationContext();
mContentResolver = context.getContentResolver();
mOriginalSaturationLevel = Settings.Secure.getInt(
@@ -77,10 +78,13 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
7);
- mScreen = spy(new PreferenceScreen(context, /* attrs= */ null));
- when(mScreen.findPreference(ToggleDaltonizerPreferenceFragment.KEY_SATURATION))
- .thenReturn(mPreference);
+ mPreference = new SeekBarPreference(context);
+ mPreference.setKey(ToggleDaltonizerPreferenceFragment.KEY_SATURATION);
+ mScreen = new PreferenceManager(context).createPreferenceScreen(context);
+ mScreen.addPreference(mPreference);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new DaltonizerSaturationSeekbarPreferenceController(
context,
ToggleDaltonizerPreferenceFragment.KEY_SATURATION);
@@ -94,6 +98,12 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
mOriginalSaturationLevel);
}
+ @Test
+ public void constructor_defaultValuesMatch() {
+ assertThat(mController.getSliderPosition()).isEqualTo(7);
+ assertThat(mController.getMax()).isEqualTo(10);
+ assertThat(mController.getMin()).isEqualTo(1);
+ }
@Test
@DisableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
@@ -103,28 +113,72 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
- public void getAvailabilityStatus_flagEnabled_available() {
+ public void getAvailabilityStatus_flagEnabledProtanEnabled_available() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
- public void constructor_defaultValuesMatch() {
- assertThat(mController.getSliderPosition()).isEqualTo(7);
- assertThat(mController.getMax()).isEqualTo(10);
- assertThat(mController.getMin()).isEqualTo(0);
+ @EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
+ public void getAvailabilityStatus_flagEnabledDeutranEnabled_available() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 12);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
- public void displayPreference_enabled_visible() {
+ public void getAvailabilityStatus_flagEnabledTritanEnabled_available() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 13);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
+ public void getAvailabilityStatus_flagEnabledGrayScale_disabled() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 0);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
+ public void getAvailabilityStatus_flagEnabledColorCorrectionDisabled_disabled() {
+ setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
+ public void getAvailabilityStatus_flagEnabledColorCorrectionDisabledGrayScale_disabled() {
+ setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
+ public void displayPreference_flagEnabledColorCorrectionEnabled_enabledWithDefaultValues() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
mController.displayPreference(mScreen);
- verify(mPreference).setMax(eq(10));
- verify(mPreference).setMin(eq(0));
- verify(mPreference).setProgress(eq(7));
- verify(mPreference).setContinuousUpdates(eq(true));
- verify(mPreference).setOnPreferenceChangeListener(eq(mController));
- verify(mPreference).setVisible(eq(true));
+ assertThat(mPreference.isEnabled()).isTrue();
+ assertThat(mPreference.getMax()).isEqualTo(10);
+ assertThat(mPreference.getMin()).isEqualTo(1);
+ assertThat(mPreference.getProgress()).isEqualTo(7);
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getOnPreferenceChangeListener()).isEqualTo(mController);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
+ public void displayPreference_flagEnabledColorCorrectionDisabled_disabledWithDefaultValues() {
+ setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ assertThat(mPreference.getMax()).isEqualTo(10);
+ assertThat(mPreference.getMin()).isEqualTo(1);
+ assertThat(mPreference.getProgress()).isEqualTo(7);
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getOnPreferenceChangeListener()).isEqualTo(mController);
}
@Test
@@ -132,12 +186,8 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
public void displayPreference_disabled_notVisible() {
mController.displayPreference(mScreen);
- verify(mPreference).setMax(eq(10));
- verify(mPreference).setMin(eq(0));
- verify(mPreference).setProgress(eq(7));
- verify(mPreference).setContinuousUpdates(eq(true));
- verify(mPreference, never()).setOnPreferenceChangeListener(any());
- verify(mPreference).setVisible(eq(false));
+ assertThat(mPreference.isVisible()).isFalse();
+ assertThat(mPreference.getOnPreferenceChangeListener()).isNull();
}
@Test
@@ -153,13 +203,13 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
@Test
public void setSliderPosition_min_secureSettingsUpdated() {
- var isSliderSet = mController.setSliderPosition(0);
+ var isSliderSet = mController.setSliderPosition(1);
assertThat(isSliderSet).isTrue();
assertThat(Settings.Secure.getInt(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
- 7)).isEqualTo(0);
+ 7)).isEqualTo(1);
}
@Test
@@ -194,4 +244,140 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
7)).isEqualTo(7);
}
+
+ @Test
+ public void updateState_enabledProtan_preferenceEnabled() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void updateState_enabledDeuteran_preferenceEnabled() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 12);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void updateState_enabledTritan_preferenceEnabled() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 13);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void updateState_disabledGrayScale_preferenceDisabled() {
+ setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void updateState_nullPreference_noError() {
+ setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
+
+ mController.updateState(null);
+ }
+
+ @Test
+ public void updateState_enabledGrayScale_preferenceDisabled() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 0);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_daltonizerEnabledAfterResumed_preferenceEnabled() {
+ setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
+ mController.displayPreference(mScreen);
+ assertThat(mPreference.isEnabled()).isFalse();
+
+ mLifecycle.addObserver(mController);
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
+
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+ 1);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ assertThat(mPreference.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void onResume_daltonizerDisabledAfterResumed_preferenceDisabled() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
+ mController.displayPreference(mScreen);
+ assertThat(mPreference.isEnabled()).isTrue();
+
+ mLifecycle.addObserver(mController);
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
+
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+ 0);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_daltonizerGrayScaledAfterResumed_preferenceDisabled() {
+ setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
+ mController.displayPreference(mScreen);
+ assertThat(mPreference.isEnabled()).isTrue();
+
+ mLifecycle.addObserver(mController);
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
+
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+ 0);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onStop_daltonizerEnabledAfterOnStop_preferenceNotChanged() {
+ setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
+ mController.displayPreference(mScreen);
+ assertThat(mPreference.isEnabled()).isFalse();
+
+ mLifecycle.addObserver(mController);
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+
+ // enabled.
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+ 1);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ }
+
+ private void setDaltonizerMode(int enabled, int mode) {
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+ enabled);
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+ mode);
+ }
}