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

Commit 38ebf0e3 authored by Steve Elliott's avatar Steve Elliott
Browse files

Extract maximum amount + speedbump index from NIC

Flag: ACONFIG com.android.systemui.notifications_icon_container_refactor DEVELOPMENT
Fixes: 309555139
Bug: 278765923
Test: manual
  1. Verify AOD icons and overflow dot render correctly.
    a. On AOD
    b. On bypass lock screen
  2. Verify Status Bar icons and overflow dot render correctly.
  3. Verify notification shelf icons and overflow dot render correctly.
    a. In expanded shade
    b. On lock screen
Change-Id: I5a5b335708461a3921e2209c1886e0ce4d79e74b
parent 0234597b
Loading
Loading
Loading
Loading
+0 −1
Original line number Original line Diff line number Diff line
@@ -603,7 +603,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
                    mAodIconsBindHandle.dispose();
                    mAodIconsBindHandle.dispose();
                }
                }
                if (nic != null) {
                if (nic != null) {
                    nic.setOnLockScreen(true);
                    final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind(
                    final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind(
                            nic,
                            nic,
                            mAodIconsViewModel,
                            mAodIconsViewModel,
+0 −1
Original line number Original line Diff line number Diff line
@@ -86,7 +86,6 @@ constructor(
        }
        }


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


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


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


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


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

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


        final float fractionToShade = Interpolators.STANDARD.getInterpolation(
        final float fractionToShade = Interpolators.STANDARD.getInterpolation(
                mAmbientState.getFractionToShade());
                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);
            final float shortestWidth = mShelfIcons.calculateWidthFor(numViewsInShelf);
            updateActualWidth(fractionToShade, shortestWidth);
            updateActualWidth(fractionToShade, shortestWidth);
        }


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

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


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

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


            val toAdd: Sequence<String> =
                // Add and bind.
                iconsDiff.added.asSequence().map { it.notifKey } + failedBindings
                val toAdd: Sequence<String> = iconsDiff.added.asSequence() + failedBindings
                for ((idx, notifKey) in toAdd.withIndex()) {
                for ((idx, notifKey) in toAdd.withIndex()) {
                    // Lookup the StatusBarIconView from the store.
                    val sbiv = viewStore.iconView(notifKey)
                    val sbiv = viewStore.iconView(notifKey)
                    if (sbiv == null) {
                    if (sbiv == null) {
                        failedBindings.add(notifKey)
                        failedBindings.add(notifKey)
                        continue
                        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.removeTransientView(sbiv)
                    view.addView(sbiv, idx)
                    view.addView(sbiv, idx)
                    boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
                    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
                // Re-sort notification icons
            val expectedChildren =
                view.changeViewPositions {
                iconsData.visibleKeys.mapNotNull { boundViewsByNotifKey[it.notifKey]?.first }
                    val expectedChildren: List<StatusBarIconView> =
                        iconsData.visibleIcons.mapNotNull {
                            boundViewsByNotifKey[it.notifKey]?.first
                        }
                    val childCount = view.childCount
                    val childCount = view.childCount
                    for (i in 0 until childCount) {
                    for (i in 0 until childCount) {
                        val actual = view.getChildAt(i)
                        val actual = view.getChildAt(i)
@@ -289,10 +302,36 @@ object NotificationIconContainerViewBinder {
                        view.removeView(expected)
                        view.removeView(expected)
                        view.addView(expected, i)
                        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. */
    /** External storage for [StatusBarIconView] instances. */
+8 −1
Original line number Original line Diff line number Diff line
@@ -15,10 +15,13 @@
 */
 */
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
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.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
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.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
import javax.inject.Inject
import javax.inject.Inject
@@ -35,9 +38,12 @@ constructor(
    iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor,
    iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor,
    keyguardInteractor: KeyguardInteractor,
    keyguardInteractor: KeyguardInteractor,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    @Main resources: Resources,
    shadeInteractor: ShadeInteractor,
    shadeInteractor: ShadeInteractor,
) {
) {


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

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