Android屏幕亮度适配
前言
最近由于接手了视频项目中的亮度调整功能,抽空总结了下Android亮度调节的方式,以及在如今系统定制化的情况下会遇到的坑。
亮度调节模式
查看官网说明,可以看到,目前Android提供了两种亮度调节模式。
SCREEN_BRIGHTNESS_MODE_AUTOMATIC 自动调节亮度,系统根据环境变化自动调整屏幕的亮度,以适应眼睛的舒适度。
SCREEN_BRIGHTNESS_MODE_MANUAL 手动调节模式,该模式即通过状态栏下拉控制面板或者设置中的亮度调节来改变当前系统的屏幕亮度。
对于这两种模式,系统提供了属性接口来设置亮度调节模式,可以用如下方法实现:
public static void setAutoAdjustBrightness(Context context, boolean auto) {
int value = 0;
if (auto) {
value = Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
} else {
value = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
}
ContentResolver cr = context.getContentResolver();
Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS_MODE, value);
}
设置亮度值
目前Android提供了两种方式来调亮度值:一是通过调整系统屏幕亮度值,二是通过调整界面当前窗口的亮度值,下面分别介绍这两种方法以及区别。
屏幕亮度值介绍
首先先介绍下屏幕亮度值,查看亮度值的介绍,目前原生系统屏幕的亮度值范围位于:0~255。获取方式如下:
public static int getSystemBrightness(Context context) {
int result = 0;
ContentResolver cr = context.getContentResolver();
try {
result = Settings.System.getInt(cr, Settings.System.SCREEN_BRIGHTNESS);
} catch (SettingNotFoundException e) {
e.printStackTrace();
}
return result;
}
以下是通过获取当前界面窗口的亮度值,但不一定是系统的亮度值。
public static int getActivityBrightness(Activity activity) {
WindowManager.LayoutParams params = activity.getWindow().getAttributes();
return int(params.screenBrightness * 255);
}
调整系统屏幕亮度值
调整系统的屏幕亮度值的方式如下。
public static void setSystemBrightness(Context context, int value) {
ContentResolver cr = context.getContentResolver();
// 0 ~ 255
Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS, value);
}
当然有个前提,需要保证AndroidManifest.xml中声明如下权限:
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
调整当前窗口亮度值
调整界面当前窗口的屏幕亮度值的方式如下。
public static void setActivityBrightness(Activity activity, int value) {
WindowManager.LayoutParams params = activity.getWindow().getAttributes();
params.screenBrightness = value / 255f;
activity.getWindow().setAttributes(params);
}
区别
上述的第一种方式是通过调整系统的亮度值来改变当前页面的亮度值,因此其他页面也会跟着调整亮度,并且会受自动亮度模式影响;而第二种方式是通过设置Window属性的方式来控制页面的亮度值,不会影响其他页面,并且不受自动亮度模式影响。
关于定制化的坑
概述
由于Android系统开源的缘故,许多厂商对其进行了深度的定制,所以安卓的碎片化很严重,并且随着系统版本的不断衍进,定制化、碎片化会越来越严重。而对于屏幕亮度来说,厂商为了适配新功能,对原生规定的亮度值范围0-255进行了扩展,最大扩展至0-2047/0-4095,并且值的计算并不是线性的,大多数是使用原生的HLG算法,因此应用层有屏幕亮度调整功能的,需要对该扩展进行适配。
适配方式
对于前面所说的两种调整屏幕亮度的方式,只有调整系统亮度的方式需要适配厂商的扩展。我们所需做的就是在使用屏幕亮度值时,将HLG算法转换成线性算法,然后应用在应用中,下面提供一个算法解析。
private static final int GAMMA_SPACE_MAX = getGammaSpaceMax();
private static final float R = 0.2f;
private static final float A = 0.314f;
private static final float B = 0.06f;
private static final float C = 0.221f;
/**
* A function for converting from the gamma space that the slider works in to the
* linear space that the setting works in.
* @param val val The slider value.
* @param min The minimum acceptable value for the setting.
* @param max The maximum acceptable value for the setting.
* @return The corresponding setting value.
*/
public static int convertGammaToLinear(int val, int min, int max) {
final float normalizedVal = norm(0, GAMMA_SPACE_MAX, val);
final float ret;
if (normalizedVal <= R) {
ret = sq(normalizedVal / R);
} else {
ret = (float) (Math.exp((normalizedVal - C) / A) + B);
}
// HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1]
// in order to derive the correct setting value.
// MIUI MOD: START
// return Math.round(MathUtils.lerp(min, max, ret / 12));
int tmpVal = Math.round(lerp(min, max, ret / 12));
return tmpVal > max ? max : tmpVal;
// END
}
/**
* A function for converting from the linear space that the setting works in to the
* gamma space that the slider works in.
* @param val The brightness setting value.
* @param min The minimum acceptable value for the setting.
* @param max The maximum acceptable value for the setting.
* @return The corresponding slider value
*/
public static int convertLinearToGamma(int val, int min, int max) {
// For some reason, HLG normalizes to the range [0, 12] rather than [0, 1]
final float normalizedVal = norm(min, max, val) * 12;
final float ret;
if (normalizedVal <= 1f) {
ret = (float) (Math.sqrt(normalizedVal) * R);
} else {
ret = A * log(normalizedVal - B) + C;
}
return Math.round(lerp(0, GAMMA_SPACE_MAX, ret));
}
private static float norm(float start, float stop, float value) {
return (value - start) / (stop - start);
}
private static float sq(float v) {
return v * v;
}
private static float lerp(float start, float stop, float amount) {
return start + (stop - start) * amount;
}
public static float log(float a) {
return (float) Math.log(a);
}