From 608ad6f632f3ee8787ee59cf8c56cb8c592c506b Mon Sep 17 00:00:00 2001 From: Daniel Solomon Date: Thu, 11 Apr 2019 19:24:39 -0700 Subject: [PATCH] Hide display white balance setting depending on accessibility To avoid conflicting with display accessibility features, hide the display white balance setting when these features are active. Bug: 130263943 Test: make ROBOTEST_FILTER=DisplayWhiteBalancePreferenceControllerTest RunSettingsRoboTests -j32 Change-Id: I9936a657521287a7a0482f2a934430bd79013cf3 --- ...splayWhiteBalancePreferenceController.java | 84 +++++++++- ...yWhiteBalancePreferenceControllerTest.java | 144 +++++++++++++++--- 2 files changed, 203 insertions(+), 25 deletions(-) diff --git a/src/com/android/settings/display/DisplayWhiteBalancePreferenceController.java b/src/com/android/settings/display/DisplayWhiteBalancePreferenceController.java index dc569aaa1da..6fc0b0e10e4 100644 --- a/src/com/android/settings/display/DisplayWhiteBalancePreferenceController.java +++ b/src/com/android/settings/display/DisplayWhiteBalancePreferenceController.java @@ -13,17 +13,34 @@ */ package com.android.settings.display; +import android.app.ActivityManager; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; import android.hardware.display.ColorDisplayManager; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; import android.os.UserHandle; import android.provider.Settings.Secure; +import android.provider.Settings.System; import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settings.core.TogglePreferenceController; -public class DisplayWhiteBalancePreferenceController extends TogglePreferenceController { +public class DisplayWhiteBalancePreferenceController extends TogglePreferenceController + implements LifecycleObserver, OnStart, OnStop { private ColorDisplayManager mColorDisplayManager; + @VisibleForTesting + ContentObserver mContentObserver; + private Preference mPreference; public DisplayWhiteBalancePreferenceController(Context context, String key) { super(context, key); @@ -31,12 +48,8 @@ public class DisplayWhiteBalancePreferenceController extends TogglePreferenceCon @Override public int getAvailabilityStatus() { - // Display white balance is only valid in linear light space. COLOR_MODE_SATURATED implies - // unmanaged color mode, and hence unknown color processing conditions. - return ColorDisplayManager.isDisplayWhiteBalanceAvailable(mContext) && - getColorDisplayManager().getColorMode() != - ColorDisplayManager.COLOR_MODE_SATURATED ? - AVAILABLE : DISABLED_FOR_USER; + return getColorDisplayManager().isDisplayWhiteBalanceAvailable(mContext) ? + AVAILABLE : DISABLED_FOR_USER; } @Override @@ -49,6 +62,50 @@ public class DisplayWhiteBalancePreferenceController extends TogglePreferenceCon return getColorDisplayManager().setDisplayWhiteBalanceEnabled(isChecked); } + @Override + public void onStart() { + if (!isAvailable()) { + return; + } + + final ContentResolver cr = mContext.getContentResolver(); + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + updateVisibility(); + } + }; + cr.registerContentObserver( + Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED), + false /* notifyForDescendants */, mContentObserver, + ActivityManager.getCurrentUser()); + cr.registerContentObserver( + Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED), + false /* notifyForDescendants */, mContentObserver, + ActivityManager.getCurrentUser()); + cr.registerContentObserver( + System.getUriFor(System.DISPLAY_COLOR_MODE), + false /* notifyForDescendants */, mContentObserver, + ActivityManager.getCurrentUser()); + + updateVisibility(); + } + + @Override + public void onStop() { + if (mContentObserver != null) { + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + mContentObserver = null; + } + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + @VisibleForTesting ColorDisplayManager getColorDisplayManager() { if (mColorDisplayManager == null) { @@ -56,4 +113,17 @@ public class DisplayWhiteBalancePreferenceController extends TogglePreferenceCon } return mColorDisplayManager; } + + @VisibleForTesting + void updateVisibility() { + if (mPreference != null) { + ColorDisplayManager cdm = getColorDisplayManager(); + + // Display white balance is only valid in linear light space. COLOR_MODE_SATURATED + // implies unmanaged color mode, and hence unknown color processing conditions. + // We also disallow display white balance when color accessibility features are enabled. + mPreference.setVisible(cdm.getColorMode() != ColorDisplayManager.COLOR_MODE_SATURATED && + !cdm.areAccessibilityTransformsEnabled(mContext)); + } + } } diff --git a/tests/robotests/src/com/android/settings/display/DisplayWhiteBalancePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/DisplayWhiteBalancePreferenceControllerTest.java index d0dbc0bb5e0..dfc13de8e14 100644 --- a/tests/robotests/src/com/android/settings/display/DisplayWhiteBalancePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/DisplayWhiteBalancePreferenceControllerTest.java @@ -1,16 +1,27 @@ package com.android.settings.display; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static com.google.common.truth.Truth.assertThat; + +import android.content.ContentResolver; +import android.content.Context; import android.hardware.display.ColorDisplayManager; +import android.provider.Settings; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + import com.android.settings.testutils.shadow.SettingsShadowResources; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -27,6 +38,15 @@ public class DisplayWhiteBalancePreferenceControllerTest { @Mock private ColorDisplayManager mColorDisplayManager; + private ContentResolver mContentResolver; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + @Mock + private PreferenceScreen mScreen; + @Mock + private Preference mPreference; + + private final String PREFERENCE_KEY = "display_white_balance"; @After public void tearDown() { @@ -36,17 +56,21 @@ public class DisplayWhiteBalancePreferenceControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mController = spy(new DisplayWhiteBalancePreferenceController(RuntimeEnvironment.application, - "display_white_balance")); + + mContentResolver = RuntimeEnvironment.application.getContentResolver(); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + when(mContext.getResources()).thenReturn(RuntimeEnvironment.application.getResources()); + when(mScreen.findPreference(PREFERENCE_KEY)).thenReturn(mPreference); + + mController = spy(new DisplayWhiteBalancePreferenceController(mContext, PREFERENCE_KEY)); doReturn(mColorDisplayManager).when(mController).getColorDisplayManager(); } @Test - public void isAvailable_configuredAvailable() { + public void isAvailable() { SettingsShadowResources.overrideResource( com.android.internal.R.bool.config_displayWhiteBalanceAvailable, true); - when(mColorDisplayManager.getColorMode()) - .thenReturn(ColorDisplayManager.COLOR_MODE_NATURAL); + assertThat(mController.isAvailable()).isTrue(); } @@ -54,20 +78,7 @@ public class DisplayWhiteBalancePreferenceControllerTest { public void isAvailable_configuredUnavailable() { SettingsShadowResources.overrideResource( com.android.internal.R.bool.config_displayWhiteBalanceAvailable, false); - when(mColorDisplayManager.getColorMode()) - .thenReturn(ColorDisplayManager.COLOR_MODE_SATURATED); - assertThat(mController.isAvailable()).isFalse(); - SettingsShadowResources.overrideResource( - com.android.internal.R.bool.config_displayWhiteBalanceAvailable, false); - when(mColorDisplayManager.getColorMode()) - .thenReturn(ColorDisplayManager.COLOR_MODE_NATURAL); - assertThat(mController.isAvailable()).isFalse(); - - SettingsShadowResources.overrideResource( - com.android.internal.R.bool.config_displayWhiteBalanceAvailable, true); - when(mColorDisplayManager.getColorMode()) - .thenReturn(ColorDisplayManager.COLOR_MODE_SATURATED); assertThat(mController.isAvailable()).isFalse(); } @@ -94,4 +105,101 @@ public class DisplayWhiteBalancePreferenceControllerTest { when(mColorDisplayManager.isDisplayWhiteBalanceEnabled()).thenReturn(false); assertThat(mController.isChecked()).isFalse(); } + + @Test + public void onStart_configuredUnavailable() { + SettingsShadowResources.overrideResource( + com.android.internal.R.bool.config_displayWhiteBalanceAvailable, false); + mController.displayPreference(mScreen); + mController.onStart(); + assertThat(mController.mContentObserver).isNull(); + } + + @Test + public void onStart_configuredAvailable() { + SettingsShadowResources.overrideResource( + com.android.internal.R.bool.config_displayWhiteBalanceAvailable, true); + when(mColorDisplayManager.getColorMode()) + .thenReturn(ColorDisplayManager.COLOR_MODE_NATURAL); + toggleAccessibilityInversion(false); + toggleAccessibilityDaltonizer(false); + + mController.displayPreference(mScreen); + mController.onStart(); + assertThat(mController.mContentObserver).isNotNull(); + } + + @Test + public void visibility_configuredAvailableAccessibilityToggled() { + SettingsShadowResources.overrideResource( + com.android.internal.R.bool.config_displayWhiteBalanceAvailable, true); + mController.displayPreference(mScreen); + + // Accessibility features disabled + toggleAccessibilityInversion(false); + reset(mPreference); + mController.updateVisibility(); + verify(mPreference).setVisible(true); + + toggleAccessibilityDaltonizer(false); + reset(mPreference); + mController.updateVisibility(); + verify(mPreference).setVisible(true); + + // Accessibility features enabled one by one + toggleAccessibilityInversion(true); + mController.updateVisibility(); + verify(mPreference).setVisible(false); + + toggleAccessibilityDaltonizer(true); + reset(mPreference); + mController.updateVisibility(); + verify(mPreference).setVisible(false); + + // Accessibility features disabled one by one + toggleAccessibilityInversion(false); + reset(mPreference); + mController.updateVisibility(); + // Daltonizer is still enabled, so we expect the preference to still be invisible + verify(mPreference).setVisible(false); + + // Now both a11y features are disabled, so we expect the preference to become visible + toggleAccessibilityDaltonizer(false); + mController.updateVisibility(); + verify(mPreference).setVisible(true); + } + + @Test + public void visibility_configuredAvailableColorModeChanged() { + SettingsShadowResources.overrideResource( + com.android.internal.R.bool.config_displayWhiteBalanceAvailable, true); + mController.displayPreference(mScreen); + + // Non-Saturated color mode selected + when(mColorDisplayManager.getColorMode()).thenReturn(ColorDisplayManager.COLOR_MODE_NATURAL); + reset(mPreference); + mController.updateVisibility(); + verify(mPreference).setVisible(true); + + // Saturated color mode selected + when(mColorDisplayManager.getColorMode()).thenReturn(ColorDisplayManager.COLOR_MODE_SATURATED); + mController.updateVisibility(); + verify(mPreference).setVisible(false); + + // Switch back to non-Saturated color mode + when(mColorDisplayManager.getColorMode()).thenReturn(ColorDisplayManager.COLOR_MODE_NATURAL); + reset(mPreference); + mController.updateVisibility(); + verify(mPreference).setVisible(true); + } + + private void toggleAccessibilityInversion(boolean enable) { + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, enable ? 1 : 0); + } + + private void toggleAccessibilityDaltonizer(boolean enable) { + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, enable ? 1 : 0); + } }