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.content.Context 19 import android.hardware.display.DisplayManager 20 import android.provider.DeviceConfig 21 import android.provider.Settings.System.PEAK_REFRESH_RATE 22 import com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE 23 import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateAmongAllDisplays 24 import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay 25 import com.android.server.display.feature.flags.Flags 26 import com.android.settings.R 27 import com.android.settingslib.datastore.HandlerExecutor 28 import com.android.settingslib.datastore.KeyValueStore 29 import com.android.settingslib.datastore.KeyedObservableDelegate 30 import com.android.settingslib.datastore.SettingsStore 31 import com.android.settingslib.datastore.SettingsSystemStore 32 import com.android.settingslib.metadata.PreferenceAvailabilityProvider 33 import com.android.settingslib.metadata.PreferenceLifecycleContext 34 import com.android.settingslib.metadata.PreferenceLifecycleProvider 35 import com.android.settingslib.metadata.PreferenceSummaryProvider 36 import com.android.settingslib.metadata.ReadWritePermit 37 import com.android.settingslib.metadata.SensitivityLevel 38 import com.android.settingslib.metadata.SwitchPreference 39 import kotlin.math.roundToInt 40 41 // LINT.IfChange 42 class PeakRefreshRateSwitchPreference : 43 SwitchPreference(KEY, R.string.peak_refresh_rate_title), 44 PreferenceAvailabilityProvider, 45 PreferenceSummaryProvider, 46 PreferenceLifecycleProvider { 47 48 private var propertiesChangedListener: DeviceConfig.OnPropertiesChangedListener? = null 49 storagenull50 override fun storage(context: Context): KeyValueStore = 51 PeakRefreshRateStore(context, SettingsSystemStore.get(context)) 52 53 override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) = 54 ReadWritePermit.ALLOW 55 56 override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = 57 ReadWritePermit.ALLOW 58 59 override val sensitivityLevel 60 get() = SensitivityLevel.NO_SENSITIVITY 61 62 override fun isAvailable(context: Context) = 63 context.resources.getBoolean(R.bool.config_show_smooth_display) && 64 context.peakRefreshRate > DEFAULT_REFRESH_RATE 65 66 override fun getSummary(context: Context): CharSequence? = 67 context.getString(R.string.peak_refresh_rate_summary, context.peakRefreshRate.roundToInt()) 68 69 override fun onStart(context: PreferenceLifecycleContext) { 70 val listener = 71 DeviceConfig.OnPropertiesChangedListener { 72 // Got notified if any property has been changed in NAMESPACE_DISPLAY_MANAGER. The 73 // KEY_PEAK_REFRESH_RATE_DEFAULT value could be added, changed, removed or 74 // unchanged. 75 // Just force a UI update for any case. 76 context.notifyPreferenceChange(KEY) 77 } 78 79 propertiesChangedListener = listener 80 81 DeviceConfig.addOnPropertiesChangedListener( 82 DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 83 HandlerExecutor.main, 84 listener, 85 ) 86 } 87 onStopnull88 override fun onStop(context: PreferenceLifecycleContext) { 89 propertiesChangedListener?.let { 90 DeviceConfig.removeOnPropertiesChangedListener(it) 91 propertiesChangedListener = null 92 } 93 } 94 95 @Suppress("UNCHECKED_CAST") 96 private class PeakRefreshRateStore( 97 private val context: Context, 98 private val settingsStore: SettingsStore, 99 ) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore { 100 containsnull101 override fun contains(key: String) = settingsStore.contains(key) 102 103 override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>): T? { 104 if (key != KEY) return super.getDefaultValue(key, valueType) 105 return context.defaultPeakRefreshRate.refreshRateAsBoolean(context) as T 106 } 107 getValuenull108 override fun <T : Any> getValue(key: String, valueType: Class<T>): T? { 109 if (key != KEY) return null 110 val refreshRate = settingsStore.getFloat(KEY) ?: context.defaultPeakRefreshRate 111 return refreshRate.refreshRateAsBoolean(context) as T 112 } 113 Floatnull114 private fun Float.refreshRateAsBoolean(context: Context) = 115 this.isInfinite() || roundToInt() == context.peakRefreshRate.roundToInt() 116 117 override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) = 118 when { 119 key != KEY -> {} 120 value == null -> settingsStore.setFloat(KEY, null) 121 else -> { 122 val peakRefreshRate = 123 if (value as Boolean) context.refreshRateIfON() else DEFAULT_REFRESH_RATE 124 settingsStore.setFloat(KEY, peakRefreshRate) 125 } 126 } 127 Contextnull128 private fun Context.refreshRateIfON() = 129 when { 130 Flags.backUpSmoothDisplayAndForcePeakRefreshRate() -> Float.POSITIVE_INFINITY 131 else -> peakRefreshRate 132 } 133 } 134 135 companion object { 136 const val KEY = PEAK_REFRESH_RATE 137 private const val INVALIDATE_REFRESH_RATE: Float = -1f 138 139 private val Context.peakRefreshRate: Float 140 get() = 141 Math.round( 142 when { 143 Flags.backUpSmoothDisplayAndForcePeakRefreshRate() -> 144 findHighestRefreshRateAmongAllDisplays(this) 145 else -> findHighestRefreshRateForDefaultDisplay(this) 146 } 147 ) 148 .toFloat() 149 150 private val Context.defaultPeakRefreshRate: Float 151 get() { 152 val defaultPeakRefreshRate = 153 DeviceConfig.getFloat( 154 DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 155 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, 156 INVALIDATE_REFRESH_RATE, 157 ) 158 if (defaultPeakRefreshRate != INVALIDATE_REFRESH_RATE) return defaultPeakRefreshRate 159 return resources 160 .getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate) 161 .toFloat() 162 } 163 } 164 } 165 // LINT.ThenChange(PeakRefreshRatePreferenceController.java) 166