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

Commit 645e9f44 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Include bundle icons in shelf" into main

parents e5c9b451 4cbf90ba
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -151,7 +151,7 @@ private fun ContentScope.BundleHeaderContent(
        )

        if (collapsed) {
            val currentPreviewIcons by
            val currentPreviewIcons: List<Drawable> by
                viewModel.previewIcons.collectAsStateWithLifecycle(initialValue = emptyList())
            if (currentPreviewIcons.isNotEmpty()) {
                BundlePreviewIcons(
+94 −58
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

package com.android.systemui.statusbar.notification.icon.domain.interactor

import android.content.applicationContext
import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -29,21 +30,15 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.promoted.domain.interactor.aodPromotedNotificationInteractor
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style.Base
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.byIsAmbient
import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply
import com.android.systemui.statusbar.notification.shared.byIsPromoted
import com.android.systemui.statusbar.notification.shared.byIsPulsing
import com.android.systemui.statusbar.notification.shared.byIsRowDismissed
import com.android.systemui.statusbar.notification.shared.byIsSilent
import com.android.systemui.statusbar.notification.shared.byIsSuppressedFromStatusBar
import com.android.systemui.statusbar.notification.shared.byKey
import com.android.systemui.statusbar.notification.shared.byAssociatedNotifModel
import com.android.systemui.statusbar.notification.shared.byIconIsAmbient
import com.android.systemui.statusbar.notification.shared.byIconNotifKey
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.eq
@@ -60,18 +55,23 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class NotificationIconsInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
    private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
    private val aodPromotedNotificationInteractor = kosmos.aodPromotedNotificationInteractor
    private val testScope
        get() = kosmos.testScope

    private val activeNotificationListRepository
        get() = kosmos.activeNotificationListRepository

    private val notificationsKeyguardInteractor
        get() = kosmos.notificationsKeyguardInteractor

    private val underTest =
        NotificationIconsInteractor(
            kosmos.activeNotificationsInteractor,
            kosmos.activeNotificationListRepository,
            kosmos.bubblesOptional,
            kosmos.headsUpNotificationIconInteractor,
            kosmos.aodPromotedNotificationInteractor,
            kosmos.notificationsKeyguardViewStateRepository,
            kosmos.applicationContext,
        )

    @Before
@@ -88,7 +88,9 @@ class NotificationIconsInteractorTest : SysuiTestCase() {
    fun filteredEntrySet() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet())
            assertThat(filteredSet).containsExactlyElementsIn(testIcons)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsExactlyElementsIn(testIcons)
        }

    @Test
@@ -96,31 +98,35 @@ class NotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            whenever(kosmos.bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
            val filteredSet by collectLastValue(underTest.filteredNotifSet())
            assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1")
            assertThat(filteredSet).comparingElementsUsing(byIconNotifKey).doesNotContain("notif1")
        }

    @Test
    fun filteredEntrySet_noAmbient() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet(showAmbient = false))
            assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true)
            assertThat(filteredSet).comparingElementsUsing(byIconIsAmbient).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byIsSuppressedFromStatusBar)
                .doesNotContain(true)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isSuppressedFromStatusBar })
        }

    @Test
    fun filteredEntrySet_noLowPriority() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet(showLowPriority = false))
            assertThat(filteredSet).comparingElementsUsing(byIsSilent).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isSilent })
        }

    @Test
    fun filteredEntrySet_noDismissed() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet(showDismissed = false))
            assertThat(filteredSet).comparingElementsUsing(byIsRowDismissed).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isRowDismissed })
        }

    @Test
@@ -129,8 +135,8 @@ class NotificationIconsInteractorTest : SysuiTestCase() {
            val filteredSet by
                collectLastValue(underTest.filteredNotifSet(showRepliedMessages = false))
            assertThat(filteredSet)
                .comparingElementsUsing(byIsLastMessageFromReply)
                .doesNotContain(true)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isLastMessageFromReply })
        }

    @Test
@@ -138,7 +144,9 @@ class NotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false))
            notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
            assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isPulsing })
        }

    @Test
@@ -146,14 +154,18 @@ class NotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false))
            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
            assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsAnyIn(testIcons.filter { it.isPulsing })
        }

    @Test
    fun filteredEntrySet_showAodPromoted() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet(showAodPromoted = true))
            assertThat(filteredSet).comparingElementsUsing(byIsPromoted).contains(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsAnyIn(testIcons.filter { it.promotedContent != null })
        }
    }

@@ -161,7 +173,9 @@ class NotificationIconsInteractorTest : SysuiTestCase() {
    fun filteredEntrySet_noAodPromoted() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.filteredNotifSet(showAodPromoted = false))
            assertThat(filteredSet).comparingElementsUsing(byIsPromoted).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.promotedContent != null })
        }
    }
}
@@ -170,9 +184,11 @@ class NotificationIconsInteractorTest : SysuiTestCase() {
@RunWith(AndroidJUnit4::class)
class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val testScope
        get() = kosmos.testScope

    private val underTest = kosmos.alwaysOnDisplayNotificationIconsInteractor
    private val underTest
        get() = kosmos.alwaysOnDisplayNotificationIconsInteractor

    @Before
    fun setup() {
@@ -187,24 +203,26 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            whenever(kosmos.bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
            val filteredSet by collectLastValue(underTest.aodNotifs)
            assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1")
            assertThat(filteredSet).comparingElementsUsing(byIconNotifKey).doesNotContain("notif1")
        }

    @Test
    fun filteredEntrySet_noAmbient() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.aodNotifs)
            assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true)
            assertThat(filteredSet).comparingElementsUsing(byIconIsAmbient).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byIsSuppressedFromStatusBar)
                .doesNotContain(true)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isSuppressedFromStatusBar })
        }

    @Test
    fun filteredEntrySet_noDismissed() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.aodNotifs)
            assertThat(filteredSet).comparingElementsUsing(byIsRowDismissed).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isRowDismissed })
        }

    @Test
@@ -212,8 +230,8 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.aodNotifs)
            assertThat(filteredSet)
                .comparingElementsUsing(byIsLastMessageFromReply)
                .doesNotContain(true)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isLastMessageFromReply })
        }

    @Test
@@ -222,7 +240,9 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
            val filteredSet by collectLastValue(underTest.aodNotifs)
            kosmos.fakeDeviceEntryBypassRepository.setBypassEnabled(false)
            kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
            assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsAnyIn(testIcons.filter { it.isPulsing })
        }

    @Test
@@ -231,7 +251,9 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
            val filteredSet by collectLastValue(underTest.aodNotifs)
            kosmos.fakeDeviceEntryBypassRepository.setBypassEnabled(false)
            kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
            assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsAnyIn(testIcons.filter { it.isPulsing })
        }

    @Test
@@ -240,7 +262,9 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
            val filteredSet by collectLastValue(underTest.aodNotifs)
            kosmos.fakeDeviceEntryBypassRepository.setBypassEnabled(true)
            kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
            assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isPulsing })
        }

    @Test
@@ -249,7 +273,9 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
            val filteredSet by collectLastValue(underTest.aodNotifs)
            kosmos.fakeDeviceEntryBypassRepository.setBypassEnabled(true)
            kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
            assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsAnyIn(testIcons.filter { it.isPulsing })
        }
}

@@ -280,17 +306,17 @@ class StatusBarNotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            whenever(kosmos.bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
            val filteredSet by collectLastValue(underTest.statusBarNotifs)
            assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1")
            assertThat(filteredSet).comparingElementsUsing(byIconNotifKey).doesNotContain("notif1")
        }

    @Test
    fun filteredEntrySet_noAmbient() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.statusBarNotifs)
            assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true)
            assertThat(filteredSet).comparingElementsUsing(byIconIsAmbient).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byIsSuppressedFromStatusBar)
                .doesNotContain(true)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isSuppressedFromStatusBar })
        }

    @Test
@@ -298,7 +324,9 @@ class StatusBarNotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.statusBarNotifs)
            kosmos.notificationListenerSettingsRepository.showSilentStatusIcons.value = false
            assertThat(filteredSet).comparingElementsUsing(byIsSilent).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isSilent })
        }

    @Test
@@ -306,14 +334,18 @@ class StatusBarNotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.statusBarNotifs)
            kosmos.notificationListenerSettingsRepository.showSilentStatusIcons.value = true
            assertThat(filteredSet).comparingElementsUsing(byIsSilent).contains(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsAnyIn(testIcons.filter { it.isSilent })
        }

    @Test
    fun filteredEntrySet_noDismissed() =
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.statusBarNotifs)
            assertThat(filteredSet).comparingElementsUsing(byIsRowDismissed).doesNotContain(true)
            assertThat(filteredSet)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isRowDismissed })
        }

    @Test
@@ -321,8 +353,8 @@ class StatusBarNotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.statusBarNotifs)
            assertThat(filteredSet)
                .comparingElementsUsing(byIsLastMessageFromReply)
                .doesNotContain(true)
                .comparingElementsUsing(byAssociatedNotifModel)
                .containsNoneIn(testIcons.filter { it.isLastMessageFromReply })
        }

    @Test
@@ -331,20 +363,24 @@ class StatusBarNotificationIconsInteractorTest : SysuiTestCase() {
        testScope.runTest {
            val filteredSet by collectLastValue(underTest.statusBarNotifs)
            kosmos.headsUpNotificationIconInteractor.setIsolatedIconNotificationKey("notif5")
            assertThat(filteredSet).comparingElementsUsing(byKey).contains("notif5")
            assertThat(filteredSet).comparingElementsUsing(byIconNotifKey).contains("notif5")
        }
}

private val testIcons =
    listOf(
        activeNotificationModel(key = "notif1"),
        activeNotificationModel(key = "notif2", isAmbient = true),
        activeNotificationModel(key = "notif3", isRowDismissed = true),
        activeNotificationModel(key = "notif4", isSilent = true),
        activeNotificationModel(key = "notif5", isLastMessageFromReply = true),
        activeNotificationModel(key = "notif6", isSuppressedFromStatusBar = true),
        activeNotificationModel(key = "notif7", isPulsing = true),
        activeNotificationModel(key = "notif8", promotedContent = promotedContent("notif8", Base)),
        activeNotificationModel(key = "notif1", groupKey = "g1"),
        activeNotificationModel(key = "notif2", groupKey = "g2", isAmbient = true),
        activeNotificationModel(key = "notif3", groupKey = "g3", isRowDismissed = true),
        activeNotificationModel(key = "notif4", groupKey = "g4", isSilent = true),
        activeNotificationModel(key = "notif5", groupKey = "g5", isLastMessageFromReply = true),
        activeNotificationModel(key = "notif6", groupKey = "g6", isSuppressedFromStatusBar = true),
        activeNotificationModel(key = "notif7", groupKey = "g7", isPulsing = true),
        activeNotificationModel(
            key = "notif8",
            groupKey = "g8",
            promotedContent = promotedContent("notif8", Base),
        ),
    )

private fun promotedContent(
+19 −20
Original line number Diff line number Diff line
@@ -15,30 +15,29 @@

package com.android.systemui.statusbar.notification.shared

import com.android.systemui.statusbar.notification.icon.domain.interactor.ActiveNotificationIconModel
import com.google.common.truth.Correspondence

val byKey: Correspondence<ActiveNotificationModel, String> =
    Correspondence.transforming({ it.key }, "has a key of")
val byIsAmbient: Correspondence<ActiveNotificationModel, Boolean> =
val byIconIsAmbient: Correspondence<ActiveNotificationIconModel, Boolean> =
    Correspondence.transforming({ it.isAmbient }, "has an isAmbient value of")
val byIsSuppressedFromStatusBar: Correspondence<ActiveNotificationModel, Boolean> =
val byAssociatedNotifModel: Correspondence<ActiveNotificationIconModel, ActiveNotificationModel> =
    Correspondence.transforming(
        { it.isSuppressedFromStatusBar },
        "has an isSuppressedFromStatusBar value of",
        /* actualTransform = */ { it },
        /* expectedTransform = */ { expected ->
            checkNotNull(expected)
            ActiveNotificationIconModel(
                expected.key,
                expected.groupKey!!,
                expected.shelfIcon,
                expected.statusBarIcon,
                expected.aodIcon,
                expected.isAmbient,
            )
val byIsSilent: Correspondence<ActiveNotificationModel, Boolean> =
    Correspondence.transforming({ it.isSilent }, "has an isSilent value of")
val byIsRowDismissed: Correspondence<ActiveNotificationModel, Boolean> =
    Correspondence.transforming({ it.isRowDismissed }, "has an isRowDismissed value of")
val byIsLastMessageFromReply: Correspondence<ActiveNotificationModel, Boolean> =
    Correspondence.transforming(
        { it.isLastMessageFromReply },
        "has an isLastMessageFromReply value of",
    )
val byIsPulsing: Correspondence<ActiveNotificationModel, Boolean> =
    Correspondence.transforming({ it.isPulsing }, "has an isPulsing value of")
val byIsPromoted: Correspondence<ActiveNotificationModel, Boolean> =
    Correspondence.transforming(
        { it.promotedContent != null },
        "has (or doesn't have) a promoted content model",
        },
        /* description = */ "is icon model of",
    )

val byIconNotifKey: Correspondence<ActiveNotificationIconModel, String> =
    Correspondence.transforming({ it.notifKey }, "has a notifKey of")
+20 −9
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.Context
import android.graphics.drawable.Icon
import android.service.notification.StatusBarNotification
import android.util.ArrayMap
import androidx.annotation.DrawableRes
import com.android.app.tracing.traceSection
import com.android.internal.logging.InstanceId
import com.android.systemui.dagger.qualifiers.Main
@@ -116,7 +117,11 @@ private class ActiveNotificationsStoreBuilder(
    private fun addBundleEntry(entry: BundleEntry) {
        val childModels = entry.children.mapNotNull { it.toModel() }
        builder.addBundle(
            existingModels.createOrReuseBundle(key = entry.key, children = childModels)
            existingModels.createOrReuseBundle(
                key = entry.key,
                iconResId = entry.bundleRepository.bundleIcon,
                children = childModels,
            )
        )
    }

@@ -132,19 +137,21 @@ private class ActiveNotificationsStoreBuilder(
        builder.setRankingsMap(flatMapToRankingsMap(entries))
    }

    fun flatMapToRankingsMap(entries: List<PipelineEntry>): Map<String, Int> {
    private fun flatMapToRankingsMap(entries: List<PipelineEntry>): Map<String, Int> {
        val result = ArrayMap<String, Int>()
        for (entry in entries) {
            when (entry) {
                is BundleEntry -> {
                    // TODO(b/410815667): Handle BundleEntry
                is BundleEntry -> flatMapToRankingsMap(entry, result)
                is ListEntry -> flatMapToRankingsMap(entry, result)
            }
                is ListEntry -> {
                    flatMapToRankingsMap(entry, result)
        }
        return result
    }

    private fun flatMapToRankingsMap(entry: BundleEntry, result: ArrayMap<String, Int>) {
        for (child in entry.children) {
            flatMapToRankingsMap(child, result)
        }
        return result
    }

    private fun flatMapToRankingsMap(entry: ListEntry, result: ArrayMap<String, Int>) {
@@ -396,17 +403,21 @@ private fun StatusBarNotification.toCallType(): CallType =

private fun ActiveNotificationsStore.createOrReuseBundle(
    key: String,
    @DrawableRes iconResId: Int,
    children: List<ActiveNotificationEntryModel>,
): ActiveBundleModel {
    return bundles[key]?.takeIf { it.isCurrent(key, children) } ?: ActiveBundleModel(key, children)
    return bundles[key]?.takeIf { it.isCurrent(key, iconResId, children) }
        ?: ActiveBundleModel(key, iconResId, children)
}

private fun ActiveBundleModel.isCurrent(
    key: String,
    @DrawableRes iconResId: Int,
    children: List<ActiveNotificationEntryModel>,
): Boolean {
    return when {
        key != this.key -> false
        iconResId != this.iconResId -> false
        !hasSameInstances(children, this.children) -> false
        else -> true
    }
+91 −22

File changed.

Preview size limit exceeded, changes collapsed.

Loading