1 /*
2  * Copyright (C) 2020 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 
17 package com.android.settings.display;
18 
19 import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
20 import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateAmongAllDisplays;
21 import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;
22 
23 import android.content.Context;
24 import android.hardware.display.DisplayManager;
25 import android.os.Handler;
26 import android.provider.DeviceConfig;
27 import android.provider.Settings;
28 import android.util.Log;
29 
30 import androidx.annotation.VisibleForTesting;
31 import androidx.preference.Preference;
32 import androidx.preference.PreferenceScreen;
33 
34 import com.android.server.display.feature.flags.Flags;
35 import com.android.settings.R;
36 import com.android.settings.core.TogglePreferenceController;
37 import com.android.settingslib.core.lifecycle.LifecycleObserver;
38 import com.android.settingslib.core.lifecycle.events.OnStart;
39 import com.android.settingslib.core.lifecycle.events.OnStop;
40 
41 import java.util.concurrent.Executor;
42 
43 public class PeakRefreshRatePreferenceController extends TogglePreferenceController
44         implements LifecycleObserver, OnStart, OnStop {
45 
46     @VisibleForTesting float mPeakRefreshRate;
47 
48     private static final String TAG = "RefreshRatePrefCtr";
49     private static final float INVALIDATE_REFRESH_RATE = -1f;
50 
51     private final Handler mHandler;
52     private final IDeviceConfigChange mOnDeviceConfigChange;
53     private DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
54     private Preference mPreference;
55 
56     private interface IDeviceConfigChange {
onDefaultRefreshRateChanged()57         void onDefaultRefreshRateChanged();
58     }
59 
PeakRefreshRatePreferenceController(Context context, String key)60     public PeakRefreshRatePreferenceController(Context context, String key) {
61         super(context, key);
62         mHandler = new Handler(context.getMainLooper());
63         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
64         mOnDeviceConfigChange =
65                 new IDeviceConfigChange() {
66                     public void onDefaultRefreshRateChanged() {
67                         updateState(mPreference);
68                     }
69                 };
70         mPeakRefreshRate = Math.round(Flags.backUpSmoothDisplayAndForcePeakRefreshRate()
71                 ? findHighestRefreshRateAmongAllDisplays(context)
72                 : findHighestRefreshRateForDefaultDisplay(context));
73         Log.d(
74                 TAG,
75                 "DEFAULT_REFRESH_RATE : "
76                         + DEFAULT_REFRESH_RATE
77                         + " mPeakRefreshRate : "
78                         + mPeakRefreshRate);
79     }
80 
81     @Override
displayPreference(PreferenceScreen screen)82     public void displayPreference(PreferenceScreen screen) {
83         super.displayPreference(screen);
84 
85         int defaultPeakRefreshRate = Math.round(mPeakRefreshRate);
86 
87         mPreference = screen.findPreference(getPreferenceKey());
88         mPreference.setSummary(mContext.getString(
89                 R.string.peak_refresh_rate_summary, defaultPeakRefreshRate));
90     }
91 
92     @Override
getAvailabilityStatus()93     public int getAvailabilityStatus() {
94         if (mContext.getResources().getBoolean(R.bool.config_show_smooth_display)) {
95             return mPeakRefreshRate > DEFAULT_REFRESH_RATE ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
96         } else {
97             return UNSUPPORTED_ON_DEVICE;
98         }
99     }
100 
101     @Override
isChecked()102     public boolean isChecked() {
103         final float peakRefreshRate =
104                 Settings.System.getFloat(
105                         mContext.getContentResolver(),
106                         Settings.System.PEAK_REFRESH_RATE,
107                         getDefaultPeakRefreshRate());
108         return Math.round(peakRefreshRate) == Math.round(mPeakRefreshRate)
109                 || Float.isInfinite(peakRefreshRate);
110     }
111 
112     @Override
setChecked(boolean isChecked)113     public boolean setChecked(boolean isChecked) {
114         final float valueIfChecked = Flags.backUpSmoothDisplayAndForcePeakRefreshRate()
115                 ? Float.POSITIVE_INFINITY : mPeakRefreshRate;
116         final float peakRefreshRate = isChecked ? valueIfChecked : DEFAULT_REFRESH_RATE;
117         Log.d(TAG, "setChecked to : " + peakRefreshRate);
118 
119         return Settings.System.putFloat(
120                 mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, peakRefreshRate);
121     }
122 
123     @Override
getSliceHighlightMenuRes()124     public int getSliceHighlightMenuRes() {
125         return R.string.menu_key_display;
126     }
127 
128     @Override
onStart()129     public void onStart() {
130         mDeviceConfigDisplaySettings.startListening();
131     }
132 
133     @Override
onStop()134     public void onStop() {
135         mDeviceConfigDisplaySettings.stopListening();
136     }
137 
138     @VisibleForTesting
injectDeviceConfigDisplaySettings( DeviceConfigDisplaySettings deviceConfigDisplaySettings)139     void injectDeviceConfigDisplaySettings(
140             DeviceConfigDisplaySettings deviceConfigDisplaySettings) {
141         mDeviceConfigDisplaySettings = deviceConfigDisplaySettings;
142     }
143 
144     @VisibleForTesting
145     class DeviceConfigDisplaySettings
146             implements DeviceConfig.OnPropertiesChangedListener, Executor {
startListening()147         public void startListening() {
148             DeviceConfig.addOnPropertiesChangedListener(
149                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
150                     this /* Executor */,
151                     this /* Listener */);
152         }
153 
stopListening()154         public void stopListening() {
155             DeviceConfig.removeOnPropertiesChangedListener(this);
156         }
157 
getDefaultPeakRefreshRate()158         public float getDefaultPeakRefreshRate() {
159             float defaultPeakRefreshRate =
160                     DeviceConfig.getFloat(
161                             DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
162                             DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT,
163                             INVALIDATE_REFRESH_RATE);
164             Log.d(TAG, "DeviceConfig getDefaultPeakRefreshRate : " + defaultPeakRefreshRate);
165 
166             return defaultPeakRefreshRate;
167         }
168 
169         @Override
onPropertiesChanged(DeviceConfig.Properties properties)170         public void onPropertiesChanged(DeviceConfig.Properties properties) {
171             // Got notified if any property has been changed in NAMESPACE_DISPLAY_MANAGER. The
172             // KEY_PEAK_REFRESH_RATE_DEFAULT value could be added, changed, removed or unchanged.
173             // Just force a UI update for any case.
174             if (mOnDeviceConfigChange != null) {
175                 mOnDeviceConfigChange.onDefaultRefreshRateChanged();
176                 updateState(mPreference);
177             }
178         }
179 
180         @Override
execute(Runnable runnable)181         public void execute(Runnable runnable) {
182             if (mHandler != null) {
183                 mHandler.post(runnable);
184             }
185         }
186     }
187 
getDefaultPeakRefreshRate()188     private float getDefaultPeakRefreshRate() {
189         float defaultPeakRefreshRate = mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
190         if (defaultPeakRefreshRate == INVALIDATE_REFRESH_RATE) {
191             defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
192                     com.android.internal.R.integer.config_defaultPeakRefreshRate);
193         }
194 
195         Log.d(TAG, "DeviceConfig getDefaultPeakRefreshRate : " + defaultPeakRefreshRate);
196         return defaultPeakRefreshRate;
197     }
198 }
199