diff options
5 files changed, 275 insertions, 0 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml index e0c5d5d33e..847335750f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2752,6 +2752,10 @@ <string name="peak_refresh_rate_title">Smooth Display</string> <!-- Display settings screen, peak refresh rate settings summary [CHAR LIMIT=NONE] --> <string name="peak_refresh_rate_summary">Automatically raises the refresh rate from 60 to 90 Hz for some content. Increases battery usage.</string> + <!-- Display developer settings: Force to the highest refresh rate [CHAR LIMIT=NONE] --> + <string name="force_high_refresh_rate_toggle">Smooth Display</string> + <!-- Display developer settings: Force to the highest refresh rate description [CHAR LIMIT=NONE]--> + <string name="force_high_refresh_rate_desc">Highest refresh rate for improved touch responsiveness & animation quality. Increases battery usage.</string> <!-- Display settings screen, setting option name to enable adaptive sleep [CHAR LIMIT=30] --> <string name="adaptive_sleep_title">Screen attention</string> <!-- Setting option summary when adaptive sleep is on [CHAR LIMIT=NONE] --> diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index e8605894f6..746a51c4b9 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -256,6 +256,11 @@ android:title="@string/overlay_settings_title" android:summary="@string/overlay_settings_summary" /> + <SwitchPreference + android:key="pref_key_peak_refresh_rate" + android:title="@string/force_high_refresh_rate_toggle" + android:summary="@string/force_high_refresh_rate_desc" /> + </PreferenceCategory> <PreferenceCategory diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 9406c89539..945970eafb 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -459,6 +459,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new SelectDebugAppPreferenceController(context, fragment)); controllers.add(new WaitForDebuggerPreferenceController(context)); controllers.add(new EnableGpuDebugLayersPreferenceController(context)); + controllers.add(new ForcePeakRefreshRatePreferenceController(context)); controllers.add(new EnableVerboseVendorLoggingPreferenceController(context)); controllers.add(new VerifyAppsOverUsbPreferenceController(context)); controllers.add(new ArtVerifierPreferenceController(context)); diff --git a/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java b/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java new file mode 100644 index 0000000000..78352d33cd --- /dev/null +++ b/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020 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.development; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.provider.Settings; +import android.util.Log; +import android.view.Display; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +public class ForcePeakRefreshRatePreferenceController extends DeveloperOptionsPreferenceController + implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { + + @VisibleForTesting + static float DEFAULT_REFRESH_RATE = 60f; + + @VisibleForTesting + static float NO_CONFIG = 0f; + + @VisibleForTesting + float mPeakRefreshRate; + + private static final String TAG = "ForcePeakRefreshRateCtr"; + private static final String PREFERENCE_KEY = "pref_key_peak_refresh_rate"; + + public ForcePeakRefreshRatePreferenceController(Context context) { + super(context); + + final DisplayManager dm = context.getSystemService(DisplayManager.class); + final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY); + + if (display == null) { + Log.w(TAG, "No valid default display device"); + mPeakRefreshRate = DEFAULT_REFRESH_RATE; + } else { + mPeakRefreshRate = findPeakRefreshRate(display.getSupportedModes()); + } + + Log.d(TAG, "DEFAULT_REFRESH_RATE : " + DEFAULT_REFRESH_RATE + + " mPeakRefreshRate : " + mPeakRefreshRate); + } + + @Override + public String getPreferenceKey() { + return PREFERENCE_KEY; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isEnabled = (Boolean) newValue; + forcePeakRefreshRate(isEnabled); + + return true; + } + + @Override + public void updateState(Preference preference) { + ((SwitchPreference) mPreference).setChecked(isForcePeakRefreshRateEnabled()); + } + + @Override + public boolean isAvailable() { + if (mContext.getResources().getBoolean(R.bool.config_show_smooth_display)) { + return mPeakRefreshRate > DEFAULT_REFRESH_RATE; + } else { + return false; + } + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + Settings.System.putFloat(mContext.getContentResolver(), + Settings.System.MIN_REFRESH_RATE, NO_CONFIG); + + ((SwitchPreference) mPreference).setChecked(false); + } + + @VisibleForTesting + void forcePeakRefreshRate(boolean enable) { + final float peakRefreshRate = enable ? mPeakRefreshRate : NO_CONFIG; + Settings.System.putFloat(mContext.getContentResolver(), + Settings.System.MIN_REFRESH_RATE, peakRefreshRate); + } + + boolean isForcePeakRefreshRateEnabled() { + final float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), + Settings.System.MIN_REFRESH_RATE, NO_CONFIG); + + return peakRefreshRate >= mPeakRefreshRate; + } + + private float findPeakRefreshRate(Display.Mode[] modes) { + float peakRefreshRate = DEFAULT_REFRESH_RATE; + for (Display.Mode mode : modes) { + if (Math.round(mode.getRefreshRate()) > DEFAULT_REFRESH_RATE) { + peakRefreshRate = mode.getRefreshRate(); + } + } + + return peakRefreshRate; + } +} diff --git a/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java new file mode 100644 index 0000000000..2e93d5ef9c --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 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.development; + +import static com.android.settings.development.ForcePeakRefreshRatePreferenceController.DEFAULT_REFRESH_RATE; +import static com.android.settings.development.ForcePeakRefreshRatePreferenceController.NO_CONFIG; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import android.util.Log; + +@RunWith(RobolectricTestRunner.class) +public class ForcePeakRefreshRatePreferenceControllerTest { + + @Mock + private SwitchPreference mPreference; + @Mock + private PreferenceScreen mScreen; + + private Context mContext; + private ForcePeakRefreshRatePreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mController = new ForcePeakRefreshRatePreferenceController(mContext); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); + when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); + mController.displayPreference(mScreen); + } + + @Test + public void onPreferenceChange_preferenceChecked_shouldEnableForcePeak() { + mController.mPeakRefreshRate = 88f; + + mController.onPreferenceChange(mPreference, true); + + assertThat(Settings.System.getFloat(mContext.getContentResolver(), + Settings.System.MIN_REFRESH_RATE, NO_CONFIG)).isEqualTo(88f); + } + + @Test + public void onPreferenceChange_preferenceUnchecked_shouldDisableForcePeak() { + mController.mPeakRefreshRate = 88f; + + mController.onPreferenceChange(mPreference, false); + + assertThat(Settings.System.getFloat(mContext.getContentResolver(), + Settings.System.MIN_REFRESH_RATE, NO_CONFIG)).isEqualTo(NO_CONFIG); + } + + @Test + public void updateState_enableForcePeak_shouldCheckedToggle() { + mController.mPeakRefreshRate = 88f; + mController.forcePeakRefreshRate(true); + + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + } + + @Test + public void updateState_disableForcePeak_shouldUncheckedToggle() { + mController.mPeakRefreshRate = 88f; + mController.forcePeakRefreshRate(false); + + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + } + + @Test + @Config(qualifiers = "mcc999") + public void isAvailable_withConfigNoShow_returnUnsupported() { + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isAvailable_refreshRateLargerThanDefault_returnTrue() { + mController.mPeakRefreshRate = DEFAULT_REFRESH_RATE + 1; + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void getAvailabilityStatus_refreshRateEqualToDefault_returnFalse() { + mController.mPeakRefreshRate = DEFAULT_REFRESH_RATE; + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void onDeveloperOptionsDisabled_shouldDisablePreference() { + mController.onDeveloperOptionsSwitchDisabled(); + + assertThat(Settings.System.getFloat(mContext.getContentResolver(), + Settings.System.MIN_REFRESH_RATE, -1f)).isEqualTo(NO_CONFIG); + assertThat(mPreference.isChecked()).isFalse(); + assertThat(mPreference.isEnabled()).isFalse(); + } +} |