Loading res/xml/display_settings.xml +4 −2 Original line number Diff line number Diff line Loading @@ -64,9 +64,11 @@ android:key="auto_rotate" android:title="@string/accelerometer_title" /> <SwitchPreference <Preference android:key="color_mode" android:title="@string/color_mode_title" /> android:title="@string/color_mode_title" android:fragment="com.android.settings.display.ColorModePreferenceFragment" settings:keywords="@string/keywords_color_mode" /> <Preference android:key="font_size" Loading src/com/android/settings/display/ColorModePreferenceController.java +23 −64 Original line number Diff line number Diff line Loading @@ -18,36 +18,24 @@ import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.support.v7.preference.Preference; import android.support.v7.preference.TwoStatePreference; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.AbstractPreferenceController; public class ColorModePreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { PreferenceControllerMixin { private static final String TAG = "ColorModePreference"; private static final String KEY_COLOR_MODE = "color_mode"; @VisibleForTesting static final float COLOR_SATURATION_DEFAULT = 1.0f; @VisibleForTesting static final float COLOR_SATURATION_VIVID = 1.1f; private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022; @VisibleForTesting static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; private static final int SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR = 1024; private final IBinder mSurfaceFlinger; private final ConfigurationWrapper mConfigWrapper; public ColorModePreferenceController(Context context) { super(context); mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); mConfigWrapper = new ConfigurationWrapper(context); mConfigWrapper = new ConfigurationWrapper(); } @Override Loading @@ -55,65 +43,36 @@ public class ColorModePreferenceController extends AbstractPreferenceController return KEY_COLOR_MODE; } @Override public void updateState(Preference preference) { TwoStatePreference colorMode = (TwoStatePreference) preference; colorMode.setChecked(getSaturationValue() > 1.0f); } @Override public boolean isAvailable() { return mConfigWrapper.isScreenWideColorGamut(); } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { float saturation = (boolean) newValue ? COLOR_SATURATION_VIVID : COLOR_SATURATION_DEFAULT; SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation)); applySaturation(saturation); @VisibleForTesting static class ConfigurationWrapper { private final IBinder mSurfaceFlinger; return true; ConfigurationWrapper() { mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); } /** * Propagates the provided saturation to the SurfaceFlinger. */ private void applySaturation(float saturation) { boolean isScreenWideColorGamut() { if (mSurfaceFlinger != null) { final Parcel data = Parcel.obtain(); final Parcel reply = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeFloat(saturation); try { mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0); mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR, data, reply, 0); return reply.readBoolean(); } catch (RemoteException ex) { Log.e(TAG, "Failed to set saturation", ex); Log.e(TAG, "Failed to query wide color support", ex); } finally { data.recycle(); reply.recycle(); } } } private static float getSaturationValue() { try { return Float.parseFloat(SystemProperties.get( PERSISTENT_PROPERTY_SATURATION, Float.toString(COLOR_SATURATION_DEFAULT))); } catch (NumberFormatException e) { return COLOR_SATURATION_DEFAULT; } } @VisibleForTesting static class ConfigurationWrapper { private final Context mContext; ConfigurationWrapper(Context context) { mContext = context; } boolean isScreenWideColorGamut() { return mContext.getResources().getConfiguration().isScreenWideColorGamut(); return false; } } } src/com/android/settings/display/ColorModePreferenceFragment.java 0 → 100644 +207 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.display; import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.widget.RadioButtonPickerFragment; import java.util.Arrays; import java.util.List; @SuppressWarnings("WeakerAccess") public class ColorModePreferenceFragment extends RadioButtonPickerFragment { private static final String TAG = "ColorModePreferenceFragment"; @VisibleForTesting static final float COLOR_SATURATION_NATURAL = 1.0f; @VisibleForTesting static final float COLOR_SATURATION_BOOSTED = 1.1f; private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022; private static final int SURFACE_FLINGER_TRANSACTION_NATIVE_MODE = 1023; @VisibleForTesting static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; @VisibleForTesting static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode"; @VisibleForTesting static final String KEY_COLOR_MODE_NATURAL = "color_mode_natural"; @VisibleForTesting static final String KEY_COLOR_MODE_BOOSTED = "color_mode_boosted"; @VisibleForTesting static final String KEY_COLOR_MODE_SATURATED = "color_mode_saturated"; private IBinder mSurfaceFlinger; private IActivityManager mActivityManager; @Override public void onAttach(Context context) { super.onAttach(context); mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); mActivityManager = ActivityManager.getService(); } @Override protected List<? extends CandidateInfo> getCandidates() { Context c = getContext(); return Arrays.asList( new ColorModeCandidateInfo(c.getString(R.string.color_mode_option_natural), KEY_COLOR_MODE_NATURAL), new ColorModeCandidateInfo(c.getString(R.string.color_mode_option_boosted), KEY_COLOR_MODE_BOOSTED), new ColorModeCandidateInfo(c.getString(R.string.color_mode_option_saturated), KEY_COLOR_MODE_SATURATED) ); } @Override protected String getDefaultKey() { if (isNativeModeEnabled()) { return KEY_COLOR_MODE_SATURATED; } if (getSaturationValue() > COLOR_SATURATION_NATURAL) { return KEY_COLOR_MODE_BOOSTED; } return KEY_COLOR_MODE_NATURAL; } @Override protected boolean setDefaultKey(String key) { switch (key) { case KEY_COLOR_MODE_NATURAL: applySaturation(COLOR_SATURATION_NATURAL); setNativeMode(false); break; case KEY_COLOR_MODE_BOOSTED: applySaturation(COLOR_SATURATION_BOOSTED); setNativeMode(false); break; case KEY_COLOR_MODE_SATURATED: applySaturation(COLOR_SATURATION_NATURAL); setNativeMode(true); break; } updateConfiguration(); return true; } @VisibleForTesting void updateConfiguration() { try { mActivityManager.updateConfiguration(null); } catch (RemoteException e) { Log.d(TAG, "Could not update configuration", e); } } @Override public int getMetricsCategory() { return MetricsProto.MetricsEvent.COLOR_MODE_SETTINGS; } /** * Propagates the provided saturation to the SurfaceFlinger. */ private void applySaturation(float saturation) { SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation)); if (mSurfaceFlinger != null) { final Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeFloat(saturation); try { mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0); } catch (RemoteException ex) { Log.e(TAG, "Failed to set saturation", ex); } finally { data.recycle(); } } } private static float getSaturationValue() { try { return Float.parseFloat(SystemProperties.get( PERSISTENT_PROPERTY_SATURATION, Float.toString(COLOR_SATURATION_NATURAL))); } catch (NumberFormatException e) { return COLOR_SATURATION_NATURAL; } } /** * Toggles native mode on/off in SurfaceFlinger. */ private void setNativeMode(boolean enabled) { SystemProperties.set(PERSISTENT_PROPERTY_NATIVE_MODE, enabled ? "1" : "0"); if (mSurfaceFlinger != null) { final Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeInt(enabled ? 1 : 0); try { mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_NATIVE_MODE, data, null, 0); } catch (RemoteException ex) { Log.e(TAG, "Failed to set native mode", ex); } finally { data.recycle(); } } } private static boolean isNativeModeEnabled() { return SystemProperties.getBoolean(PERSISTENT_PROPERTY_NATIVE_MODE, false); } @VisibleForTesting static class ColorModeCandidateInfo extends CandidateInfo { private final CharSequence mLabel; private final String mKey; ColorModeCandidateInfo(CharSequence label, String key) { super(true); mLabel = label; mKey = key; } @Override public CharSequence loadLabel() { return mLabel; } @Override public Drawable loadIcon() { return null; } @Override public String getKey() { return mKey; } } } tests/robotests/src/com/android/settings/display/ColorModePreferenceControllerTest.java→tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java +170 −0 Original line number Diff line number Diff line Loading @@ -15,99 +15,156 @@ */ package com.android.settings.display; import android.content.Context; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import android.app.IActivityManager; import android.content.res.Configuration; import android.os.IBinder; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.PreferenceScreen; import com.android.settings.testutils.SettingsRobolectricTestRunner; import android.os.RemoteException; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.TestConfig; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowSystemProperties; import com.android.settings.widget.RadioButtonPickerFragment; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class ColorModePreferenceControllerTest { @Mock private ColorModePreferenceController.ConfigurationWrapper mConfigWrapper; @Mock private SwitchPreference mPreference; @Mock private PreferenceScreen mScreen; @Mock private Context mContext; public class ColorModePreferenceFragmentTest { @Mock private IBinder mSurfaceFlinger; @Mock private IActivityManager mActivityManager; private ColorModePreferenceController mController; private ColorModePreferenceFragment mFragment; @Before public void setup() { MockitoAnnotations.initMocks(this); SettingsShadowSystemProperties.clear(); mController = new ColorModePreferenceController(mContext); ReflectionHelpers.setField(mController, "mSurfaceFlinger", mSurfaceFlinger); ReflectionHelpers.setField(mController, "mConfigWrapper", mConfigWrapper); mFragment = spy(new ColorModePreferenceFragment()); doReturn(RuntimeEnvironment.application).when(mFragment).getContext(); doNothing().when(mFragment).updateConfiguration(); when(mConfigWrapper.isScreenWideColorGamut()).thenReturn(true); ReflectionHelpers.setField(mFragment, "mSurfaceFlinger", mSurfaceFlinger); ReflectionHelpers.setField(mFragment, "mActivityManager", mActivityManager); } when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); @Test public void verifyMetricsConstant() { assertThat(mFragment.getMetricsCategory()) .isEqualTo(MetricsProto.MetricsEvent.COLOR_MODE_SETTINGS); } @Test public void getCandidates() { List<? extends RadioButtonPickerFragment.CandidateInfo> candidates = mFragment.getCandidates(); assertThat(candidates.size()).isEqualTo(3); assertThat(candidates.get(0).getKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL); assertThat(candidates.get(1).getKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED); assertThat(candidates.get(2).getKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldCheckPreference() { public void getKey_natural() { SettingsShadowSystemProperties.set( ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_NATURAL)); SettingsShadowSystemProperties.set( ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceController.COLOR_SATURATION_VIVID)); ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE, "0"); mController.updateState(mPreference); assertThat(mFragment.getDefaultKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL); } verify(mPreference).setChecked(true); @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void getKey_boosted() { SettingsShadowSystemProperties.set( ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_BOOSTED)); SettingsShadowSystemProperties.set( ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE, "0"); assertThat(mFragment.getDefaultKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldUncheckPreference() { public void getKey_saturated() { SettingsShadowSystemProperties.set( ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceController.COLOR_SATURATION_DEFAULT)); ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE, "1"); mController.updateState(mPreference); assertThat(mFragment.getDefaultKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED); } verify(mPreference).setChecked(false); @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void setKey_natural() { mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL); String saturation = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION); assertThat(saturation) .isEqualTo(Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_NATURAL)); String nativeMode = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE); assertThat(nativeMode).isEqualTo("0"); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldBoostSaturationOnCheck() { mController.onPreferenceChange(mPreference, true); public void setKey_boosted() { mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED); String saturation = SettingsShadowSystemProperties .get(ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION); .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION); assertThat(saturation) .isEqualTo(Float.toString(ColorModePreferenceController.COLOR_SATURATION_VIVID)); .isEqualTo(Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_BOOSTED)); String nativeMode = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE); assertThat(nativeMode).isEqualTo("0"); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldResetSaturationOnUncheck() { mController.onPreferenceChange(mPreference, false); public void setKey_saturated() { mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED); String saturation = SettingsShadowSystemProperties .get(ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION); .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION); assertThat(saturation) .isEqualTo(Float.toString(ColorModePreferenceController.COLOR_SATURATION_DEFAULT)); .isEqualTo(Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_NATURAL)); String nativeMode = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE); assertThat(nativeMode).isEqualTo("1"); } } Loading
res/xml/display_settings.xml +4 −2 Original line number Diff line number Diff line Loading @@ -64,9 +64,11 @@ android:key="auto_rotate" android:title="@string/accelerometer_title" /> <SwitchPreference <Preference android:key="color_mode" android:title="@string/color_mode_title" /> android:title="@string/color_mode_title" android:fragment="com.android.settings.display.ColorModePreferenceFragment" settings:keywords="@string/keywords_color_mode" /> <Preference android:key="font_size" Loading
src/com/android/settings/display/ColorModePreferenceController.java +23 −64 Original line number Diff line number Diff line Loading @@ -18,36 +18,24 @@ import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.support.v7.preference.Preference; import android.support.v7.preference.TwoStatePreference; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.AbstractPreferenceController; public class ColorModePreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { PreferenceControllerMixin { private static final String TAG = "ColorModePreference"; private static final String KEY_COLOR_MODE = "color_mode"; @VisibleForTesting static final float COLOR_SATURATION_DEFAULT = 1.0f; @VisibleForTesting static final float COLOR_SATURATION_VIVID = 1.1f; private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022; @VisibleForTesting static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; private static final int SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR = 1024; private final IBinder mSurfaceFlinger; private final ConfigurationWrapper mConfigWrapper; public ColorModePreferenceController(Context context) { super(context); mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); mConfigWrapper = new ConfigurationWrapper(context); mConfigWrapper = new ConfigurationWrapper(); } @Override Loading @@ -55,65 +43,36 @@ public class ColorModePreferenceController extends AbstractPreferenceController return KEY_COLOR_MODE; } @Override public void updateState(Preference preference) { TwoStatePreference colorMode = (TwoStatePreference) preference; colorMode.setChecked(getSaturationValue() > 1.0f); } @Override public boolean isAvailable() { return mConfigWrapper.isScreenWideColorGamut(); } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { float saturation = (boolean) newValue ? COLOR_SATURATION_VIVID : COLOR_SATURATION_DEFAULT; SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation)); applySaturation(saturation); @VisibleForTesting static class ConfigurationWrapper { private final IBinder mSurfaceFlinger; return true; ConfigurationWrapper() { mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); } /** * Propagates the provided saturation to the SurfaceFlinger. */ private void applySaturation(float saturation) { boolean isScreenWideColorGamut() { if (mSurfaceFlinger != null) { final Parcel data = Parcel.obtain(); final Parcel reply = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeFloat(saturation); try { mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0); mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR, data, reply, 0); return reply.readBoolean(); } catch (RemoteException ex) { Log.e(TAG, "Failed to set saturation", ex); Log.e(TAG, "Failed to query wide color support", ex); } finally { data.recycle(); reply.recycle(); } } } private static float getSaturationValue() { try { return Float.parseFloat(SystemProperties.get( PERSISTENT_PROPERTY_SATURATION, Float.toString(COLOR_SATURATION_DEFAULT))); } catch (NumberFormatException e) { return COLOR_SATURATION_DEFAULT; } } @VisibleForTesting static class ConfigurationWrapper { private final Context mContext; ConfigurationWrapper(Context context) { mContext = context; } boolean isScreenWideColorGamut() { return mContext.getResources().getConfiguration().isScreenWideColorGamut(); return false; } } }
src/com/android/settings/display/ColorModePreferenceFragment.java 0 → 100644 +207 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.display; import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.widget.RadioButtonPickerFragment; import java.util.Arrays; import java.util.List; @SuppressWarnings("WeakerAccess") public class ColorModePreferenceFragment extends RadioButtonPickerFragment { private static final String TAG = "ColorModePreferenceFragment"; @VisibleForTesting static final float COLOR_SATURATION_NATURAL = 1.0f; @VisibleForTesting static final float COLOR_SATURATION_BOOSTED = 1.1f; private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022; private static final int SURFACE_FLINGER_TRANSACTION_NATIVE_MODE = 1023; @VisibleForTesting static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; @VisibleForTesting static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode"; @VisibleForTesting static final String KEY_COLOR_MODE_NATURAL = "color_mode_natural"; @VisibleForTesting static final String KEY_COLOR_MODE_BOOSTED = "color_mode_boosted"; @VisibleForTesting static final String KEY_COLOR_MODE_SATURATED = "color_mode_saturated"; private IBinder mSurfaceFlinger; private IActivityManager mActivityManager; @Override public void onAttach(Context context) { super.onAttach(context); mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); mActivityManager = ActivityManager.getService(); } @Override protected List<? extends CandidateInfo> getCandidates() { Context c = getContext(); return Arrays.asList( new ColorModeCandidateInfo(c.getString(R.string.color_mode_option_natural), KEY_COLOR_MODE_NATURAL), new ColorModeCandidateInfo(c.getString(R.string.color_mode_option_boosted), KEY_COLOR_MODE_BOOSTED), new ColorModeCandidateInfo(c.getString(R.string.color_mode_option_saturated), KEY_COLOR_MODE_SATURATED) ); } @Override protected String getDefaultKey() { if (isNativeModeEnabled()) { return KEY_COLOR_MODE_SATURATED; } if (getSaturationValue() > COLOR_SATURATION_NATURAL) { return KEY_COLOR_MODE_BOOSTED; } return KEY_COLOR_MODE_NATURAL; } @Override protected boolean setDefaultKey(String key) { switch (key) { case KEY_COLOR_MODE_NATURAL: applySaturation(COLOR_SATURATION_NATURAL); setNativeMode(false); break; case KEY_COLOR_MODE_BOOSTED: applySaturation(COLOR_SATURATION_BOOSTED); setNativeMode(false); break; case KEY_COLOR_MODE_SATURATED: applySaturation(COLOR_SATURATION_NATURAL); setNativeMode(true); break; } updateConfiguration(); return true; } @VisibleForTesting void updateConfiguration() { try { mActivityManager.updateConfiguration(null); } catch (RemoteException e) { Log.d(TAG, "Could not update configuration", e); } } @Override public int getMetricsCategory() { return MetricsProto.MetricsEvent.COLOR_MODE_SETTINGS; } /** * Propagates the provided saturation to the SurfaceFlinger. */ private void applySaturation(float saturation) { SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation)); if (mSurfaceFlinger != null) { final Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeFloat(saturation); try { mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0); } catch (RemoteException ex) { Log.e(TAG, "Failed to set saturation", ex); } finally { data.recycle(); } } } private static float getSaturationValue() { try { return Float.parseFloat(SystemProperties.get( PERSISTENT_PROPERTY_SATURATION, Float.toString(COLOR_SATURATION_NATURAL))); } catch (NumberFormatException e) { return COLOR_SATURATION_NATURAL; } } /** * Toggles native mode on/off in SurfaceFlinger. */ private void setNativeMode(boolean enabled) { SystemProperties.set(PERSISTENT_PROPERTY_NATIVE_MODE, enabled ? "1" : "0"); if (mSurfaceFlinger != null) { final Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeInt(enabled ? 1 : 0); try { mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_NATIVE_MODE, data, null, 0); } catch (RemoteException ex) { Log.e(TAG, "Failed to set native mode", ex); } finally { data.recycle(); } } } private static boolean isNativeModeEnabled() { return SystemProperties.getBoolean(PERSISTENT_PROPERTY_NATIVE_MODE, false); } @VisibleForTesting static class ColorModeCandidateInfo extends CandidateInfo { private final CharSequence mLabel; private final String mKey; ColorModeCandidateInfo(CharSequence label, String key) { super(true); mLabel = label; mKey = key; } @Override public CharSequence loadLabel() { return mLabel; } @Override public Drawable loadIcon() { return null; } @Override public String getKey() { return mKey; } } }
tests/robotests/src/com/android/settings/display/ColorModePreferenceControllerTest.java→tests/robotests/src/com/android/settings/display/ColorModePreferenceFragmentTest.java +170 −0 Original line number Diff line number Diff line Loading @@ -15,99 +15,156 @@ */ package com.android.settings.display; import android.content.Context; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import android.app.IActivityManager; import android.content.res.Configuration; import android.os.IBinder; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.PreferenceScreen; import com.android.settings.testutils.SettingsRobolectricTestRunner; import android.os.RemoteException; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.TestConfig; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowSystemProperties; import com.android.settings.widget.RadioButtonPickerFragment; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class ColorModePreferenceControllerTest { @Mock private ColorModePreferenceController.ConfigurationWrapper mConfigWrapper; @Mock private SwitchPreference mPreference; @Mock private PreferenceScreen mScreen; @Mock private Context mContext; public class ColorModePreferenceFragmentTest { @Mock private IBinder mSurfaceFlinger; @Mock private IActivityManager mActivityManager; private ColorModePreferenceController mController; private ColorModePreferenceFragment mFragment; @Before public void setup() { MockitoAnnotations.initMocks(this); SettingsShadowSystemProperties.clear(); mController = new ColorModePreferenceController(mContext); ReflectionHelpers.setField(mController, "mSurfaceFlinger", mSurfaceFlinger); ReflectionHelpers.setField(mController, "mConfigWrapper", mConfigWrapper); mFragment = spy(new ColorModePreferenceFragment()); doReturn(RuntimeEnvironment.application).when(mFragment).getContext(); doNothing().when(mFragment).updateConfiguration(); when(mConfigWrapper.isScreenWideColorGamut()).thenReturn(true); ReflectionHelpers.setField(mFragment, "mSurfaceFlinger", mSurfaceFlinger); ReflectionHelpers.setField(mFragment, "mActivityManager", mActivityManager); } when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); @Test public void verifyMetricsConstant() { assertThat(mFragment.getMetricsCategory()) .isEqualTo(MetricsProto.MetricsEvent.COLOR_MODE_SETTINGS); } @Test public void getCandidates() { List<? extends RadioButtonPickerFragment.CandidateInfo> candidates = mFragment.getCandidates(); assertThat(candidates.size()).isEqualTo(3); assertThat(candidates.get(0).getKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL); assertThat(candidates.get(1).getKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED); assertThat(candidates.get(2).getKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldCheckPreference() { public void getKey_natural() { SettingsShadowSystemProperties.set( ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_NATURAL)); SettingsShadowSystemProperties.set( ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceController.COLOR_SATURATION_VIVID)); ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE, "0"); mController.updateState(mPreference); assertThat(mFragment.getDefaultKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL); } verify(mPreference).setChecked(true); @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void getKey_boosted() { SettingsShadowSystemProperties.set( ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_BOOSTED)); SettingsShadowSystemProperties.set( ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE, "0"); assertThat(mFragment.getDefaultKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldUncheckPreference() { public void getKey_saturated() { SettingsShadowSystemProperties.set( ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION, Float.toString(ColorModePreferenceController.COLOR_SATURATION_DEFAULT)); ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE, "1"); mController.updateState(mPreference); assertThat(mFragment.getDefaultKey()) .isEqualTo(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED); } verify(mPreference).setChecked(false); @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void setKey_natural() { mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_NATURAL); String saturation = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION); assertThat(saturation) .isEqualTo(Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_NATURAL)); String nativeMode = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE); assertThat(nativeMode).isEqualTo("0"); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldBoostSaturationOnCheck() { mController.onPreferenceChange(mPreference, true); public void setKey_boosted() { mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_BOOSTED); String saturation = SettingsShadowSystemProperties .get(ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION); .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION); assertThat(saturation) .isEqualTo(Float.toString(ColorModePreferenceController.COLOR_SATURATION_VIVID)); .isEqualTo(Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_BOOSTED)); String nativeMode = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE); assertThat(nativeMode).isEqualTo("0"); } @Config(shadows = {SettingsShadowSystemProperties.class}) @Test public void shouldResetSaturationOnUncheck() { mController.onPreferenceChange(mPreference, false); public void setKey_saturated() { mFragment.setDefaultKey(ColorModePreferenceFragment.KEY_COLOR_MODE_SATURATED); String saturation = SettingsShadowSystemProperties .get(ColorModePreferenceController.PERSISTENT_PROPERTY_SATURATION); .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_SATURATION); assertThat(saturation) .isEqualTo(Float.toString(ColorModePreferenceController.COLOR_SATURATION_DEFAULT)); .isEqualTo(Float.toString(ColorModePreferenceFragment.COLOR_SATURATION_NATURAL)); String nativeMode = SettingsShadowSystemProperties .get(ColorModePreferenceFragment.PERSISTENT_PROPERTY_NATIVE_MODE); assertThat(nativeMode).isEqualTo("1"); } }