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);
}