转自吴小龙公众号
下拉状态栏有个亮度的进度条,如果开启了亮度自动调节开关,会随着周围光线变化,这个进度条也会随着变化,接下来就是看看这个功能是如何实现的。
源码版本
基于 Android 9.0 分析。
BrightnessDialog,位于:frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
ToggleSliderView,位于:frameworks/base/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java
DisplayPowerController,位于:frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
AutomaticBrightnessController,位于:frameworks/base/services/core/java/com/android/server/display/AutomaticBrightnessController.java
BrightnessMappingStrategy,
概述
状态栏里亮度页面是 BrightnessDialog,其中进度条设置是 ToggleSliderView,亮度自动调节主要是 DisplayPowerController 和 AutomaticBrightnessController 两个类,当亮度发生变化时,如果关联到 ToggleSliderView,用的是 ContentObserver,Uri 为 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ。
源码梳理
1、BrightnessDialog#onCreate:
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//省略部分代码mBrightnessController = new BrightnessController(this, icon, slider);
}
2、这里进行了 BrightnessController 初始化,来看下:
public BrightnessController(Context context, ImageView icon, ToggleSlider control) {//省略部分代码mBrightnessObserver = new BrightnessObserver(mHandler);//省略部分代码
}
又进行了 BrightnessObserver 初始化:
/** ContentObserver to watch brightness **/
private class BrightnessObserver extends ContentObserver {//省略部分代码private final Uri BRIGHTNESS_FOR_VR_URI =Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR);//Add By WuXiaolong for AutomaticBrightnessprivate final Uri BRIGHTNESS_ADJ_URI =Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ);public BrightnessObserver(Handler handler) {super(handler);}@Overridepublic void onChange(boolean selfChange) {onChange(selfChange, null);}@Overridepublic void onChange(boolean selfChange, Uri uri) {if (selfChange) return;if (BRIGHTNESS_MODE_URI.equals(uri)) {mBackgroundHandler.post(mUpdateModeRunnable);mBackgroundHandler.post(mUpdateSliderRunnable);} //省略部分代码//Add By WuXiaolong for AutomaticBrightnesselse if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {mBackgroundHandler.post(mUpdateSliderRunnable);} else {mBackgroundHandler.post(mUpdateModeRunnable);mBackgroundHandler.post(mUpdateSliderRunnable);}for (BrightnessStateChangeCallback cb : mChangeCallbacks) {cb.onBrightnessLevelChanged();}}public void startObserving() {final ContentResolver cr = mContext.getContentResolver();cr.unregisterContentObserver(this);//省略部分代码cr.registerContentObserver(BRIGHTNESS_FOR_VR_URI,false, this, UserHandle.USER_ALL);//Add By WuXiaolong for AutomaticBrightnesscr.registerContentObserver(BRIGHTNESS_ADJ_URI,false, this, UserHandle.USER_ALL);}public void stopObserving() {final ContentResolver cr = mContext.getContentResolver();cr.unregisterContentObserver(this);}
}
其实我目前下载的源码,这块功能是不全的,我已经加上了,哪里进行 BrightnessObserver 的 ContentObserver 注册呢?
3、回到 BrightnessDialog#onStart:
@Override
protected void onStart() {super.onStart();mBrightnessController.registerCallbacks();MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
}
4、调用mBrightnessController.registerCallbacks();
最终走到 mStartListeningRunnable:
private final Runnable mStartListeningRunnable = new Runnable() {@Overridepublic void run() {//BrightnessObserver 注册mBrightnessObserver.startObserving();mUserTracker.startTracking();// Update the slider and mode before attaching the listener so we don't// receive the onChanged notifications for the initial values.mUpdateModeRunnable.run();mUpdateSliderRunnable.run();mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER);}
};
当亮度有变化时,会走 BrightnessObserver#onChange,最终走到:
private final Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {mExternalChange = true;try {switch (msg.what) {//省略部分代码case MSG_UPDATE_SLIDER:updateSlider(msg.arg1, msg.arg2 != 0);break;//省略部分代码default:super.handleMessage(msg);}} finally {mExternalChange = false;}}
};
走 updateSlider方法,到 :
private void animateSliderTo(int target) {if (!mControlValueInitialized) {// Don't animate the first value since it's default state isn't meamControl.setValue(target);mControlValueInitialized = true;}//省略部分代码
}
5、跳到 ToggleSliderView#setValue:
@Override
public void setValue(int value) {//这里正是修改进度条mSlider.setProgress(value);if (mMirror != null) {mMirror.setValue(value);}
}
接下来就是看看亮度自动调节主要的两个类 DisplayPowerController 和 AutomaticBrightnessController。DisplayPowerController 属于 Display 模块,其控制设备屏幕亮灭、背光、与Power关系密切,这里主要看下屏幕亮度的控制这方面的逻辑。
6、首先,在 DisplayManagerService 中初始化 DisplayPowerController,如下:
private final class LocalService extends DisplayManagerInternal {@Overridepublic void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,SensorManager sensorManager) {synchronized (mSyncRoot) {//省略部分代码mDisplayPowerController = new DisplayPowerController(mContext, callbacks, handler, sensorManager, blanker);}mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);}
7、接着看下 DisplayPowerController 构造方法,如下:
public DisplayPowerController(Context context,DisplayPowerCallbacks callbacks, Handler handler,SensorManager sensorManager, DisplayBlanker blanker) {//省略部分代码mUseSoftwareAutoBrightnessConfig = resources.getBoolean(com.android.internal.R.bool.config_automatic_brightness_available);//省略部分代码if (mUseSoftwareAutoBrightnessConfig) {//省略部分代码mBrightnessMapper = BrightnessMappingStrategy.create(resources);if (mBrightnessMapper != null) {mAutomaticBrightnessController = new AutomaticBrightnessController(this,handler.getLooper(), sensorManager, mBrightnessMapper,lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,autoBrightnessResetAmbientLuxAfterWarmUp, hysteresisLevels);} else {mUseSoftwareAutoBrightnessConfig = false;}}//省略部分代码mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();mTemporaryAutoBrightnessAdjustment = Float.NaN;//省略部分代码
}
由于亮屏之后屏幕自动亮度才会生效,所以在亮屏的时候,流程会走到 DisplayPowerController 中的核心函数 updatePowerState():
private void updatePowerState() {// Update the power state request.//省略部分代码final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();if (autoBrightnessAdjustmentChanged) {mTemporaryAutoBrightnessAdjustment = Float.NaN;}// Use the autobrightness adjustment override if set.final float autoBrightnessAdjustment;if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;mAppliedTemporaryAutoBrightnessAdjustment = true;} else {autoBrightnessAdjustment = mAutoBrightnessAdjustment;mAppliedTemporaryAutoBrightnessAdjustment = false;}boolean hadUserBrightnessPoint = false;// Configure auto-brightness.if (mAutomaticBrightnessController != null) {hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();mAutomaticBrightnessController.configure(autoBrightnessEnabled,mBrightnessConfiguration,mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,userSetBrightnessChanged, autoBrightnessAdjustment,autoBrightnessAdjustmentChanged, mPowerRequest.policy);}// Apply auto-brightness.boolean slowChange = false;if (brightness < 0) {float newAutoBrightnessAdjustment = autoBrightnessAdjustment;if (autoBrightnessEnabled) {brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();newAutoBrightnessAdjustment =mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();}if (brightness >= 0) {// Use current auto-brightness value and slowly adjust to changes.brightness = clampScreenBrightness(brightness);if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {slowChange = true; // slowly adapt to auto-brightness}// Tell the rest of the system about the new brightness. Note that we do this// before applying the low power or dim transformations so that the slider// accurately represents the full possible range, even if they range changes what// it means in absolute terms.putScreenBrightnessSetting(brightness);mAppliedAutoBrightness = true;} else {mAppliedAutoBrightness = false;}if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) {// If the autobrightness controller has decided to change the adjustment value// used, make sure that's reflected in settings.putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);}} else {mAppliedAutoBrightness = false;}//省略部分代码
}
接下来分别看看 autoBrightnessAdjustment 和 newAutoBrightnessAdjustment 怎么来的?
autoBrightnessAdjustment 是来自 mTemporaryAutoBrightnessAdjustment 或 mAutoBrightnessAdjustment 赋值,mAutoBrightnessAdjustment 在第 7 步mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
有初始化,看下 getAutoBrightnessAdjustmentSetting():
private float getAutoBrightnessAdjustmentSetting() {final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
}
继续看下 clampAutoBrightnessAdjustment:
private static float clampAutoBrightnessAdjustment(float value) {return MathUtils.constrain(value, -1.0f, 1.0f);
}
这里注意下 MathUtils.constrain() 表示百分比缩放函数,比如 MathUtils.constrain(0.5, 0, 255) 表示 (255-0)*0.5。
这样了解了 autoBrightnessAdjustment,接下来看 newAutoBrightnessAdjustment。
8、回到 DisplayPowerController#updatePowerState(),看到 newAutoBrightnessAdjustment 调用了 AutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment()
,最终是到了 BrightnessMapper#getAutoBrightnessAdjustment()
其中 mAutoBrightnessAdjustment 变量,赋值是在BrightnessMapper#setAutoBrightnessAdjustment
:
@Override
public boolean setAutoBrightnessAdjustment(float adjustment) {adjustment = MathUtils.constrain(adjustment, -1, 1);if (adjustment == mAutoBrightnessAdjustment) {return false;}if (DEBUG) {Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +adjustment);PLOG.start("auto-brightness adjustment");}mAutoBrightnessAdjustment = adjustment;computeSpline();return true;
}
9、BrightnessMapper#setAutoBrightnessAdjustment 这个方法调用又回到了 AutomaticBrightnessController#setAutoBrightnessAdjustment:
private boolean setAutoBrightnessAdjustment(float adjustment) {return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
}
AutomaticBrightnessController#setAutoBrightnessAdjustment
调用是来到AutomaticBrightnessController#configure()
方法:
public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,float brightness, boolean userChangedBrightness, float adjustment,boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {// While dozing, the application processor may be suspended which will prevent us from// receiving new information from the light sensor. On some devices, we may be able to// switch to a wake-up light sensor instead but for now we will simply disable the sensor// and hold onto the last computed screen auto brightness. We save the dozing flag for// debugging purposes.boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);boolean changed = setBrightnessConfiguration(configuration);changed |= setDisplayPolicy(displayPolicy);if (userChangedAutoBrightnessAdjustment) {changed |= setAutoBrightnessAdjustment(adjustment);}if (userChangedBrightness && enable) {// Update the brightness curve with the new user control point. It's critical this// happens after we update the autobrightness adjustment since it may reset it.changed |= setScreenBrightnessByUser(brightness);}final boolean userInitiatedChange =userChangedBrightness || userChangedAutoBrightnessAdjustment;if (userInitiatedChange && enable && !dozing) {prepareBrightnessAdjustmentSample();}changed |= setLightSensorEnabled(enable && !dozing);if (changed) {updateAutoBrightness(false /*sendUpdate*/);}
}
AutomaticBrightnessController#configure()
调用来到了 DisplayPowerController #updatePowerState()
。
这样也知道了 newAutoBrightnessAdjustment,继续 putAutoBrightnessAdjustmentSetting:
private void putAutoBrightnessAdjustmentSetting(float adjustment) {mAutoBrightnessAdjustment = adjustment;Settings.System.putFloatForUser(mContext.getContentResolver(),Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, UserHandle.USER_CURRENT);
}
就调到第 4 步 BrightnessObserver#onChange,进度条随之变化,Over