Fix colors in QS Header

Fixes icon and text colors in QS Header to be darker in Light mode.
Also, makes sure that all colors in the icons match.

Does this by implementing the following:
* A new theme that overrides darkIconTheme in Theme.SystemUI by one with
darker colors.
* QuickStatusBarHeader applies that theme and passes is onto its
children, in particular StatusIconContainer/StatusBarMobileView
and BatteryMeterView.
* StatusBarMobileView and BatteryMeterView retrieve the colors from the
theme and apply them accordingly.
* Also applies to QSCarrierGroup
* Additionally, the single color is used for the other icons.

Also invalidates SignalDrawable after setColors is called if at least
one of the colors changed. This is a real issue.

For more info:
* Text and icons set to 70% black on light mode, white in dark mode.
* Dual tone set for background overlaid with fill match single tone.

Test: visual
Fixes: 124466915

Change-Id: Ia6cb28fca90a07c8936726334502e7accf534df6
This commit is contained in:
Fabian Kozynski
2019-04-22 14:23:47 -04:00
parent 7aa5752bbe
commit f86df994fa
11 changed files with 160 additions and 55 deletions

View File

@@ -203,8 +203,11 @@ public class SignalDrawable extends Drawable {
}
public void setColors(int background, int foreground) {
int colorBackground = mPaint.getColor();
int colorForeground = mForegroundPaint.getColor();
mPaint.setColor(background);
mForegroundPaint.setColor(foreground);
if (colorBackground != background || colorForeground != foreground) invalidateSelf();
}
public void setDarkIntensity(float darkIntensity) {

View File

@@ -28,7 +28,8 @@
android:clipToPadding="false"
android:minHeight="20dp"
android:clickable="false"
android:focusable="true">
android:focusable="true"
android:theme="@style/QSHeaderTheme">
<com.android.systemui.statusbar.policy.DateView
android:id="@+id/date"

View File

@@ -20,7 +20,8 @@
android:layout_height="@dimen/qs_header_tooltip_height"
android:layout_below="@id/quick_status_bar_system_icons"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end">
android:paddingEnd="@dimen/status_bar_padding_end"
android:theme="@style/QSHeaderTheme">
<LinearLayout
android:layout_width="wrap_content"
@@ -50,7 +51,6 @@
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
android:src="@drawable/ic_alarm"
android:tint="?android:attr/textColorPrimary"
android:contentDescription="@string/accessibility_quick_settings_alarm_set"
android:visibility="gone"/>
@@ -85,7 +85,6 @@
android:id="@+id/ringer_mode_icon"
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
android:tint="?android:attr/textColorPrimary"
android:visibility="gone"/>
<TextView

View File

@@ -24,4 +24,9 @@
<item name="android:windowIsFloating">true</item>
</style>
<style name="TextAppearance.QS.Status" parent="TextAppearance.QS.TileLabel.Secondary">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
</resources>

View File

@@ -118,6 +118,11 @@
<color name="light_mode_icon_color_dual_tone_background">#4dffffff</color>
<color name="light_mode_icon_color_dual_tone_fill">#ffffff</color>
<color name="dark_mode_qs_icon_color_single_tone">#B3000000</color><!-- 70% black -->
<color name="dark_mode_qs_icon_color_dual_tone_background">#3d000000</color>
<!-- Chosen so fill over background matches single tone -->
<color name="dark_mode_qs_icon_color_dual_tone_fill">#99000000</color>
<color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
<drawable name="forced_resizable_background">#59000000</drawable>

View File

@@ -224,6 +224,7 @@
<style name="TextAppearance.QS.Status" parent="TextAppearance.QS.TileLabel.Secondary">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:textColor">@color/dark_mode_qs_icon_color_single_tone</item>
</style>
<style name="TextAppearance.AppOpsDialog" />
@@ -371,6 +372,17 @@
<item name="fillColor">@color/dark_mode_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/dark_mode_icon_color_single_tone</item>
</style>
<style name="QSHeaderDarkTheme">
<item name="backgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item>
<item name="fillColor">@color/dark_mode_qs_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/dark_mode_qs_icon_color_single_tone</item>
</style>
<style name="QSHeaderTheme" parent="@style/Theme.SystemUI">
<item name="lightIconTheme">@style/DualToneLightTheme</item>
<item name="darkIconTheme">@style/QSHeaderDarkTheme</item>
</style>
<style name="QSIconTheme">
<item name="backgroundColor">?android:attr/textColorHint</item>
<item name="fillColor">?android:attr/textColorPrimary</item>

View File

@@ -23,7 +23,6 @@ import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.animation.ArgbEvaluator;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.annotation.IntDef;
@@ -39,7 +38,6 @@ import android.provider.Settings;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -99,13 +97,7 @@ public class BatteryMeterView extends LinearLayout implements
private boolean mIsSubscribedForTunerUpdates;
private boolean mCharging;
private int mDarkModeSingleToneColor;
private int mDarkModeBackgroundColor;
private int mDarkModeFillColor;
private int mLightModeSingleToneColor;
private int mLightModeBackgroundColor;
private int mLightModeFillColor;
private DualToneHandler mDualToneHandler;
private int mUser;
/**
@@ -161,7 +153,7 @@ public class BatteryMeterView extends LinearLayout implements
addView(mBatteryIconView, mlp);
updateShowPercent();
setColorsFromContext(context);
mDualToneHandler = new DualToneHandler(context);
// Init to not dark at all.
onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
@@ -282,21 +274,7 @@ public class BatteryMeterView extends LinearLayout implements
return;
}
Context dualToneDarkTheme = new ContextThemeWrapper(context,
Utils.getThemeAttr(context, R.attr.darkIconTheme));
Context dualToneLightTheme = new ContextThemeWrapper(context,
Utils.getThemeAttr(context, R.attr.lightIconTheme));
mDarkModeSingleToneColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme,
R.attr.singleToneColor);
mDarkModeBackgroundColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme,
R.attr.backgroundColor);
mDarkModeFillColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme,
R.attr.fillColor);
mLightModeSingleToneColor = Utils.getColorAttrDefaultColor(dualToneLightTheme,
R.attr.singleToneColor);
mLightModeBackgroundColor = Utils.getColorAttrDefaultColor(dualToneLightTheme,
R.attr.backgroundColor);
mLightModeFillColor = Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.fillColor);
mDualToneHandler.setColorsFromContext(context);
}
@Override
@@ -448,12 +426,9 @@ public class BatteryMeterView extends LinearLayout implements
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
mNonAdaptedSingleToneColor = getColorForDarkIntensity(
intensity, mLightModeSingleToneColor, mDarkModeSingleToneColor);
mNonAdaptedForegroundColor = getColorForDarkIntensity(
intensity, mLightModeFillColor, mDarkModeFillColor);
mNonAdaptedBackgroundColor = getColorForDarkIntensity(
intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor);
mNonAdaptedSingleToneColor = mDualToneHandler.getSingleColor(intensity);
mNonAdaptedForegroundColor = mDualToneHandler.getFillColor(intensity);
mNonAdaptedBackgroundColor = mDualToneHandler.getBackgroundColor(intensity);
if (!mUseWallpaperTextColors) {
updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,
@@ -469,10 +444,6 @@ public class BatteryMeterView extends LinearLayout implements
}
}
private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + "";
CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText();

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui
import android.animation.ArgbEvaluator
import android.content.Context
import android.view.ContextThemeWrapper
import com.android.settingslib.Utils
/**
* A color blender for `Theme.SystemUI` and other themes.
*
* This class is used to handle colors from themes in [Context] in the following fashion:
* * The theme associated has a `darkIconTheme` and a `lightIconTheme`
* * Each of these themes define colors for the items `singleToneColor`, `backgroundColor`,
* and `fillColor`.
*
* In particular, `Theme.SystemUI` is a valid [Context]. If the provided [Context] does not have
* the correct themes, the colors that are not found will default to black.
*
* It provides a way to obtain these colors and blends for a given background intensity.
*/
class DualToneHandler(context: Context) {
private lateinit var darkColor: Color
private lateinit var lightColor: Color
init {
setColorsFromContext(context)
}
/**
* Sets the colors in this object from the given [Context]
*
* @param[context] A context with the appropriate themes to extract the colors from.
*/
fun setColorsFromContext(context: Context) {
val dualToneDarkTheme = ContextThemeWrapper(context,
Utils.getThemeAttr(context, R.attr.darkIconTheme))
val dualToneLightTheme = ContextThemeWrapper(context,
Utils.getThemeAttr(context, R.attr.lightIconTheme))
darkColor = Color(
Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.singleToneColor),
Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.backgroundColor),
Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.fillColor))
lightColor = Color(
Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.singleToneColor),
Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.backgroundColor),
Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.fillColor))
}
private fun getColorForDarkIntensity(darkIntensity: Float, lightColor: Int, darkColor: Int) =
ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor) as Int
/**
* Blends the single color associated with the light and dark theme
*
* @param[intensity] Intensity of the background. Correspond with the "percentage" of color
* from `darkIconTheme` to use
* @return The blended color
*/
fun getSingleColor(intensity: Float) =
getColorForDarkIntensity(intensity, lightColor.single, darkColor.single)
/**
* Blends the background color associated with the light and dark theme
*
* @param[intensity] Intensity of the background. Correspond with the "percentage" of color
* from `darkIconTheme` to use
* @return The blended color
*/
fun getBackgroundColor(intensity: Float) =
getColorForDarkIntensity(intensity, lightColor.background, darkColor.background)
/**
* Blends the fill color associated with the light and dark theme
*
* @param[intensity] Intensity of the background. Correspond with the "percentage" of color
* from `darkIconTheme` to use
* @return The blended color
*/
fun getFillColor(intensity: Float) =
getColorForDarkIntensity(intensity, lightColor.fill, darkColor.fill)
private data class Color(val single: Int, val background: Int, val fill: Int)
}

View File

@@ -27,6 +27,7 @@ import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
public class QSCarrier extends LinearLayout {
@@ -35,6 +36,7 @@ public class QSCarrier extends LinearLayout {
private QSCarrierText mCarrierText;
private ImageView mMobileSignal;
private ImageView mMobileRoaming;
private DualToneHandler mDualToneHandler;
private ColorStateList mColorForegroundStateList;
private float mColorForegroundIntensity;
@@ -57,6 +59,7 @@ public class QSCarrier extends LinearLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDualToneHandler = new DualToneHandler(getContext());
mMobileGroup = findViewById(R.id.mobile_combo);
mMobileSignal = findViewById(R.id.mobile_signal);
mMobileRoaming = findViewById(R.id.mobile_roaming);
@@ -66,16 +69,17 @@ public class QSCarrier extends LinearLayout {
android.R.attr.colorForeground);
mColorForegroundStateList = ColorStateList.valueOf(colorForeground);
mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground);
}
public void updateState(QSCarrierGroup.CellSignalState state) {
mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
if (state.visible) {
mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
mMobileRoaming.setImageTintList(mColorForegroundStateList);
mMobileRoaming.setImageTintList(ColorStateList.valueOf(
mDualToneHandler.getSingleColor(mColorForegroundIntensity)));
SignalDrawable d = new SignalDrawable(mContext);
d.setDarkIntensity(mColorForegroundIntensity);
d.setColors(mDualToneHandler.getBackgroundColor(mColorForegroundIntensity),
mDualToneHandler.getFillColor(mColorForegroundIntensity));
mMobileSignal.setImageDrawable(d);
mMobileSignal.setImageLevel(state.mobileSignalIconId);

View File

@@ -25,6 +25,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
@@ -39,6 +40,7 @@ import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.StatsLog;
import android.view.ContextThemeWrapper;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
@@ -53,6 +55,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -115,6 +118,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private TouchAnimator mStatusIconsAlphaAnimator;
private TouchAnimator mHeaderTextContainerAlphaAnimator;
private TouchAnimator mPrivacyChipAlphaAnimator;
private DualToneHandler mDualToneHandler;
private View mSystemIconsView;
private View mQuickQsStatusIcons;
@@ -168,6 +172,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mStatusBarIconController = statusBarIconController;
mActivityStarter = activityStarter;
mPrivacyItemController = privacyItemController;
mDualToneHandler = new DualToneHandler(
new ContextThemeWrapper(context, R.style.QSHeaderTheme));
}
@Override
@@ -205,13 +211,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.colorForeground);
float intensity = getColorIntensity(colorForeground);
int fillColor = fillColorForIntensity(intensity, getContext());
int fillColor = mDualToneHandler.getSingleColor(intensity);
// Set light text on the header icons because they will always be on a black background
applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
// Set the correct tint for the status icons so they contrast
mIconManager.setTint(fillColor);
mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor));
mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor));
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
@@ -314,13 +322,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
}
}
private int fillColorForIntensity(float intensity, Context context) {
if (intensity == 0) {
return context.getColor(R.color.light_mode_icon_color_single_tone);
}
return context.getColor(R.color.dark_mode_icon_color_single_tone);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -576,8 +577,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.colorForeground);
float intensity = getColorIntensity(colorForeground);
int fillColor = fillColorForIntensity(intensity, getContext());
mBatteryRemainingIcon.setColorsFromContext(mHost.getContext());
int fillColor = mDualToneHandler.getSingleColor(intensity);
mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor);
}

View File

@@ -36,6 +36,7 @@ import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
@@ -57,6 +58,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
private ImageView mMobile, mMobileType, mMobileRoaming;
private View mMobileRoamingSpace;
private int mVisibleState = -1;
private DualToneHandler mDualToneHandler;
public static StatusBarMobileView fromContext(Context context, String slot) {
LayoutInflater inflater = LayoutInflater.from(context);
@@ -98,6 +100,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
}
private void init() {
mDualToneHandler = new DualToneHandler(getContext());
mMobileGroup = findViewById(R.id.mobile_group);
mMobile = findViewById(R.id.mobile_signal);
mMobileType = findViewById(R.id.mobile_type);
@@ -208,7 +211,8 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
if (!isInArea(area, this)) {
return;
}
mMobileDrawable.setDarkIntensity(darkIntensity);
mMobileDrawable.setColors(mDualToneHandler.getBackgroundColor(darkIntensity),
mDualToneHandler.getFillColor(darkIntensity));
ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint));
mIn.setImageTintList(color);
mOut.setImageTintList(color);
@@ -231,8 +235,10 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
public void setStaticDrawableColor(int color) {
ColorStateList list = ColorStateList.valueOf(color);
float intensity = color == Color.WHITE ? 0 : 1;
mMobileDrawable.setDarkIntensity(intensity);
// We want the ability to change the theme from the one set by SignalDrawable in certain
// surfaces. In this way, we can pass a theme to the view.
mMobileDrawable.setColors(mDualToneHandler.getBackgroundColor(intensity),
mDualToneHandler.getFillColor(intensity));
mIn.setImageTintList(list);
mOut.setImageTintList(list);
mMobileType.setImageTintList(list);