前言
什么是息屏顯示?
息屏顯示就是手機(jī)在息屏狀態(tài)下,屏幕上會(huì)顯示當(dāng)前時(shí)間、日期信息,無需點(diǎn)亮手機(jī)屏幕即可查看。息屏顯示的原理主要是利用了OLED屏幕像素點(diǎn)自發(fā)光的特性,僅顯示時(shí)間的像素點(diǎn)發(fā)光,功耗相比LCD屏幕要低很多。

Android原生的主動(dòng)顯示
玩過Android源碼的同學(xué)應(yīng)該知道,在Settings里有一個(gè)開關(guān)項(xiàng):

這就是設(shè)置 - 顯示下的主動(dòng)顯示選項(xiàng),勾選了這個(gè)選項(xiàng)后,當(dāng)設(shè)備在息屏?xí)r接到一條新通知會(huì)顯示這樣的效果:

是不是發(fā)現(xiàn)與息屏顯示的效果一模一樣,但觸發(fā)條件卻不一樣,需要息屏后有通知才會(huì)顯示出來,并且過一段時(shí)間又會(huì)自動(dòng)消失回歸黑屏,那么怎樣才能做到像三星那樣的息屏后就能一直都顯示呢?
源碼分析
既然知道主動(dòng)顯示開關(guān)是放在設(shè)置里面的,那不妨先從Settings的源碼看起,首先找到主動(dòng)顯示對(duì)應(yīng)的Preference,
<Preference
android:key="ambient_display"
android:title="@string/ambient_display_screen_title"
android:fragment="com.android.settings.display.AmbientDisplaySettings" />
然后發(fā)現(xiàn)在AmbientDisplaySettings里注冊(cè)了一些controller,
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle, AmbientDisplayConfiguration config,
MetricsFeatureProvider metricsFeatureProvider,
AmbientDisplayAlwaysOnPreferenceController.OnPreferenceChangedCallback aodCallback) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new AmbientDisplayNotificationsPreferenceController(context, config,
metricsFeatureProvider));
controllers.add(new AmbientDisplayAlwaysOnPreferenceController(context, config,
aodCallback));
controllers.add(new DoubleTapScreenPreferenceController(context, lifecycle, config,
MY_USER_ID, KEY_AMBIENT_DISPLAY_DOUBLE_TAP));
controllers.add(new PickupGesturePreferenceController(context, lifecycle, config,
MY_USER_ID, KEY_AMBIENT_DISPLAY_PICK_UP));
return controllers;
}
先關(guān)注里面的兩個(gè):AmbientDisplayNotificationsPreferenceController和AmbientDisplayAlwaysOnPreferenceController,看名字大概能知道,第一個(gè)與通知有關(guān),應(yīng)該是上文提到的息屏后來通知才顯示;而第二個(gè)就是我們要找的“始終開啟”。
AmbientDisplayAlwaysOnPreferenceController :
public class AmbientDisplayAlwaysOnPreferenceController extends
AbstractPreferenceController implements PreferenceControllerMixin,
Preference.OnPreferenceChangeListener {
private final int ON = 1;
private final int OFF = 0;
...
// 每次進(jìn)入該P(yáng)referenceScreen都會(huì)調(diào)用一次刷新開關(guān)狀態(tài)
@Override
public void updateState(Preference preference) {
((SwitchPreference) preference).setChecked(isAlwaysOnEnabled(mConfig));
}
// 通過AmbientDisplayConfiguration獲得當(dāng)前enable狀態(tài)
public static boolean isAlwaysOnEnabled(AmbientDisplayConfiguration config) {
return config.alwaysOnEnabled(MY_USER);
}
// 每次點(diǎn)擊后寫入數(shù)據(jù)
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
int enabled = (boolean) newValue ? ON : OFF;
Settings.Secure.putInt(
mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON, enabled);
if (mCallback != null) {
mCallback.onPreferenceChanged();
}
return true;
}
// 該P(yáng)reference是否可用
@Override
public boolean isAvailable() {
return isAvailable(mConfig);
}
// 通過AmbientDisplayConfiguration 獲得available狀態(tài)
public static boolean isAvailable(AmbientDisplayConfiguration config) {
return config.alwaysOnAvailableForUser(MY_USER);
}
...
}
分析AmbientDisplayAlwaysOnPreferenceController的源碼發(fā)現(xiàn),AlwaysOn的enable和available狀態(tài)都需要通過AmbientDisplayConfiguration 這個(gè)類來獲得,并且這個(gè)類位于framework中。
簡單介紹下AmbientDisplayConfiguration 中與alwaysOn有關(guān)的幾個(gè)函數(shù):
public boolean alwaysOnEnabled(int user) {
return boolSettingDefaultOn(Settings.Secure.DOZE_ALWAYS_ON, user) && alwaysOnAvailable()
&& !accessibilityInversionEnabled(user);
}
public boolean alwaysOnAvailable() {
return (alwaysOnDisplayDebuggingEnabled() || alwaysOnDisplayAvailable())
&& ambientDisplayAvailable();
}
private boolean alwaysOnDisplayAvailable() {
return mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnDisplayAvailable);
}
public boolean accessibilityInversionEnabled(int user) {
return boolSettingDefaultOff(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, user);
}
private boolean ambientDisplayAvailable() {
return !TextUtils.isEmpty(ambientDisplayComponent());
}
public String ambientDisplayComponent() {
return mContext.getResources().getString(R.string.config_dozeComponent);
}
private boolean boolSettingDefaultOn(String name, int user) {
return boolSetting(name, user, 1);
}
private boolean boolSetting(String name, int user, int def) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, def, user) != 0;
}
alwaysOnAvailable為true需要同時(shí)滿足兩個(gè)條件:
- 處于debug模式,或者config_dozeAlwaysOnDisplayAvailable為true,這個(gè)值寫在frameworks/base/core/res/res/values/config.xml里,默認(rèn)是false;
- config_dozeComponent取值不為空,這個(gè)值同樣寫在上面講到的config.xml里,默認(rèn)是空著的。
alwaysOnEnabled為true需要同時(shí)滿足三個(gè)條件:
- DOZE_ALWAYS_ON值寫入了1,即Settings里開啟了開關(guān);
- alwaysOnAvailable為true;
- ACCESSIBILITY_DISPLAY_INVERSION_ENABLED值為0,即沒有開啟顏色反轉(zhuǎn)。
原來源碼里面默認(rèn)把AlwaysOn功能給關(guān)閉了,如果想啟用這個(gè)功能,需要修改config.xml里的兩個(gè)值或者強(qiáng)制alwaysOnAvailable返回true,修改后設(shè)置里的主動(dòng)顯示一欄就會(huì)多出一項(xiàng)“始終開啟”可以勾選,這樣一來我們的設(shè)備在息屏之后就能自動(dòng)開啟主動(dòng)顯示功能了。
以上源碼均取自Android O