blob: aa23251b9b856562d62d73d6f7f70843059e9649 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
import java.util.LinkedList;
import java.util.Queue;
/**
* BrightnessSynchronizer helps convert between the int (old) system and float
* (new) system for storing the brightness. It has methods to convert between the two and also
* observes for when one of the settings is changed and syncs this with the other.
*/
public class BrightnessSynchronizer{
private static final int MSG_UPDATE_FLOAT = 1;
private static final int MSG_UPDATE_INT = 2;
private static final String TAG = "BrightnessSynchronizer";
private static final Uri BRIGHTNESS_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
private static final Uri BRIGHTNESS_FLOAT_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
// The tolerance within which we consider brightness values approximately equal to eachother.
// This value is approximately 1/3 of the smallest possible brightness value.
public static final float EPSILON = 0.001f;
private final Context mContext;
private final Queue<Object> mWriteHistory = new LinkedList<>();
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_FLOAT:
updateBrightnessFloatFromInt(msg.arg1);
break;
case MSG_UPDATE_INT:
updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
break;
default:
super.handleMessage(msg);
}
}
};
public BrightnessSynchronizer(Context context) {
final BrightnessSyncObserver mBrightnessSyncObserver;
mContext = context;
mBrightnessSyncObserver = new BrightnessSyncObserver(mHandler);
mBrightnessSyncObserver.startObserving();
}
/**
* Converts between the int brightness system and the float brightness system.
*/
public static float brightnessIntToFloat(Context context, int brightnessInt) {
PowerManager pm = context.getSystemService(PowerManager.class);
float pmMinBrightness = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
float pmMaxBrightness = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
PowerManager.BRIGHTNESS_ON);
int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
PowerManager.BRIGHTNESS_ON);
return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt,
pmMinBrightness, pmMaxBrightness);
}
/**
* Converts between the int brightness system and the float brightness system.
*/
public static float brightnessIntToFloat(int brightnessInt, int minInt, int maxInt,
float minFloat, float maxFloat) {
if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
return PowerManager.BRIGHTNESS_OFF_FLOAT;
} else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
} else {
return MathUtils.constrainedMap(minFloat, maxFloat, (float) minInt, (float) maxInt,
brightnessInt);
}
}
/**
* Converts between the float brightness system and the int brightness system.
*/
public static int brightnessFloatToInt(Context context, float brightnessFloat) {
PowerManager pm = context.getSystemService(PowerManager.class);
float pmMinBrightness = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
float pmMaxBrightness = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
PowerManager.BRIGHTNESS_ON);
int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
PowerManager.BRIGHTNESS_ON);
return brightnessFloatToInt(brightnessFloat, pmMinBrightness, pmMaxBrightness,
minBrightnessInt, maxBrightnessInt);
}
/**
* Converts between the float brightness system and the int brightness system.
*/
public static int brightnessFloatToInt(float brightnessFloat, float minFloat, float maxFloat,
int minInt, int maxInt) {
if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
return PowerManager.BRIGHTNESS_OFF;
} else if (Float.isNaN(brightnessFloat)) {
return PowerManager.BRIGHTNESS_INVALID;
} else {
return Math.round(MathUtils.constrainedMap((float) minInt, (float) maxInt, minFloat,
maxFloat, brightnessFloat));
}
}
private static float getScreenBrightnessFloat(Context context) {
return Settings.System.getFloatForUser(context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_FLOAT, Float.NaN, UserHandle.USER_CURRENT);
}
private static int getScreenBrightnessInt(Context context) {
return Settings.System.getIntForUser(context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, 0, UserHandle.USER_CURRENT);
}
private float mPreferredSettingValue;
/**
* Updates the float setting based on a passed in int value. This is called whenever the int
* setting changes. mWriteHistory keeps a record of the values that been written to the settings
* from either this method or updateBrightnessIntFromFloat. This is to ensure that the value
* being set is due to an external value being set, rather than the updateBrightness* methods.
* The intention of this is to avoid race conditions when the setting is being changed
* frequently and to ensure we are not reacting to settings changes from this file.
* @param value Brightness value as int to store in the float setting.
*/
private void updateBrightnessFloatFromInt(int value) {
Object topOfQueue = mWriteHistory.peek();
if (topOfQueue != null && topOfQueue.equals(value)) {
mWriteHistory.poll();
} else {
if (brightnessFloatToInt(mContext, mPreferredSettingValue) == value) {
return;
}
float newBrightnessFloat = brightnessIntToFloat(mContext, value);
mWriteHistory.offer(newBrightnessFloat);
mPreferredSettingValue = newBrightnessFloat;
Settings.System.putFloatForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_FLOAT, newBrightnessFloat,
UserHandle.USER_CURRENT);
}
}
/**
* Updates the int setting based on a passed in float value. This is called whenever the float
* setting changes. mWriteHistory keeps a record of the values that been written to the settings
* from either this method or updateBrightnessFloatFromInt. This is to ensure that the value
* being set is due to an external value being set, rather than the updateBrightness* methods.
* The intention of this is to avoid race conditions when the setting is being changed
* frequently and to ensure we are not reacting to settings changes from this file.
* @param value Brightness setting as float to store in int setting.
*/
private void updateBrightnessIntFromFloat(float value) {
int newBrightnessInt = brightnessFloatToInt(mContext, value);
Object topOfQueue = mWriteHistory.peek();
if (topOfQueue != null && topOfQueue.equals(value)) {
mWriteHistory.poll();
} else {
mWriteHistory.offer(newBrightnessInt);
mPreferredSettingValue = value;
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
}
}
/**
* Tests whether two brightness float values are within a small enough tolerance
* of each other.
* @param a first float to compare
* @param b second float to compare
* @return whether the two values are within a small enough tolerance value
*/
public static boolean floatEquals(float a, float b) {
if (a == b) {
return true;
} else if (Float.isNaN(a) && Float.isNaN(b)) {
return true;
} else if (Math.abs(a - b) < EPSILON) {
return true;
} else {
return false;
}
}
private class BrightnessSyncObserver extends ContentObserver {
/**
* Creates a content observer.
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
BrightnessSyncObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
onChange(selfChange, null);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (selfChange) {
return;
}
if (BRIGHTNESS_URI.equals(uri)) {
int currentBrightness = getScreenBrightnessInt(mContext);
mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
} else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
float currentFloat = getScreenBrightnessFloat(mContext);
int toSend = Float.floatToIntBits(currentFloat);
mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
}
}
public void startObserving() {
final ContentResolver cr = mContext.getContentResolver();
cr.unregisterContentObserver(this);
cr.registerContentObserver(BRIGHTNESS_URI, false, this, UserHandle.USER_ALL);
cr.registerContentObserver(BRIGHTNESS_FLOAT_URI, false, this, UserHandle.USER_ALL);
}
public void stopObserving() {
final ContentResolver cr = mContext.getContentResolver();
cr.unregisterContentObserver(this);
}
}
}