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