Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a819dee authored by Jeff DeCew's avatar Jeff DeCew
Browse files

Adding ColorUpdateLogger to track color updates so that we demonstrate the...

Adding ColorUpdateLogger to track color updates so that we demonstrate the root cause of our theme bugs

Bug: 294347738
Test: dumpsysui ColorUpdateLogger
Change-Id: I4879dd28a8d88b3ee6fc1f09c9029eb653bfa460
Flag: ACONFIG com.android.systemui.notification_color_update_logger DEVELOPMENT
parent d7c53b21
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -28,6 +28,13 @@ flag {
    bug: "217799515"
}

flag {
    name: "notification_color_update_logger"
    namespace: "systemui"
    description: "Enabled debug logging and dumping of notification color updates."
    bug: "294347738"
}

flag {
    name: "notifications_footer_view_refactor"
    namespace: "systemui"
@@ -452,4 +459,3 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}
+15 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;

import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
import static com.android.systemui.util.ColorUtilKt.hexColorString;

import android.content.Context;
import android.content.res.Configuration;
@@ -42,6 +43,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -187,7 +189,7 @@ public class NotificationShelf extends ActivatableNotificationView {

    @Override
    public String toString() {
        return "NotificationShelf"
        return super.toString()
                + " (hideBackground=" + mHideBackground
                + " notGoneIndex=" + mNotGoneIndex
                + " hasItemsInStableShelf=" + mHasItemsInStableShelf
@@ -368,6 +370,17 @@ public class NotificationShelf extends ActivatableNotificationView {
                && isYInView(localY, slop, top, bottom);
    }

    @Override
    public void updateBackgroundColors() {
        super.updateBackgroundColors();
        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
        if (colorUpdateLogger != null) {
            colorUpdateLogger.logEvent("Shelf.updateBackgroundColors()",
                    "normalBgColor=" + hexColorString(getNormalBgColor())
                            + " background=" + mBackgroundNormal.toDumpString());
        }
    }

    /**
     * Update the shelf appearance based on the other notifications around it. This transforms
     * the icons from the notification area into the shelf.
+153 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.notification

import android.icu.text.SimpleDateFormat
import android.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.Flags.notificationColorUpdateLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.util.Compile
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printCollection
import com.android.systemui.util.withIncreasedIndent
import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
import java.util.Locale
import java.util.SortedSet
import java.util.TreeSet
import javax.inject.Inject

@SysUISingleton
class ColorUpdateLogger
@Inject
constructor(
    val featureFlags: FeatureFlagsClassic,
    dumpManager: DumpManager,
) : Dumpable {

    inline val isEnabled
        get() = Compile.IS_DEBUG && notificationColorUpdateLogger()
    private val frames: MutableList<Frame> = mutableListOf()

    init {
        dumpManager.registerDumpable(this)
        if (isEnabled) {
            instance = this
        }
    }

    @JvmOverloads
    fun logTriggerEvent(@CompileTimeConstant type: String, extra: String? = null) {
        if (!isEnabled) return
        val event = Event(type = type, extraValue = extra)
        val didAppend = frames.lastOrNull()?.tryAddTrigger(event) == true
        if (!didAppend) {
            frames.add(Frame(event))
            if (frames.size > maxFrames) frames.removeFirst()
        }
    }

    @JvmOverloads
    fun logEvent(@CompileTimeConstant type: String, extra: String? = null) {
        if (!isEnabled) return
        val frame = frames.lastOrNull() ?: return
        frame.events.add(Event(type = type, extraValue = extra))
        frame.trim()
    }

    @JvmOverloads
    fun logNotificationEvent(
        @CompileTimeConstant type: String,
        key: String,
        extra: String? = null
    ) {
        if (!isEnabled) return
        val frame = frames.lastOrNull() ?: return
        frame.events.add(Event(type = type, extraValue = extra, notificationKey = key))
        frame.trim()
    }

    override fun dump(pwOrig: PrintWriter, args: Array<out String>) {
        val pw = pwOrig.asIndenting()
        pw.println("enabled: $isEnabled")
        pw.printCollection("frames", frames) { it.dump(pw) }
    }

    private class Frame(event: Event) {
        val startTime: Long = event.time
        val events: MutableList<Event> = mutableListOf(event)
        val triggers: SortedSet<String> = TreeSet<String>().apply { add(event.type) }
        var trimmedEvents: Int = 0

        fun tryAddTrigger(newEvent: Event): Boolean {
            if (newEvent.time < startTime) return false
            if (newEvent.time - startTime > triggerStartsNewFrameAge) return false
            if (newEvent.type in triggers) return false
            triggers.add(newEvent.type)
            events.add(newEvent)
            trim()
            return true
        }

        fun trim() {
            if (events.size > maxEventsPerFrame) {
                events.removeFirst()
                trimmedEvents++
            }
        }

        fun dump(pw: IndentingPrintWriter) {
            pw.println("Frame")
            pw.withIncreasedIndent {
                pw.println("startTime: ${timeString(startTime)}")
                pw.printCollection("triggers", triggers)
                pw.println("trimmedEvents: $trimmedEvents")
                pw.printCollection("events", events) { it.dump(pw) }
            }
        }
    }

    private class Event(
        @CompileTimeConstant val type: String,
        val extraValue: String? = null,
        val notificationKey: String? = null,
    ) {
        val time: Long = System.currentTimeMillis()

        fun dump(pw: IndentingPrintWriter) {
            pw.append(timeString(time)).append(": ").append(type)
            extraValue?.let { pw.append(" ").append(it) }
            notificationKey?.let { pw.append(" ---- ").append(logKey(it)) }
            pw.println()
        }
    }

    private companion object {
        @JvmStatic
        var instance: ColorUpdateLogger? = null
            private set
        private const val maxFrames = 5
        private const val maxEventsPerFrame = 250
        private const val triggerStartsNewFrameAge = 5000

        private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
        private fun timeString(time: Long): String = dateFormat.format(time)
    }
}
+13 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
@@ -41,7 +42,8 @@ class ViewConfigCoordinator @Inject internal constructor(
    private val mConfigurationController: ConfigurationController,
    private val mLockscreenUserManager: NotificationLockscreenUserManager,
    private val mGutsManager: NotificationGutsManager,
    private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
    private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val colorUpdateLogger: ColorUpdateLogger,
) : Coordinator, ConfigurationController.ConfigurationListener {

    private var mIsSwitchingUser = false
@@ -51,11 +53,13 @@ class ViewConfigCoordinator @Inject internal constructor(

    private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
        override fun onUserSwitching(userId: Int) {
            colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitching()")
            log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
            mIsSwitchingUser = true
        }

        override fun onUserSwitchComplete(userId: Int) {
            colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitchComplete()")
            log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
            mIsSwitchingUser = false
            applyChangesOnUserSwitched()
@@ -64,6 +68,7 @@ class ViewConfigCoordinator @Inject internal constructor(

    private val mUserChangedListener = object : UserChangedListener {
        override fun onUserChanged(userId: Int) {
            colorUpdateLogger.logTriggerEvent("VCC.mUserChangedListener.onUserChanged()")
            log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
            applyChangesOnUserSwitched()
        }
@@ -77,6 +82,7 @@ class ViewConfigCoordinator @Inject internal constructor(
    }

    override fun onDensityOrFontScaleChanged() {
        colorUpdateLogger.logTriggerEvent("VCC.onDensityOrFontScaleChanged()")
        log {
            val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
            "ViewConfigCoordinator.onDensityOrFontScaleChanged()" +
@@ -93,6 +99,7 @@ class ViewConfigCoordinator @Inject internal constructor(
    }

    override fun onUiModeChanged() {
        colorUpdateLogger.logTriggerEvent("VCC.onUiModeChanged()")
        log {
            val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
            "ViewConfigCoordinator.onUiModeChanged()" +
@@ -107,10 +114,12 @@ class ViewConfigCoordinator @Inject internal constructor(
    }

    override fun onThemeChanged() {
        colorUpdateLogger.logTriggerEvent("VCC.onThemeChanged()")
        onDensityOrFontScaleChanged()
    }

    private fun applyChangesOnUserSwitched() {
        colorUpdateLogger.logEvent("VCC.applyChangesOnUserSwitched()")
        if (mReinflateNotificationsOnUserSwitched) {
            updateNotificationsOnDensityOrFontScaleChanged()
            mReinflateNotificationsOnUserSwitched = false
@@ -122,6 +131,8 @@ class ViewConfigCoordinator @Inject internal constructor(
    }

    private fun updateNotificationsOnUiModeChanged() {
        colorUpdateLogger.logEvent("VCC.updateNotificationsOnUiModeChanged()",
                "mode=" + mConfigurationController.nightModeName)
        log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
        traceSection("updateNotifOnUiModeChanged") {
            mPipeline?.allNotifs?.forEach { entry ->
@@ -131,6 +142,7 @@ class ViewConfigCoordinator @Inject internal constructor(
    }

    private fun updateNotificationsOnDensityOrFontScaleChanged() {
        colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
        mPipeline?.allNotifs?.forEach { entry ->
            entry.onDensityOrFontScaleChanged()
            val exposedGuts = entry.areGutsExposed()
+22 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.footer.ui.view;
import static android.graphics.PorterDuff.Mode.SRC_ATOP;

import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
import static com.android.systemui.util.ColorUtilKt.hexColorString;

import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -40,11 +41,13 @@ import androidx.annotation.NonNull;

import com.android.settingslib.Utils;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.row.FooterViewButton;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.util.DrawableDumpKt;
import com.android.systemui.util.DumpUtilsKt;

import java.io.PrintWriter;
@@ -239,6 +242,10 @@ public class FooterView extends StackScrollerDecorView {

    @Override
    protected void onFinishInflate() {
        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
        if (colorUpdateLogger != null) {
            colorUpdateLogger.logTriggerEvent("Footer.onFinishInflate()");
        }
        super.onFinishInflate();
        mClearAllButton = (FooterViewButton) findSecondaryView();
        mManageOrHistoryButton = findViewById(R.id.manage_text);
@@ -348,6 +355,10 @@ public class FooterView extends StackScrollerDecorView {

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
        if (colorUpdateLogger != null) {
            colorUpdateLogger.logTriggerEvent("Footer.onConfigurationChanged()");
        }
        super.onConfigurationChanged(newConfig);
        updateColors();
        if (!FooterViewRefactor.isEnabled()) {
@@ -365,14 +376,17 @@ public class FooterView extends StackScrollerDecorView {
                com.android.internal.R.attr.materialColorOnSurface);
        final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
        final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
        final @ColorInt int scHigh;
        if (!notificationBackgroundTintOptimization()) {
            final @ColorInt int scHigh = Utils.getColorAttrDefaultColor(mContext,
            scHigh = Utils.getColorAttrDefaultColor(mContext,
                    com.android.internal.R.attr.materialColorSurfaceContainerHigh);
            if (scHigh != 0) {
                final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
                clearAllBg.setColorFilter(bgColorFilter);
                manageBg.setColorFilter(bgColorFilter);
            }
        } else {
            scHigh = 0;
        }
        mClearAllButton.setBackground(clearAllBg);
        mClearAllButton.setTextColor(onSurface);
@@ -380,6 +394,13 @@ public class FooterView extends StackScrollerDecorView {
        mManageOrHistoryButton.setTextColor(onSurface);
        mSeenNotifsFooterTextView.setTextColor(onSurface);
        mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
        if (colorUpdateLogger != null) {
            colorUpdateLogger.logEvent("Footer.updateColors()",
                    "textColor(onSurface)=" + hexColorString(onSurface)
                            + " backgroundTint(surfaceContainerHigh)=" + hexColorString(scHigh)
                            + " background=" + DrawableDumpKt.dumpToString(manageBg));
        }
    }

    private void updateResources() {
Loading