Android 7.1 亮度调节之BrightnessController
通过“设置->显示->亮度”可以调节屏幕亮度,当点击“亮度”这个选项时,会弹出如下菜单:实际上这是一个系统Activity,下面就先来分析一下是如何启动这个Activity的。
启动亮度调节Activity
“设置->显示”的布局文件是"display_settings.xml":
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:title="@string/display_settings"//亮度
settings:keywords="@string/keywords_display">
<PreferenceScreen
android:key="brightness"
android:title="@string/brightness"
settings:keywords="@string/keywords_display_brightness_level">
//通过Intent启动SHOW_BRIGHTNESS_DIALOG
<intent android:action="android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
</PreferenceScreen>
...以下省略
当点击“亮度”的PreferenceScreen时,会通过Intent启动 “android.intent.action.SHOW_BRIGHTNESS_DIALOG”。这个action对应的是什么呢?可以在“frameworks\base\packages\SystemUI\AndroidManifest.xml”中找到答案:
<activity
android:name=".settings.BrightnessDialog"
android:label="@string/quick_settings_brightness_dialog_title"
android:theme="@android:style/Theme.DeviceDefault.Dialog"
android:finishOnCloseSystemDialogs="true"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
可以看到,这个action会启动SystemUI的"BrightnessDialog"。 那么接下来就分析一下BrightnessDialog这个Activity吧。
BrightnessDialog
这个Activity非常简单,我都在犹豫要不要贴代码。。。
public class BrightnessDialog extends Activity {
private BrightnessController mBrightnessController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Window window = getWindow();
window.setGravity(Gravity.TOP);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.quick_settings_brightness_dialog);
final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
mBrightnessController = new BrightnessController(this, icon, slider);
}
@Override
protected void onStart() {
super.onStart();
mBrightnessController.registerCallbacks();
MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
}
@Override
protected void onStop() {
super.onStop();
MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
mBrightnessController.unregisterCallbacks();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
finish();
}
return super.onKeyDown(keyCode, event);
}
}
BrightnessDialog的onCreate一共干了两件事,一是加载了布局文件,二是创建了BrightnessController。而onStart就是执行了BrightnessController.registerCallbacks。所以,重点在BrightnessController。接下来就分析一下BrightnessController。
BrightnessController
先看一下BrightnessController的构造函数:
public BrightnessController(Context context, ImageView icon, ToggleSlider control) {
mContext = context;
mIcon = icon;
mControl = control;
mBackgroundHandler = new Handler(Looper.getMainLooper());
mUserTracker = new CurrentUserTracker(mContext) {
@Override
public void onUserSwitched(int newUserId) {
mBackgroundHandler.post(mUpdateModeRunnable);
mBackgroundHandler.post(mUpdateSliderRunnable);
}
};
//创建BrightnessObserver
mBrightnessObserver = new BrightnessObserver(mHandler);
...以下省略
其中创建了一个BrightnessObserver对象,BrightnessObserver继承自ContentObserver,当注册的Content发生变化时,它会接收到回调。那么我们看看它注册了什么Content:
public void startObserving() {
final ContentResolver cr = mContext.getContentResolver();
cr.unregisterContentObserver(this);
cr.registerContentObserver(
BRIGHTNESS_MODE_URI,//亮度模式
false, this, UserHandle.USER_ALL);
cr.registerContentObserver(
BRIGHTNESS_URI,//亮度
false, this, UserHandle.USER_ALL);
cr.registerContentObserver(
BRIGHTNESS_FOR_VR_URI,//用于VR的亮度
false, this, UserHandle.USER_ALL);
cr.registerContentObserver(
BRIGHTNESS_ADJ_URI,//自动亮度调节
false, this, UserHandle.USER_ALL);
}
BrightnessObserver一共对四个URI进行了关注,就好像拿起了望远镜对他们说:“I am watching U"。
BrightnessObserver.startObserving的调用关系是:BrightnessDialog.start -> BrightnessController.registerCallbacks ->mBackgroundHandler.post(mStartListeningRunnable) ->mBrightnessObserver.startObserving()。所以,当启动BrightnessDialog时,这望远镜就拿起来了。因为今天是分析亮度调节,我们只关注BRIGHTNESS_URI即亮度值的变化即可。
接着看一下BrightnessController在registerCallbacks 时,mStartListeningRunnable除了startObserving还做了什么:
private final Runnable mStartListeningRunnable = new Runnable() {
@Override
public void run() {
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();//1
mUpdateSliderRunnable.run();//2
mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER);//3
}
};
这一段代码非常有用。标记1处是更新了亮度模式和icon。标记2处updateSlider,以使进度条与当前亮度值一致。标记3处是注册BrightnessController为ToggleSlider的Listener,所以当滑动TogglerSlider时,会触发BrightnessController.onChanged。我们只看手动滑动TogglerSlider的情形:
@Override
public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value,
boolean stopTracking) {
...以上省略
} else if (!mAutomatic) {
final int val = value + mMinimumBacklight;
if (stopTracking) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_BRIGHTNESS, val);
}
setBrightness(val);//1
if (!tracking) {//2
AsyncTask.execute(new Runnable() {
public void run() {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, val,
UserHandle.USER_CURRENT);//3
}
});
}
}
for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
cb.onBrightnessLevelChanged();//目前为空
}
}
标记1处是实际设置亮度的地方,本篇先不展开。标记3处是将当前亮度值保存到系统数据库。标记2处对tracking进行了判断,当tracking为false时才保存亮度值。当连续滑动调节亮度的ToggleSlider没有停止时,tracking为true;当停止滑动后,tracking为false。也就是说在滑动过程中只改变屏幕亮度,并不保存亮度值,只有在停止滑动时,才保存亮度值。
此时有没有想起,我们最开始registerCallbacks时已经注册了BrightnessObserver?其中就包括数据库中的亮度值。标记3处修改了数据库中亮度值,所以此时会调用BrightnessObserver的onChange:
@Override
public void onChange(boolean selfChange, Uri uri) {
if (selfChange) return;
...以上省略
else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
mBackgroundHandler.post(mUpdateSliderRunnable);
}
...以下省略
}
我只选取了对亮度值变化的处理,即mUpdateSliderRunnable。
private final Runnable mUpdateSliderRunnable = new Runnable() {
@Override
public void run() {
...以上省略
} else {
int value;
value = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight,
UserHandle.USER_CURRENT);
mHandler.obtainMessage(MSG_UPDATE_SLIDER, mMaximumBacklight - mMinimumBacklight,
value - mMinimumBacklight).sendToTarget();
}
}
};
这个Runnable的作用是updateSlider。(其实在滑动ToggleSlider进行亮度调节时,并不需要updateSlider,但是在另一种调节亮度的方式——通过按键调节时,就需要updateSlider了。否则你按了Brightness Up 按键,屏幕亮度也增加了,ToggleSlider的进度条却岿然不动,所以此处还是加上了mUpdateSliderRunnable 。)
以上就分析完用手滑动ToggleSlider进行亮度调节的全过程。至于真正设置亮度的方法setBrightness:
private void setBrightness(int brightness) {
try {
mPower.setTemporaryScreenBrightnessSettingOverride(brightness);
} catch (RemoteException ex) {
}
}
留待下篇再说。ヾ( ̄▽ ̄)ByeBye