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

Commit a0974ea8 authored by Steve Elliott's avatar Steve Elliott
Browse files

Fix NIC colors not applying correctly

We need to re-apply the colors whenever the child views of the NIC
change. To model this better, we bind directly to the child views, as
opposed to the NIC; this keeps the colorization logic centralized.

Bug: 278765923
Flag: ACONFIG com.android.systemui.notifications_icon_container_refactor DEVELOPMENT
Test: atest SystemUITests
Test: manual - verify icon colors are correct on AOD

Change-Id: I4bd8fb7d181268065ecb976f5265f6ce657d086a
parent 10614f64
Loading
Loading
Loading
Loading
+33 −51
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import android.graphics.Color
import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
@@ -32,7 +31,6 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColorLookup
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
@@ -52,6 +50,7 @@ import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -88,14 +87,17 @@ object NotificationIconContainerViewBinder {
        return view.repeatWhenAttached {
            lifecycleScope.run {
                launch {
                    val iconColors =
                        viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
                    viewModel.icons.bindIcons(
                        view,
                        configuration,
                        configurationController,
                        viewStore
                    )
                        viewStore,
                    ) { _, sbiv ->
                        iconColors.collect { sbiv.updateTintForIcon(it, contrastColorUtil) }
                    }
                }
                launch { viewModel.iconColors.bindIconColors(view, contrastColorUtil) }
                launch { viewModel.bindIsolatedIcon(view, viewStore) }
                launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
            }
@@ -119,17 +121,19 @@ object NotificationIconContainerViewBinder {
                        configuration,
                        configurationController,
                        viewStore,
                    )
                }
                launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
                launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
                launch {
                    ) { _, sbiv ->
                        configuration
                            .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
                        .bindIconColors(view)
                            .collect { tint ->
                                sbiv.staticDrawableColor = tint
                                sbiv.setDecorColor(tint)
                            }
                    }
                }
                launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
                launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
            }
        }
    }

    /** Binds to [NotificationIconContainer.setAnimationsEnabled] */
@@ -137,31 +141,6 @@ object NotificationIconContainerViewBinder {
        collect(view::setAnimationsEnabled)
    }

    /**
     * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
     * of the [children] of an [NotificationIconContainer].
     */
    private suspend fun Flow<NotificationIconColorLookup>.bindIconColors(
        view: NotificationIconContainer,
        contrastColorUtil: ContrastColorUtil,
    ) {
        mapNotNull { lookup -> lookup.iconColors(view.viewBounds) }
            .collect { iconLookup -> view.applyTint(iconLookup, contrastColorUtil) }
    }

    /**
     * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
     * of the [children] of an [NotificationIconContainer].
     */
    private suspend fun Flow<Int>.bindIconColors(view: NotificationIconContainer) {
        collect { tint ->
            view.children.filterIsInstance<StatusBarIconView>().forEach { icon ->
                icon.staticDrawableColor = tint
                icon.setDecorColor(tint)
            }
        }
    }

    private suspend fun Flow<AnimatedValue<Boolean>>.bindIsDozing(
        view: NotificationIconContainer,
        dozeParameters: DozeParameters,
@@ -208,12 +187,19 @@ object NotificationIconContainerViewBinder {
        }
    }

    /** Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children]. */
    /**
     * Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children].
     *
     * [bindIcon] will be invoked to bind a child [StatusBarIconView] to an icon associated with the
     * given `iconKey`. The parent [Job] of this coroutine will be cancelled automatically when the
     * view is to be unbound.
     */
    private suspend fun Flow<NotificationIconsViewData>.bindIcons(
        view: NotificationIconContainer,
        configuration: ConfigurationState,
        configurationController: ConfigurationController,
        viewStore: IconViewStore,
        bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
    ): Unit = coroutineScope {
        val iconSizeFlow: Flow<Int> =
            configuration.getDimensionPixelSize(
@@ -242,6 +228,7 @@ object NotificationIconContainerViewBinder {
            }
        }

        val iconBindings = mutableMapOf<String, Job>()
        var prevIcons = NotificationIconsViewData()
        sample(layoutParams, ::Pair).collect {
            (iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams),
@@ -261,15 +248,20 @@ object NotificationIconContainerViewBinder {
                }

            iconsDiff.removed
                .mapNotNull { key -> childrenByNotifKey[key] }
                .forEach { child -> view.removeView(child) }
                .mapNotNull { key -> childrenByNotifKey[key]?.let { key to it } }
                .forEach { (key, child) ->
                    view.removeView(child)
                    iconBindings.remove(key)?.cancel()
                }

            val toAdd = iconsDiff.added.map { viewStore.iconView(it.notifKey) }
            for ((i, sbiv) in toAdd.withIndex()) {
            val toAdd = iconsDiff.added.map { it.notifKey to viewStore.iconView(it.notifKey) }
            for ((i, keyAndView) in toAdd.withIndex()) {
                val (key, sbiv) = keyAndView
                // The view might still be transiently added if it was just removed
                // and added again
                view.removeTransientView(sbiv)
                view.addView(sbiv, i, layoutParams)
                iconBindings[key] = launch { bindIcon(key, sbiv) }
            }

            view.setChangingViewPositions(true)
@@ -292,16 +284,6 @@ object NotificationIconContainerViewBinder {

    // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this
    //  can be moved there and cleaned up.
    private fun ViewGroup.applyTint(
        iconColors: NotificationIconColors,
        contrastColorUtil: ContrastColorUtil,
    ) {
        children
            .filterIsInstance<StatusBarIconView>()
            .filter { it.width != 0 }
            .forEach { iv -> iv.updateTintForIcon(iconColors, contrastColorUtil) }
    }

    private fun StatusBarIconView.updateTintForIcon(
        iconColors: NotificationIconColors,
        contrastColorUtil: ContrastColorUtil,