1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.display 17 18 import android.app.settings.SettingsEnums.ACTION_SMOOTH_DISPLAY 19 import android.content.Context 20 import android.hardware.display.DisplayManager 21 import android.provider.DeviceConfig 22 import android.provider.Settings.System.PEAK_REFRESH_RATE 23 import com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE 24 import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateAmongAllDisplays 25 import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay 26 import com.android.server.display.feature.flags.Flags 27 import com.android.settings.R 28 import com.android.settings.contract.KEY_SMOOTH_DISPLAY 29 import com.android.settings.metrics.PreferenceActionMetricsProvider 30 import com.android.settingslib.datastore.HandlerExecutor 31 import com.android.settingslib.datastore.KeyValueStore 32 import com.android.settingslib.datastore.KeyValueStoreDelegate 33 import com.android.settingslib.datastore.SettingsSystemStore 34 import com.android.settingslib.metadata.PreferenceAvailabilityProvider 35 import com.android.settingslib.metadata.PreferenceLifecycleContext 36 import com.android.settingslib.metadata.PreferenceLifecycleProvider 37 import com.android.settingslib.metadata.PreferenceSummaryProvider 38 import com.android.settingslib.metadata.ReadWritePermit 39 import com.android.settingslib.metadata.SensitivityLevel 40 import com.android.settingslib.metadata.SwitchPreference 41 import kotlin.math.roundToInt 42 43 // LINT.IfChange 44 class PeakRefreshRateSwitchPreference : 45 SwitchPreference(KEY, R.string.peak_refresh_rate_title), 46 PreferenceActionMetricsProvider, 47 PreferenceAvailabilityProvider, 48 PreferenceSummaryProvider, 49 PreferenceLifecycleProvider { 50 51 private var propertiesChangedListener: DeviceConfig.OnPropertiesChangedListener? = null 52 53 override val preferenceActionMetrics: Int 54 get() = ACTION_SMOOTH_DISPLAY 55 tagsnull56 override fun tags(context: Context) = arrayOf(KEY_SMOOTH_DISPLAY) 57 58 override fun storage(context: Context): KeyValueStore = 59 PeakRefreshRateStore(context, SettingsSystemStore.get(context)) 60 61 override fun getReadPermissions(context: Context) = SettingsSystemStore.getReadPermissions() 62 63 override fun getWritePermissions(context: Context) = SettingsSystemStore.getWritePermissions() 64 65 override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = 66 ReadWritePermit.ALLOW 67 68 override fun getWritePermit( 69 context: Context, 70 value: Boolean?, 71 callingPid: Int, 72 callingUid: Int, 73 ) = ReadWritePermit.ALLOW 74 75 override val sensitivityLevel 76 get() = SensitivityLevel.NO_SENSITIVITY 77 78 override fun isAvailable(context: Context) = 79 context.resources.getBoolean(R.bool.config_show_smooth_display) && 80 context.peakRefreshRate > DEFAULT_REFRESH_RATE 81 82 override fun getSummary(context: Context): CharSequence? = 83 context.getString(R.string.peak_refresh_rate_summary, context.peakRefreshRate.roundToInt()) 84 85 override fun onStart(context: PreferenceLifecycleContext) { 86 val listener = 87 DeviceConfig.OnPropertiesChangedListener { 88 // Got notified if any property has been changed in NAMESPACE_DISPLAY_MANAGER. The 89 // KEY_PEAK_REFRESH_RATE_DEFAULT value could be added, changed, removed or 90 // unchanged. 91 // Just force a UI update for any case. 92 context.notifyPreferenceChange(KEY) 93 } 94 95 propertiesChangedListener = listener 96 97 DeviceConfig.addOnPropertiesChangedListener( 98 DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 99 HandlerExecutor.main, 100 listener, 101 ) 102 } 103 onStopnull104 override fun onStop(context: PreferenceLifecycleContext) { 105 propertiesChangedListener?.let { 106 DeviceConfig.removeOnPropertiesChangedListener(it) 107 propertiesChangedListener = null 108 } 109 } 110 111 @Suppress("UNCHECKED_CAST") 112 private class PeakRefreshRateStore( 113 private val context: Context, 114 private val settingsStore: KeyValueStore, 115 ) : KeyValueStoreDelegate { 116 117 override val keyValueStoreDelegate 118 get() = settingsStore 119 getDefaultValuenull120 override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) = 121 context.defaultPeakRefreshRate.refreshRateAsBoolean(context) as T 122 123 override fun <T : Any> getValue(key: String, valueType: Class<T>): T? { 124 val refreshRate = settingsStore.getFloat(KEY) ?: context.defaultPeakRefreshRate 125 return refreshRate.refreshRateAsBoolean(context) as T 126 } 127 Floatnull128 private fun Float.refreshRateAsBoolean(context: Context) = 129 this.isInfinite() || roundToInt() == context.peakRefreshRate.roundToInt() 130 131 override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) = 132 when { 133 key != KEY -> {} 134 value == null -> settingsStore.setFloat(KEY, null) 135 else -> { 136 val peakRefreshRate = 137 if (value as Boolean) context.refreshRateIfON() else DEFAULT_REFRESH_RATE 138 settingsStore.setFloat(KEY, peakRefreshRate) 139 } 140 } 141 Contextnull142 private fun Context.refreshRateIfON() = 143 when { 144 Flags.backUpSmoothDisplayAndForcePeakRefreshRate() -> Float.POSITIVE_INFINITY 145 else -> peakRefreshRate 146 } 147 } 148 149 companion object { 150 const val KEY = PEAK_REFRESH_RATE 151 private const val INVALIDATE_REFRESH_RATE: Float = -1f 152 153 private val Context.peakRefreshRate: Float 154 get() = 155 Math.round( 156 when { 157 Flags.backUpSmoothDisplayAndForcePeakRefreshRate() -> 158 findHighestRefreshRateAmongAllDisplays(this) 159 else -> findHighestRefreshRateForDefaultDisplay(this) 160 } 161 ) 162 .toFloat() 163 164 private val Context.defaultPeakRefreshRate: Float 165 get() { 166 val defaultPeakRefreshRate = 167 DeviceConfig.getFloat( 168 DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 169 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, 170 INVALIDATE_REFRESH_RATE, 171 ) 172 if (defaultPeakRefreshRate != INVALIDATE_REFRESH_RATE) return defaultPeakRefreshRate 173 return resources 174 .getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate) 175 .toFloat() 176 } 177 } 178 } 179 // LINT.ThenChange(PeakRefreshRatePreferenceController.java) 180