作者其他文章
- AOSP | Android 9 控制导航栏的隐藏与显示
- AOSP | Android 11 Settings 开发(01) 环境搭建
- AOSP | Android 11 Framework 修改记录(持续更新)
- AOSP | Android 9 Framework 修改记录(持续更新)
- AOSP | Android 9 过滤Recents
- AOSP | Android 9 APP源码移植到系统源码中进行编译
- 后端 | 沙箱环境下实现支付宝网站支付
- 前端 | vue-echarts渲染时视图模糊的解决办法
问题来源
在进行日常测试时,测试组发现当Android 9的机器更换了黑色(或者深色背景的壁纸后),会导致下拉状态栏中的QS(快速设置项)变得异常,如下图
从上述对比图中可知,异常情况下,不见图标颜色加深变黑,连QS Label也消失了。
问题解析
根据出现问题的步骤,我们复现该问题(也就是切换黑色壁纸,并下拉状态栏),并打印日志查看原因。
2021-12-23 15:25:44.481 735-756/system_process W/WallpaperManagerService: Cannot get theme colors because WallpaperColors is null.
2021-12-23 15:25:45.308 926-926/com.android.systemui D/Tonal: Tonal Palette - index: 1. Main color: ff013631
Colors: ff011d1a, ff013631, ff014f44, ff026d5d, ff039276, ff03b8a7, ff04ddc9, ff37ead9, ff4cfcf7, ff6bfcfc, ff8ef7fd, ffc0fafe, ffe3fefe
2021-12-23 15:25:45.308 926-926/com.android.systemui D/Tonal: Tonal Palette - index: 1. Main color: ff013631
Colors: ff011d1a, ff013631, ff014f44, ff026d5d, ff039276, ff03b8a7, ff04ddc9, ff37ead9, ff4cfcf7, ff6bfcfc, ff8ef7fd, ffc0fafe, ffe3fefe
2021-12-23 15:25:51.243 926-926/com.android.systemui D/QSTileBaseView: handleStateChanged, circleColor -16743049, state: SignalState[,icon=ResourceIcon[resId=0x7f0802e5],iconSupplier=null,label=test-network,secondaryLabel=null,contentDescription=WLAN,WLAN 信号满格。,Koridy-test,dualLabelContentDescription=打开WLAN设置。,expandedAccessibilityClassName=android.widget.Switch,disabledByPolicy=false,dualTarget=true,isTransient=false,state=2,slash="isSlashed=false,rotation=6.0",value=true,activityIn=false,activityOut=false]
可以发现,当我们切换了壁纸后,会打印出
D/Tonal: Tonal Palette - index: 1. Main color: ff013631
查找下在哪里输出这个打印
wayhoi@ubuntu20.04$ find ./ -name "*.java" | xargs grep "Tonal Palette" -s
./packages/apps/Launcher3/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java: // the Material Tonal Palette defines hues from 0 to 1.
./frameworks/base/core/java/com/android/internal/colorextraction/types/Tonal.java: // the Material Tonal Palette defines hues from 0 to 1.
./frameworks/base/core/java/com/android/internal/colorextraction/types/Tonal.java: StringBuilder builder = new StringBuilder("Tonal Palette - index: " + fitIndex +
根据查找结果,查看类frameworks/base/core/java/com/android/internal/colorextraction/types/Tonal.java
public class Tonal implements ExtractionType {
//省略代码
public void extractInto(@Nullable WallpaperColors inWallpaperColors,
@NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark,
@NonNull GradientColors outColorsExtraDark) {
//调用处
boolean success = runTonalExtraction(inWallpaperColors, outColorsNormal, outColorsDark,outColorsExtraDark);
if (!success) {
applyFallback(inWallpaperColors, outColorsNormal, outColorsDark, outColorsExtraDark);
}
}
private boolean runTonalExtraction(@Nullable WallpaperColors inWallpaperColors,
@NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark,
@NonNull GradientColors outColorsExtraDark) {
//省略代码
if (DEBUG) {
StringBuilder builder = new StringBuilder("Tonal Palette - index: " + fitIndex +
". Main color: " + Integer.toHexString(getColorInt(fitIndex, h, s, l)) +
"\nColors: ");
}
//省略代码
}
}
发现源头在Tonal.extractInto(WallpaperColors inWallpaperColors, GradientColors outColorsNormal, GradientColors outColorsDark,GradientColors outColorsExtraDark)
,我们在该方法处加入调用栈打印,看看相关调用。
根据调用栈的日志打印,最终发现最初的调用者在frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java的mColorExtractor.addOnColorsChangedListener(this);
中,我们看一下该接口:
frameworks\base\core\java\com\android\internal\colorextraction\ColorExtractor.java
public interface OnColorsChangedListener {
void onColorsChanged(ColorExtractor colorExtractor, int which);
}
在StatusBar.java中查看具体实现
@Override
public void onColorsChanged(ColorExtractor extractor, int which) {
updateTheme();
}
/**
* Switches theme from light to dark and vice-versa.
*/
protected void updateTheme() {
final boolean inflated = mStackScroller != null;
// The system wallpaper defines if QS should be light or dark.
WallpaperColors systemColors = mColorExtractor
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
final boolean useDarkTheme = systemColors != null
&& (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
if (isUsingDarkTheme() != useDarkTheme) {
mUiOffloadThread.submit(() -> {
try {
mOverlayManager.setEnabled("com.android.systemui.theme.dark",
useDarkTheme, mLockscreenUserManager.getCurrentUserId());
} catch (RemoteException e) {
Log.w(TAG, "Can't change theme", e);
}
});
}
// Lock wallpaper defines the color of the majority of the views, hence we'll use it
// to set our default theme.
final boolean lockDarkText = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, true
/* ignoreVisibility */).supportsDarkText();
final int themeResId = lockDarkText ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI;
if (mContext.getThemeResId() != themeResId) {
mContext.setTheme(themeResId);
if (inflated) {
onThemeChanged();
}
}
if (inflated) {
int which;
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
which = WallpaperManager.FLAG_LOCK;
} else {
which = WallpaperManager.FLAG_SYSTEM;
}
final boolean useDarkText = mColorExtractor.getColors(which,
true /* ignoreVisibility */).supportsDarkText();
mStackScroller.updateDecorViews(useDarkText);
// Make sure we have the correct navbar/statusbar colors.
mStatusBarWindowManager.setKeyguardDark(useDarkText);
}
}
可以注意到上述updateTheme()
方法中,有一段注释
// The system wallpaper defines if QS should be light or dark.
// Translate:系统壁纸定义了QS应该是亮还是暗。
WallpaperColors systemColors = mColorExtractor
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
final boolean useDarkTheme = systemColors != null
&& (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
if (isUsingDarkTheme() != useDarkTheme) {
mUiOffloadThread.submit(() -> {
try {
mOverlayManager.setEnabled("com.android.systemui.theme.dark",
useDarkTheme, mLockscreenUserManager.getCurrentUserId());
} catch (RemoteException e) {
Log.w(TAG, "Can't change theme", e);
}
});
}
于是,我们就找到了问题根源所在之处,接下来要做的就是阅读上述代码,进行逻辑修改。
我们需要的是,切换黑色/深色壁纸后,QS的颜色不变。
可以注意到
final boolean useDarkTheme = systemColors != null
&& (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
mOverlayManager.setEnabled("com.android.systemui.theme.dark",
useDarkTheme, mLockscreenUserManager.getCurrentUserId());
boolean useDarkTheme = systemColors != null
, 见文知意,可以猜测出,这个值就是是否根据壁纸颜色来调节QS的颜色(主题),因为我们始终不需要黑色,所以我们将userDarkTheme
固定为false
。
WallpaperColors systemColors = mColorExtractor.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
final boolean useDarkTheme = systemColors != null
&& (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
if (isUsingDarkTheme() != useDarkTheme) {
mUiOffloadThread.submit(() -> {
try {
mOverlayManager.setEnabled("com.android.systemui.theme.dark",
+ /// M: modify by wayhoi, QS close dark theme @{
- useDarkTheme, mLockscreenUserManager.getCurrentUserId());
+ false/*useDarkTheme*/, mLockscreenUserManager.getCurrentUserId());
+ /// }
} catch (RemoteException e) {
Log.w(TAG, "Can't change theme", e);
}
});
}
刷机,测试,复现步骤走一遍,发现结果如我们预期及需求。
总结
该问题其实来源于Android 9的特性,加入了主题(黑色主题、白色主题、自动主题(根据壁纸的颜色来判定为是黑还是白主题)),过程中,打印日志很重要,同时要注意的一点就是,取好一个变量名也十分重要,如文中出现的useDarkTheme,让我可以一看到就知道这里就是控制主题的。