blob: aa23251b9b856562d62d73d6f7f70843059e9649 [file] [log] [blame]
Fiona Campbelld4eb2952019-11-04 17:19:56 +00001/*
2 * Copyright (C) 2019 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
17package com.android.internal;
18
19
20import android.content.ContentResolver;
21import android.content.Context;
22import android.database.ContentObserver;
23import android.net.Uri;
24import android.os.Handler;
25import android.os.Message;
26import android.os.PowerManager;
27import android.os.UserHandle;
28import android.provider.Settings;
29import android.util.MathUtils;
30
31import java.util.LinkedList;
32import java.util.Queue;
33
34/**
35 * BrightnessSynchronizer helps convert between the int (old) system and float
36 * (new) system for storing the brightness. It has methods to convert between the two and also
37 * observes for when one of the settings is changed and syncs this with the other.
38 */
39public class BrightnessSynchronizer{
40
41 private static final int MSG_UPDATE_FLOAT = 1;
42 private static final int MSG_UPDATE_INT = 2;
43
44 private static final String TAG = "BrightnessSynchronizer";
45 private static final Uri BRIGHTNESS_URI =
46 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
47 private static final Uri BRIGHTNESS_FLOAT_URI =
48 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
49
50 // The tolerance within which we consider brightness values approximately equal to eachother.
51 // This value is approximately 1/3 of the smallest possible brightness value.
52 public static final float EPSILON = 0.001f;
53
54 private final Context mContext;
55
56 private final Queue<Object> mWriteHistory = new LinkedList<>();
57
58 private final Handler mHandler = new Handler() {
59 @Override
60 public void handleMessage(Message msg) {
61 switch (msg.what) {
62 case MSG_UPDATE_FLOAT:
63 updateBrightnessFloatFromInt(msg.arg1);
64 break;
65 case MSG_UPDATE_INT:
66 updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
67 break;
68 default:
69 super.handleMessage(msg);
70 }
71
72 }
73 };
74
75
76 public BrightnessSynchronizer(Context context) {
77 final BrightnessSyncObserver mBrightnessSyncObserver;
78 mContext = context;
79 mBrightnessSyncObserver = new BrightnessSyncObserver(mHandler);
80 mBrightnessSyncObserver.startObserving();
81 }
82
83 /**
84 * Converts between the int brightness system and the float brightness system.
85 */
86 public static float brightnessIntToFloat(Context context, int brightnessInt) {
87 PowerManager pm = context.getSystemService(PowerManager.class);
88 float pmMinBrightness = pm.getBrightnessConstraint(
89 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
90 float pmMaxBrightness = pm.getBrightnessConstraint(
91 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
92 int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
93 PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
94 PowerManager.BRIGHTNESS_ON);
95 int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
96 PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
97 PowerManager.BRIGHTNESS_ON);
98
99 return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt,
100 pmMinBrightness, pmMaxBrightness);
101 }
102
103 /**
104 * Converts between the int brightness system and the float brightness system.
105 */
106 public static float brightnessIntToFloat(int brightnessInt, int minInt, int maxInt,
107 float minFloat, float maxFloat) {
108 if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
109 return PowerManager.BRIGHTNESS_OFF_FLOAT;
110 } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
111 return PowerManager.BRIGHTNESS_INVALID_FLOAT;
112 } else {
113 return MathUtils.constrainedMap(minFloat, maxFloat, (float) minInt, (float) maxInt,
114 brightnessInt);
115 }
116 }
117
118 /**
119 * Converts between the float brightness system and the int brightness system.
120 */
121 public static int brightnessFloatToInt(Context context, float brightnessFloat) {
122 PowerManager pm = context.getSystemService(PowerManager.class);
123 float pmMinBrightness = pm.getBrightnessConstraint(
124 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
125 float pmMaxBrightness = pm.getBrightnessConstraint(
126 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
127 int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
128 PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
129 PowerManager.BRIGHTNESS_ON);
130 int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
131 PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
132 PowerManager.BRIGHTNESS_ON);
133
134 return brightnessFloatToInt(brightnessFloat, pmMinBrightness, pmMaxBrightness,
135 minBrightnessInt, maxBrightnessInt);
136 }
137
138 /**
139 * Converts between the float brightness system and the int brightness system.
140 */
141 public static int brightnessFloatToInt(float brightnessFloat, float minFloat, float maxFloat,
142 int minInt, int maxInt) {
143 if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
144 return PowerManager.BRIGHTNESS_OFF;
145 } else if (Float.isNaN(brightnessFloat)) {
146 return PowerManager.BRIGHTNESS_INVALID;
147 } else {
148 return Math.round(MathUtils.constrainedMap((float) minInt, (float) maxInt, minFloat,
149 maxFloat, brightnessFloat));
150 }
151 }
152
153 private static float getScreenBrightnessFloat(Context context) {
154 return Settings.System.getFloatForUser(context.getContentResolver(),
155 Settings.System.SCREEN_BRIGHTNESS_FLOAT, Float.NaN, UserHandle.USER_CURRENT);
156 }
157
158 private static int getScreenBrightnessInt(Context context) {
159 return Settings.System.getIntForUser(context.getContentResolver(),
160 Settings.System.SCREEN_BRIGHTNESS, 0, UserHandle.USER_CURRENT);
161 }
162
163 private float mPreferredSettingValue;
164
165 /**
166 * Updates the float setting based on a passed in int value. This is called whenever the int
167 * setting changes. mWriteHistory keeps a record of the values that been written to the settings
168 * from either this method or updateBrightnessIntFromFloat. This is to ensure that the value
169 * being set is due to an external value being set, rather than the updateBrightness* methods.
170 * The intention of this is to avoid race conditions when the setting is being changed
171 * frequently and to ensure we are not reacting to settings changes from this file.
172 * @param value Brightness value as int to store in the float setting.
173 */
174 private void updateBrightnessFloatFromInt(int value) {
175 Object topOfQueue = mWriteHistory.peek();
176 if (topOfQueue != null && topOfQueue.equals(value)) {
177 mWriteHistory.poll();
178 } else {
179 if (brightnessFloatToInt(mContext, mPreferredSettingValue) == value) {
180 return;
181 }
182 float newBrightnessFloat = brightnessIntToFloat(mContext, value);
183 mWriteHistory.offer(newBrightnessFloat);
184 mPreferredSettingValue = newBrightnessFloat;
185 Settings.System.putFloatForUser(mContext.getContentResolver(),
186 Settings.System.SCREEN_BRIGHTNESS_FLOAT, newBrightnessFloat,
187 UserHandle.USER_CURRENT);
188 }
189 }
190
191 /**
192 * Updates the int setting based on a passed in float value. This is called whenever the float
193 * setting changes. mWriteHistory keeps a record of the values that been written to the settings
194 * from either this method or updateBrightnessFloatFromInt. This is to ensure that the value
195 * being set is due to an external value being set, rather than the updateBrightness* methods.
196 * The intention of this is to avoid race conditions when the setting is being changed
197 * frequently and to ensure we are not reacting to settings changes from this file.
198 * @param value Brightness setting as float to store in int setting.
199 */
200 private void updateBrightnessIntFromFloat(float value) {
201 int newBrightnessInt = brightnessFloatToInt(mContext, value);
202 Object topOfQueue = mWriteHistory.peek();
203 if (topOfQueue != null && topOfQueue.equals(value)) {
204 mWriteHistory.poll();
205 } else {
206 mWriteHistory.offer(newBrightnessInt);
207 mPreferredSettingValue = value;
208 Settings.System.putIntForUser(mContext.getContentResolver(),
209 Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
210 }
211 }
212
213 /**
214 * Tests whether two brightness float values are within a small enough tolerance
215 * of each other.
216 * @param a first float to compare
217 * @param b second float to compare
218 * @return whether the two values are within a small enough tolerance value
219 */
220 public static boolean floatEquals(float a, float b) {
221 if (a == b) {
222 return true;
223 } else if (Float.isNaN(a) && Float.isNaN(b)) {
224 return true;
225 } else if (Math.abs(a - b) < EPSILON) {
226 return true;
227 } else {
228 return false;
229 }
230 }
231
232 private class BrightnessSyncObserver extends ContentObserver {
233 /**
234 * Creates a content observer.
235 * @param handler The handler to run {@link #onChange} on, or null if none.
236 */
237 BrightnessSyncObserver(Handler handler) {
238 super(handler);
239 }
240
241 @Override
242 public void onChange(boolean selfChange) {
243 onChange(selfChange, null);
244 }
245
246 @Override
247 public void onChange(boolean selfChange, Uri uri) {
248 if (selfChange) {
249 return;
250 }
251 if (BRIGHTNESS_URI.equals(uri)) {
252 int currentBrightness = getScreenBrightnessInt(mContext);
253 mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
254 } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
255 float currentFloat = getScreenBrightnessFloat(mContext);
256 int toSend = Float.floatToIntBits(currentFloat);
257 mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
258 }
259 }
260
261 public void startObserving() {
262 final ContentResolver cr = mContext.getContentResolver();
263 cr.unregisterContentObserver(this);
264 cr.registerContentObserver(BRIGHTNESS_URI, false, this, UserHandle.USER_ALL);
265 cr.registerContentObserver(BRIGHTNESS_FLOAT_URI, false, this, UserHandle.USER_ALL);
266 }
267
268 public void stopObserving() {
269 final ContentResolver cr = mContext.getContentResolver();
270 cr.unregisterContentObserver(this);
271 }
272 }
273}