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

Commit 514bbc88 authored by Steve Elliott's avatar Steve Elliott Committed by Android (Google) Code Review
Browse files

Merge "Extract maximum amount + speedbump index from NIC" into main

parents b297bc8e 38ebf0e3
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -604,7 +604,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
                    mAodIconsBindHandle.dispose();
                }
                if (nic != null) {
                    nic.setOnLockScreen(true);
                    final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind(
                            nic,
                            mAodIconsViewModel,
+0 −1
Original line number Diff line number Diff line
@@ -87,7 +87,6 @@ constructor(
        }

        if (NotificationIconContainerRefactor.isEnabled) {
            nic.setOnLockScreen(true)
            nicBindingDisposable?.dispose()
            nicBindingDisposable =
                NotificationIconContainerViewBinder.bind(
+30 −3
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ public class NotificationShelf extends ActivatableNotificationView {
    private int mIndexOfFirstViewInShelf = -1;
    private float mCornerAnimationDistance;
    private float mActualWidth = -1;
    private int mMaxIconsOnLockscreen;
    private final RefactorFlag mSensitiveRevealAnim =
            RefactorFlag.forView(Flags.SENSITIVE_REVEAL_ANIM);
    private boolean mCanModifyColorOfNotifications;
@@ -136,6 +137,7 @@ public class NotificationShelf extends ActivatableNotificationView {
        Resources res = getResources();
        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
        mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
        mMaxIconsOnLockscreen = res.getInteger(R.integer.max_notif_icons_on_lockscreen);

        ViewGroup.LayoutParams layoutParams = getLayoutParams();
        final int newShelfHeight = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -271,6 +273,7 @@ public class NotificationShelf extends ActivatableNotificationView {
    }

    private int getSpeedBumpIndex() {
        NotificationIconContainerRefactor.assertInLegacyMode();
        return mHostLayout.getSpeedBumpIndex();
    }

@@ -280,6 +283,7 @@ public class NotificationShelf extends ActivatableNotificationView {
     */
    @VisibleForTesting
    public void updateActualWidth(float fractionToShade, float shortestWidth) {
        NotificationIconContainerRefactor.assertInLegacyMode();
        final float actualWidth = mAmbientState.isOnKeyguard()
                ? MathUtils.lerp(shortestWidth, getWidth(), fractionToShade)
                : getWidth();
@@ -290,6 +294,15 @@ public class NotificationShelf extends ActivatableNotificationView {
        mActualWidth = actualWidth;
    }

    private void setActualWidth(float actualWidth) {
        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
        setBackgroundWidth((int) actualWidth);
        if (mShelfIcons != null) {
            mShelfIcons.setActualLayoutWidth((int) actualWidth);
        }
        mActualWidth = actualWidth;
    }

    @Override
    public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
        super.getBoundsOnScreen(outRect, clipToParent);
@@ -467,12 +480,26 @@ public class NotificationShelf extends ActivatableNotificationView {

        final float fractionToShade = Interpolators.STANDARD.getInterpolation(
                mAmbientState.getFractionToShade());

        if (NotificationIconContainerRefactor.isEnabled()) {
            if (mAmbientState.isOnKeyguard()) {
                float numViews = MathUtils.min(numViewsInShelf, mMaxIconsOnLockscreen + 1);
                float shortestWidth = mShelfIcons.calculateWidthFor(numViews);
                float actualWidth = MathUtils.lerp(shortestWidth, getWidth(), fractionToShade);
                setActualWidth(actualWidth);
            } else {
                setActualWidth(getWidth());
            }
        } else {
            final float shortestWidth = mShelfIcons.calculateWidthFor(numViewsInShelf);
            updateActualWidth(fractionToShade, shortestWidth);
        }

        // TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
        setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
        if (!NotificationIconContainerRefactor.isEnabled()) {
            mShelfIcons.setSpeedBumpIndex(getSpeedBumpIndex());
        }
        mShelfIcons.calculateIconXTranslations();
        mShelfIcons.applyIconStates();
        for (int i = 0; i < getHostLayoutChildCount(); i++) {
+95 −56
Original line number Diff line number Diff line
@@ -25,9 +25,7 @@ import androidx.lifecycle.lifecycleScope
import com.android.internal.policy.SystemBarUtils
import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.CoreStartable
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
@@ -39,21 +37,15 @@ import com.android.systemui.statusbar.notification.icon.ui.viewmodel.Notificatio
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.util.asIndenting
import com.android.systemui.util.kotlin.mapValuesNotNullTo
import com.android.systemui.util.kotlin.stateFlow
import com.android.systemui.util.printCollection
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import dagger.Binds
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
@@ -134,6 +126,7 @@ object NotificationIconContainerViewBinder {
    ): DisposableHandle {
        return view.repeatWhenAttached {
            lifecycleScope.launch {
                view.setUseIncreasedIconScale(true)
                launch {
                    viewModel.icons.bindIcons(
                        view,
@@ -229,7 +222,6 @@ object NotificationIconContainerViewBinder {
                ->
                FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
            }

        val failedBindings = mutableSetOf<String>()
        val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>()
        var prevIcons = NotificationIconsViewData()
@@ -237,12 +229,13 @@ object NotificationIconContainerViewBinder {
            val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
            prevIcons = iconsData

            // Lookup 1:1 group icon replacements
            val replacingIcons: ArrayMap<String, StatusBarIcon> =
                iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, info) ->
                    boundViewsByNotifKey[info.notifKey]?.first?.statusBarIcon
                iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, notifKey) ->
                    boundViewsByNotifKey[notifKey]?.first?.statusBarIcon
                }
            view.setReplacingIcons(replacingIcons)

            view.withIconReplacements(replacingIcons) {
                // Remove and unbind.
                for (notifKey in iconsDiff.removed) {
                    failedBindings.remove(notifKey)
                    val (child, job) = boundViewsByNotifKey.remove(notifKey) ?: continue
@@ -250,15 +243,17 @@ object NotificationIconContainerViewBinder {
                    job.cancel()
                }

            val toAdd: Sequence<String> =
                iconsDiff.added.asSequence().map { it.notifKey } + failedBindings
                // Add and bind.
                val toAdd: Sequence<String> = iconsDiff.added.asSequence() + failedBindings
                for ((idx, notifKey) in toAdd.withIndex()) {
                    // Lookup the StatusBarIconView from the store.
                    val sbiv = viewStore.iconView(notifKey)
                    if (sbiv == null) {
                        failedBindings.add(notifKey)
                        continue
                    }
                // The view might still be transiently added if it was just removed and added again
                    // The view might still be transiently added if it was just removed and added
                    // again
                    view.removeTransientView(sbiv)
                    view.addView(sbiv, idx)
                    boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
@@ -272,13 +267,31 @@ object NotificationIconContainerViewBinder {
                        )
                }

            notifyBindingFailures(failedBindings)
                // Set the maximum number of icons to show in the container. Any icons over this
                // amount will render as an "overflow dot".
                val maxIconsAmount: Int =
                    when (iconsData.limitType) {
                        LimitType.MaximumIndex -> {
                            iconsData.visibleIcons
                                .asSequence()
                                .take(iconsData.iconLimit)
                                .count { info -> info.notifKey in boundViewsByNotifKey }
                        }
                        LimitType.MaximumAmount -> {
                            iconsData.iconLimit
                        }
                    }
                view.setMaxIconsAmount(maxIconsAmount)

            view.setChangingViewPositions(true)
                // Track the binding failures so that they appear in dumpsys.
                notifyBindingFailures(failedBindings)

                // Re-sort notification icons
            val expectedChildren =
                iconsData.visibleKeys.mapNotNull { boundViewsByNotifKey[it.notifKey]?.first }
                view.changeViewPositions {
                    val expectedChildren: List<StatusBarIconView> =
                        iconsData.visibleIcons.mapNotNull {
                            boundViewsByNotifKey[it.notifKey]?.first
                        }
                    val childCount = view.childCount
                    for (i in 0 until childCount) {
                        val actual = view.getChildAt(i)
@@ -289,10 +302,36 @@ object NotificationIconContainerViewBinder {
                        view.removeView(expected)
                        view.addView(expected, i)
                    }
            view.setChangingViewPositions(false)
                }
            }
            // Recalculate all icon positions, to reflect our updates.
            view.calculateIconXTranslations()
        }
    }

            view.setReplacingIcons(null)
    /**
     * Track which groups are being replaced with a different icon instance, but with the same
     * visual icon. This prevents a weird animation where it looks like an icon disappears and
     * reappears unchanged.
     */
    // TODO(b/305739416): Ideally we wouldn't swap out the StatusBarIconView at all, and instead use
    //  a single SBIV instance for the group. Then this whole concept can go away.
    private inline fun <R> NotificationIconContainer.withIconReplacements(
        replacements: ArrayMap<String, StatusBarIcon>,
        block: () -> R
    ): R {
        setReplacingIcons(replacements)
        return block().also { setReplacingIcons(null) }
    }

    /**
     * Any invocations of [NotificationIconContainer.addView] /
     * [NotificationIconContainer.removeView] inside of [block] will not cause a new add / remove
     * animation.
     */
    private inline fun <R> NotificationIconContainer.changeViewPositions(block: () -> R): R {
        setChangingViewPositions(true)
        return block().also { setChangingViewPositions(false) }
    }

    /** External storage for [StatusBarIconView] instances. */
+8 −1
Original line number Diff line number Diff line
@@ -15,10 +15,13 @@
 */
package com.android.systemui.statusbar.notification.icon.ui.viewmodel

import android.content.res.Resources
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
import javax.inject.Inject
@@ -35,9 +38,12 @@ constructor(
    iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor,
    keyguardInteractor: KeyguardInteractor,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    @Main resources: Resources,
    shadeInteractor: ShadeInteractor,
) {

    private val maxIcons = resources.getInteger(R.integer.max_notif_icons_on_aod)

    /** Are changes to the icon container animated? */
    val areContainerChangesAnimated: Flow<Boolean> =
        combine(
@@ -67,7 +73,8 @@ constructor(
    val icons: Flow<NotificationIconsViewData> =
        iconsInteractor.aodNotifs.map { entries ->
            NotificationIconsViewData(
                visibleKeys = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
                visibleIcons = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
                iconLimit = maxIcons,
            )
        }
}
Loading