Merge changes from topic "caitlinshk-callchipflag-removal" into main
* changes:
[SB] Remove status_bar_call_chip_notification_icon flag.
[SB] Remove status_bar_use_repos_for_call_chip flag.
[SB][Notif] Only hide text in chip that was actually tapped.
[SB][Notif] Re-tapping notif chip hides the HUN.
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 7d5fd90..1d49e50 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -424,24 +424,6 @@
}
flag {
- name: "status_bar_use_repos_for_call_chip"
- namespace: "systemui"
- description: "Use repositories as the source of truth for call notifications shown as a chip in"
- "the status bar"
- bug: "328584859"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "status_bar_call_chip_notification_icon"
- namespace: "systemui"
- description: "Use the small icon set on the notification for the status bar call chip"
- bug: "354930838"
-}
-
-flag {
name: "status_bar_signal_policy_refactor"
namespace: "systemui"
description: "Use a settings observer for airplane mode and make StatusBarSignalPolicy startable"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 0061c41..75d000b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -22,7 +22,6 @@
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -126,65 +125,8 @@
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun chip_positiveStartTime_notifIconFlagOff_iconIsPhone() =
- testScope.runTest {
- val latest by collectLastValue(underTest.chip)
-
- repo.setOngoingCallState(
- inCallModel(startTimeMs = 1000, notificationIcon = createStatusBarIconViewOrNull())
- )
-
- assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
- val icon =
- (((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
- .impl as Icon.Resource
- assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
- assertThat(icon.contentDescription).isNotNull()
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
- fun chip_positiveStartTime_notifIconFlagOn_iconIsNotifIcon() =
- testScope.runTest {
- val latest by collectLastValue(underTest.chip)
-
- val notifIcon = createStatusBarIconViewOrNull()
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = notifIcon))
-
- assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isInstanceOf(OngoingActivityChipModel.ChipIcon.StatusBarView::class.java)
- val actualIcon =
- (((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.StatusBarView)
- .impl
- assertThat(actualIcon).isEqualTo(notifIcon)
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
- fun chip_positiveStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() =
- testScope.runTest {
- val latest by collectLastValue(underTest.chip)
-
- repo.setOngoingCallState(
- inCallModel(
- startTimeMs = 1000,
- notificationIcon = createStatusBarIconViewOrNull(),
- notificationKey = "notifKey",
- )
- )
-
- assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey"))
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
- fun chip_positiveStartTime_notifIconAndConnectedDisplaysFlagOn_iconIsNotifIcon() =
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_positiveStartTime_connectedDisplaysFlagOn_iconIsNotifIcon() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -205,29 +147,8 @@
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun chip_zeroStartTime_notifIconFlagOff_iconIsPhone() =
- testScope.runTest {
- val latest by collectLastValue(underTest.chip)
-
- repo.setOngoingCallState(
- inCallModel(startTimeMs = 0, notificationIcon = createStatusBarIconViewOrNull())
- )
-
- assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
- val icon =
- (((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
- .impl as Icon.Resource
- assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
- assertThat(icon.contentDescription).isNotNull()
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
- fun chip_zeroStartTime_notifIconFlagOn_cdFlagOff_iconIsNotifIcon() =
+ fun chip_zeroStartTime_cdFlagOff_iconIsNotifIcon() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -244,8 +165,8 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
- fun chip_zeroStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() =
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_zeroStartTime_cdFlagOn_iconIsNotifKeyIcon() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -262,7 +183,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOff_iconIsPhone() =
testScope.runTest {
@@ -281,7 +201,7 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_notifIconFlagOn_butNullNotifIcon_iconNotifKey() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index e561e3e..902db5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -501,7 +501,7 @@
@Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
- fun chips_hasHeadsUpByUser_onlyShowsIcon() =
+ fun chips_hasHeadsUpBySystem_showsTime() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -523,7 +523,99 @@
)
)
- // WHEN there's a HUN pinned by a user
+ // WHEN there's a HUN pinned by the system
+ kosmos.headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "notif",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
+ )
+ )
+
+ // THEN the chip keeps showing time
+ // (In real life the chip won't show at all, but that's handled in a different part of
+ // the system. What we know here is that the chip shouldn't shrink to icon only.)
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_hasHeadsUpByUser_forOtherNotif_showsTime() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ val otherPromotedContentBuilder =
+ PromotedNotificationContentModel.Builder("other notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 654321L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ val icon = createStatusBarIconViewOrNull()
+ val otherIcon = createStatusBarIconViewOrNull()
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = icon,
+ promotedContent = promotedContentBuilder.build(),
+ ),
+ activeNotificationModel(
+ key = "other notif",
+ statusBarChipIcon = otherIcon,
+ promotedContent = otherPromotedContentBuilder.build(),
+ ),
+ )
+ )
+
+ // WHEN there's a HUN pinned for the "other notif" chip
+ kosmos.headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "other notif",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ // THEN the "notif" chip keeps showing time
+ val chip = latest!![0]
+ assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertIsNotifChip(chip, icon, "notif")
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_hasHeadsUpByUser_forThisNotif_onlyShowsIcon() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ // WHEN this notification is pinned by the user
kosmos.headsUpNotificationRepository.setNotifications(
UnconfinedFakeHeadsUpRowRepository(
key = "notif",
@@ -531,6 +623,7 @@
)
)
+ // THEN the chip shrinks to icon only
assertThat(latest!![0])
.isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index a3ffd91..609885d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -458,7 +458,7 @@
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun showPromotedNotification_hasNotifEntry_shownAsHUN() =
+ fun onPromotedNotificationChipTapped_hasNotifEntry_shownAsHUN() =
testScope.runTest {
whenever(notifCollection.getEntry(entry.key)).thenReturn(entry)
@@ -473,7 +473,7 @@
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun showPromotedNotification_noNotifEntry_noHUN() =
+ fun onPromotedNotificationChipTapped_noNotifEntry_noHUN() =
testScope.runTest {
whenever(notifCollection.getEntry(entry.key)).thenReturn(null)
@@ -488,7 +488,7 @@
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun showPromotedNotification_shownAsHUNEvenIfEntryShouldNot() =
+ fun onPromotedNotificationChipTapped_shownAsHUNEvenIfEntryShouldNot() =
testScope.runTest {
whenever(notifCollection.getEntry(entry.key)).thenReturn(entry)
@@ -511,7 +511,7 @@
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun showPromotedNotification_atSameTimeAsOnAdded_promotedShownAsHUN() =
+ fun onPromotedNotificationChipTapped_atSameTimeAsOnAdded_promotedShownAsHUN() =
testScope.runTest {
// First, the promoted notification appears as not heads up
val promotedEntry = NotificationEntryBuilder().setPkg("promotedPackage").build()
@@ -548,6 +548,33 @@
}
@Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun onPromotedNotificationChipTapped_chipTappedTwice_hunHiddenOnSecondTap() =
+ testScope.runTest {
+ whenever(notifCollection.getEntry(entry.key)).thenReturn(entry)
+
+ // WHEN chip tapped first
+ statusBarNotificationChipsInteractor.onPromotedNotificationChipTapped(entry.key)
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+
+ // THEN HUN is shown
+ finishBind(entry)
+ verify(headsUpManager).showNotification(entry, isPinnedByUser = true)
+ addHUN(entry)
+
+ // WHEN chip is tapped again
+ statusBarNotificationChipsInteractor.onPromotedNotificationChipTapped(entry.key)
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+
+ // THEN HUN is hidden
+ verify(headsUpManager).removeNotification(eq(entry.key), eq(false), any())
+ }
+
+ @Test
fun testTransferIsolatedChildAlert_withGroupAlertSummary() {
setShouldHeadsUp(groupSummary)
whenever(notifPipeline.allNotifs).thenReturn(listOf(groupSummary, groupSibling1))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
index 22ef408..fae7d51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.notification.domain.model.TopPinnedState
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
@@ -412,46 +413,53 @@
@Test
fun statusBarHeadsUpState_pinnedBySystem() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
headsUpRepository.setNotifications(
FakeHeadsUpRowRepository(key = "key 0", pinnedStatus = PinnedStatus.PinnedBySystem)
)
runCurrent()
- assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.PinnedBySystem)
+ assertThat(state).isEqualTo(TopPinnedState.Pinned("key 0", PinnedStatus.PinnedBySystem))
+ assertThat(status).isEqualTo(PinnedStatus.PinnedBySystem)
}
@Test
fun statusBarHeadsUpState_pinnedByUser() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
headsUpRepository.setNotifications(
FakeHeadsUpRowRepository(key = "key 0", pinnedStatus = PinnedStatus.PinnedByUser)
)
runCurrent()
- assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.PinnedByUser)
+ assertThat(state).isEqualTo(TopPinnedState.Pinned("key 0", PinnedStatus.PinnedByUser))
+ assertThat(status).isEqualTo(PinnedStatus.PinnedByUser)
}
@Test
fun statusBarHeadsUpState_withoutPinnedNotifications_notPinned() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
headsUpRepository.setNotifications(
FakeHeadsUpRowRepository(key = "key 0", PinnedStatus.NotPinned)
)
runCurrent()
- assertThat(statusBarHeadsUpState).isEqualTo(PinnedStatus.NotPinned)
+ assertThat(state).isEqualTo(TopPinnedState.NothingPinned)
+ assertThat(status).isEqualTo(PinnedStatus.NotPinned)
}
@Test
fun statusBarHeadsUpState_whenShadeExpanded_false() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
@@ -463,13 +471,15 @@
// should emit `false`.
kosmos.fakeShadeRepository.setLegacyShadeExpansion(1.0f)
- assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
+ assertThat(state).isEqualTo(TopPinnedState.NothingPinned)
+ assertThat(status!!.isPinned).isFalse()
}
@Test
fun statusBarHeadsUpState_notificationsAreHidden_false() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
@@ -477,13 +487,15 @@
// AND the notifications are hidden
keyguardViewStateRepository.areNotificationsFullyHidden.value = true
- assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
+ assertThat(state).isEqualTo(TopPinnedState.NothingPinned)
+ assertThat(status!!.isPinned).isFalse()
}
@Test
fun statusBarHeadsUpState_onLockScreen_false() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
@@ -494,13 +506,15 @@
testSetup = true,
)
- assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
+ assertThat(state).isEqualTo(TopPinnedState.NothingPinned)
+ assertThat(status!!.isPinned).isFalse()
}
@Test
fun statusBarHeadsUpState_onByPassLockScreen_true() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
// WHEN a row is pinned
headsUpRepository.setNotifications(fakeHeadsUpRowRepository("key 0", isPinned = true))
@@ -513,13 +527,15 @@
// AND bypass is enabled
faceAuthRepository.isBypassEnabled.value = true
- assertThat(statusBarHeadsUpState!!.isPinned).isTrue()
+ assertThat(state).isInstanceOf(TopPinnedState.Pinned::class.java)
+ assertThat(status!!.isPinned).isTrue()
}
@Test
fun statusBarHeadsUpState_onByPassLockScreen_withoutNotifications_false() =
testScope.runTest {
- val statusBarHeadsUpState by collectLastValue(underTest.statusBarHeadsUpState)
+ val state by collectLastValue(underTest.statusBarHeadsUpState)
+ val status by collectLastValue(underTest.statusBarHeadsUpStatus)
// WHEN no pinned rows
// AND the lock screen is shown
@@ -530,7 +546,8 @@
// AND bypass is enabled
faceAuthRepository.isBypassEnabled.value = true
- assertThat(statusBarHeadsUpState!!.isPinned).isFalse()
+ assertThat(state).isEqualTo(TopPinnedState.NothingPinned)
+ assertThat(status!!.isPinned).isFalse()
}
private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean = false) =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
similarity index 93%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index cf512cd..b984099 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -28,9 +28,7 @@
import android.widget.LinearLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
-import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.dump.DumpManager
@@ -42,7 +40,6 @@
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
@@ -76,9 +73,8 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP)
@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
-class OngoingCallControllerViaRepoTest : SysuiTestCase() {
+class OngoingCallControllerTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val clock = kosmos.fakeSystemClock
@@ -114,7 +110,6 @@
testScope.backgroundScope,
context,
ongoingCallRepository,
- mock<CommonNotifCollection>(),
kosmos.activeNotificationsInteractor,
clock,
mockActivityStarter,
@@ -162,28 +157,7 @@
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun interactorHasOngoingCallNotif_notifIconFlagOff_repoHasNoNotifIcon() =
- testScope.runTest {
- val icon = mock<StatusBarIconView>()
- setNotifOnRepo(
- activeNotificationModel(
- key = "ongoingNotif",
- callType = CallType.Ongoing,
- uid = CALL_UID,
- statusBarChipIcon = icon,
- whenTime = 567,
- )
- )
-
- val repoState = ongoingCallRepository.ongoingCallState.value
- assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
- assertThat((repoState as OngoingCallModel.InCall).notificationIconView).isNull()
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun interactorHasOngoingCallNotif_notifIconFlagOn_repoHasNotifIcon() =
+ fun interactorHasOngoingCallNotif_repoHasNotifIcon() =
testScope.runTest {
val icon = mock<StatusBarIconView>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
deleted file mode 100644
index cd3539d..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * Copyright (C) 2021 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.phone.ongoingcall
-
-import android.app.ActivityManager
-import android.app.IActivityManager
-import android.app.IUidObserver
-import android.app.Notification
-import android.app.PendingIntent
-import android.app.Person
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
-import android.testing.TestableLooper
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.LinearLayout
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
-import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
-import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
-import com.android.systemui.statusbar.window.StatusBarWindowController
-import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.nullable
-import org.mockito.Mock
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import org.mockito.kotlin.whenever
-
-private const val CALL_UID = 900
-
-// A process state that represents the process being visible to the user.
-private const val PROC_STATE_VISIBLE = ActivityManager.PROCESS_STATE_TOP
-
-// A process state that represents the process being invisible to the user.
-private const val PROC_STATE_INVISIBLE = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-@OptIn(ExperimentalCoroutinesApi::class)
-@DisableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP, StatusBarChipsModernization.FLAG_NAME)
-class OngoingCallControllerViaListenerTest : SysuiTestCase() {
- private val kosmos = Kosmos()
-
- private val clock = FakeSystemClock()
- private val mainExecutor = FakeExecutor(clock)
- private val testScope = TestScope()
- private val statusBarModeRepository = FakeStatusBarModeRepository()
- private val ongoingCallRepository = kosmos.ongoingCallRepository
-
- private lateinit var controller: OngoingCallController
- private lateinit var notifCollectionListener: NotifCollectionListener
-
- @Mock
- private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler
- @Mock private lateinit var mockOngoingCallListener: OngoingCallListener
- @Mock private lateinit var mockActivityStarter: ActivityStarter
- @Mock private lateinit var mockIActivityManager: IActivityManager
- @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
- @Mock private lateinit var mockStatusBarWindowControllerStore: StatusBarWindowControllerStore
-
- private lateinit var chipView: View
-
- @Before
- fun setUp() {
- allowTestableLooperAsMainThread()
- TestableLooper.get(this).runWithLooper {
- chipView =
- LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip_primary, null)
- }
-
- MockitoAnnotations.initMocks(this)
- val notificationCollection = mock(CommonNotifCollection::class.java)
- whenever(mockStatusBarWindowControllerStore.defaultDisplay)
- .thenReturn(mockStatusBarWindowController)
-
- controller =
- OngoingCallController(
- testScope.backgroundScope,
- context,
- ongoingCallRepository,
- notificationCollection,
- kosmos.activeNotificationsInteractor,
- clock,
- mockActivityStarter,
- mainExecutor,
- mockIActivityManager,
- DumpManager(),
- mockStatusBarWindowControllerStore,
- mockSwipeStatusBarAwayGestureHandler,
- statusBarModeRepository,
- logcatLogBuffer("OngoingCallControllerViaListenerTest"),
- )
- controller.start()
- controller.addCallback(mockOngoingCallListener)
- controller.setChipView(chipView)
- onTeardown { controller.tearDownChipView() }
-
- val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
- verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture())
- notifCollectionListener = collectionListenerCaptor.value!!
-
- `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
- }
-
- @Test
- fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() {
- val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
- notification.modifyNotification(context).setWhen(567)
- notifCollectionListener.onEntryUpdated(notification.build())
-
- verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- val repoState = ongoingCallRepository.ongoingCallState.value
- assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
- assertThat((repoState as OngoingCallModel.InCall).startTimeMs).isEqualTo(567)
- }
-
- @Test
- fun onEntryUpdated_isOngoingCallNotif_windowControllerUpdated() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- verify(mockStatusBarWindowController).setOngoingProcessRequiresStatusBarVisible(true)
- }
-
- @Test
- fun onEntryUpdated_notOngoingCallNotif_listenerAndRepoNotNotified() {
- notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
-
- verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
- assertThat(ongoingCallRepository.ongoingCallState.value)
- .isInstanceOf(OngoingCallModel.NoCall::class.java)
- }
-
- @Test
- fun onEntryUpdated_ongoingCallNotifThenScreeningCallNotif_listenerNotifiedTwice() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
-
- verify(mockOngoingCallListener, times(2)).onOngoingCallStateChanged(anyBoolean())
- }
-
- @Test
- fun onEntryUpdated_ongoingCallNotifThenScreeningCallNotif_repoUpdated() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- assertThat(ongoingCallRepository.ongoingCallState.value)
- .isInstanceOf(OngoingCallModel.InCall::class.java)
-
- notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
-
- assertThat(ongoingCallRepository.ongoingCallState.value)
- .isInstanceOf(OngoingCallModel.NoCall::class.java)
- }
-
- /** Regression test for b/191472854. */
- @Test
- fun onEntryUpdated_notifHasNullContentIntent_noCrash() {
- notifCollectionListener.onEntryUpdated(
- createCallNotifEntry(ongoingCallStyle, nullContentIntent = true)
- )
- }
-
- /** Regression test for b/192379214. */
- @Test
- @DisableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME, FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun onEntryUpdated_notificationWhenIsZero_timeHidden() {
- val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
- notification.modifyNotification(context).setWhen(0)
-
- notifCollectionListener.onEntryUpdated(notification.build())
- chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- )
-
- assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isEqualTo(0)
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun onEntryUpdated_notificationWhenIsZero_timeShown() {
- val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
- notification.modifyNotification(context).setWhen(0)
-
- notifCollectionListener.onEntryUpdated(notification.build())
- chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- )
-
- assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isGreaterThan(0)
- }
-
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun onEntryUpdated_notificationWhenIsValid_timeShown() {
- val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
- notification.modifyNotification(context).setWhen(clock.currentTimeMillis())
-
- notifCollectionListener.onEntryUpdated(notification.build())
- chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- )
-
- assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isGreaterThan(0)
- }
-
- /** Regression test for b/194731244. */
- @Test
- fun onEntryUpdated_calledManyTimes_uidObserverOnlyRegisteredOnce() {
- for (i in 0 until 4) {
- // Re-create the notification each time so that it's considered a different object and
- // will re-trigger the whole flow.
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- }
-
- verify(mockIActivityManager, times(1)).registerUidObserver(any(), any(), any(), any())
- }
-
- /** Regression test for b/216248574. */
- @Test
- fun entryUpdated_getUidProcessStateThrowsException_noCrash() {
- `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenThrow(SecurityException())
-
- // No assert required, just check no crash
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- }
-
- /** Regression test for b/216248574. */
- @Test
- fun entryUpdated_registerUidObserverThrowsException_noCrash() {
- `when`(
- mockIActivityManager.registerUidObserver(
- any(),
- any(),
- any(),
- nullable(String::class.java),
- )
- )
- .thenThrow(SecurityException())
-
- // No assert required, just check no crash
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- }
-
- /** Regression test for b/216248574. */
- @Test
- fun entryUpdated_packageNameProvidedToActivityManager() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- val packageNameCaptor = ArgumentCaptor.forClass(String::class.java)
- verify(mockIActivityManager)
- .registerUidObserver(any(), any(), any(), packageNameCaptor.capture())
- assertThat(packageNameCaptor.value).isNotNull()
- }
-
- /**
- * If a call notification is never added before #onEntryRemoved is called, then the listener
- * should never be notified.
- */
- @Test
- fun onEntryRemoved_callNotifNeverAddedBeforehand_listenerNotNotified() {
- notifCollectionListener.onEntryRemoved(createOngoingCallNotifEntry(), REASON_USER_STOPPED)
-
- verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
- }
-
- @Test
- fun onEntryRemoved_callNotifAddedThenRemoved_listenerNotified() {
- val ongoingCallNotifEntry = createOngoingCallNotifEntry()
- notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
- reset(mockOngoingCallListener)
-
- notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
-
- verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- }
-
- @Test
- fun onEntryRemoved_callNotifAddedThenRemoved_repoUpdated() {
- val ongoingCallNotifEntry = createOngoingCallNotifEntry()
- notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
- assertThat(ongoingCallRepository.ongoingCallState.value)
- .isInstanceOf(OngoingCallModel.InCall::class.java)
-
- notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
-
- assertThat(ongoingCallRepository.ongoingCallState.value)
- .isInstanceOf(OngoingCallModel.NoCall::class.java)
- }
-
- @Test
- fun onEntryUpdated_callNotifAddedThenRemoved_windowControllerUpdated() {
- val ongoingCallNotifEntry = createOngoingCallNotifEntry()
- notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
-
- notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
-
- verify(mockStatusBarWindowController).setOngoingProcessRequiresStatusBarVisible(false)
- }
-
- /** Regression test for b/188491504. */
- @Test
- fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_listenerNotified() {
- val ongoingCallNotifEntry = createOngoingCallNotifEntry()
- notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
- reset(mockOngoingCallListener)
-
- // Create another notification based on the ongoing call one, but remove the features that
- // made it a call notification.
- val removedEntryBuilder = NotificationEntryBuilder(ongoingCallNotifEntry)
- removedEntryBuilder.modifyNotification(context).style = null
-
- notifCollectionListener.onEntryRemoved(removedEntryBuilder.build(), REASON_USER_STOPPED)
-
- verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- }
-
- /** Regression test for b/188491504. */
- @Test
- fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_repoUpdated() {
- val ongoingCallNotifEntry = createOngoingCallNotifEntry()
- notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
-
- // Create another notification based on the ongoing call one, but remove the features that
- // made it a call notification.
- val removedEntryBuilder = NotificationEntryBuilder(ongoingCallNotifEntry)
- removedEntryBuilder.modifyNotification(context).style = null
-
- notifCollectionListener.onEntryRemoved(removedEntryBuilder.build(), REASON_USER_STOPPED)
-
- assertThat(ongoingCallRepository.ongoingCallState.value)
- .isInstanceOf(OngoingCallModel.NoCall::class.java)
- }
-
- @Test
- fun onEntryRemoved_notifKeyDoesNotMatchOngoingCallNotif_listenerNotNotified() {
- notifCollectionListener.onEntryAdded(createOngoingCallNotifEntry())
- reset(mockOngoingCallListener)
-
- notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
-
- verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
- }
-
- @Test
- fun onEntryRemoved_notifKeyDoesNotMatchOngoingCallNotif_repoNotUpdated() {
- notifCollectionListener.onEntryAdded(createOngoingCallNotifEntry())
-
- notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
-
- assertThat(ongoingCallRepository.ongoingCallState.value)
- .isInstanceOf(OngoingCallModel.InCall::class.java)
- }
-
- @Test
- fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() {
- assertThat(controller.hasOngoingCall()).isFalse()
- }
-
- @Test
- fun hasOngoingCall_unrelatedNotifSent_returnsFalse() {
- notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
-
- assertThat(controller.hasOngoingCall()).isFalse()
- }
-
- @Test
- fun hasOngoingCall_screeningCallNotifSent_returnsFalse() {
- notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
-
- assertThat(controller.hasOngoingCall()).isFalse()
- }
-
- @Test
- fun hasOngoingCall_ongoingCallNotifSentAndCallAppNotVisible_returnsTrue() {
- `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- assertThat(controller.hasOngoingCall()).isTrue()
- }
-
- @Test
- fun hasOngoingCall_ongoingCallNotifSentButCallAppVisible_returnsFalse() {
- `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_VISIBLE)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- assertThat(controller.hasOngoingCall()).isFalse()
- }
-
- @Test
- fun hasOngoingCall_ongoingCallNotifSentButInvalidChipView_returnsFalse() {
- val invalidChipView = LinearLayout(context)
- controller.setChipView(invalidChipView)
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- assertThat(controller.hasOngoingCall()).isFalse()
- }
-
- @Test
- fun hasOngoingCall_ongoingCallNotifSentThenRemoved_returnsFalse() {
- val ongoingCallNotifEntry = createOngoingCallNotifEntry()
-
- notifCollectionListener.onEntryUpdated(ongoingCallNotifEntry)
- notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
-
- assertThat(controller.hasOngoingCall()).isFalse()
- }
-
- @Test
- fun hasOngoingCall_ongoingCallNotifSentThenScreeningCallNotifSent_returnsFalse() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
-
- assertThat(controller.hasOngoingCall()).isFalse()
- }
-
- @Test
- fun hasOngoingCall_ongoingCallNotifSentThenUnrelatedNotifSent_returnsTrue() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
-
- assertThat(controller.hasOngoingCall()).isTrue()
- }
-
- /**
- * This test fakes a theme change during an ongoing call.
- *
- * When a theme change happens, [CollapsedStatusBarFragment] and its views get re-created, so
- * [OngoingCallController.setChipView] gets called with a new view. If there's an active ongoing
- * call when the theme changes, the new view needs to be updated with the call information.
- */
- @Test
- fun setChipView_whenHasOngoingCallIsTrue_listenerNotifiedWithNewView() {
- // Start an ongoing call.
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- reset(mockOngoingCallListener)
-
- lateinit var newChipView: View
- TestableLooper.get(this).runWithLooper {
- newChipView =
- LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip_primary, null)
- }
-
- // Change the chip view associated with the controller.
- controller.setChipView(newChipView)
-
- verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- }
-
- @Test
- fun callProcessChangesToVisible_listenerNotified() {
- // Start the call while the process is invisible.
- `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- reset(mockOngoingCallListener)
-
- val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
- verify(mockIActivityManager)
- .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
- val uidObserver = captor.value
-
- // Update the process to visible.
- uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_VISIBLE, 0, 0)
- mainExecutor.advanceClockToLast()
- mainExecutor.runAllReady()
-
- verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- }
-
- @Test
- fun callProcessChangesToInvisible_listenerNotified() {
- // Start the call while the process is visible.
- `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_VISIBLE)
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- reset(mockOngoingCallListener)
-
- val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
- verify(mockIActivityManager)
- .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
- val uidObserver = captor.value
-
- // Update the process to invisible.
- uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_INVISIBLE, 0, 0)
- mainExecutor.advanceClockToLast()
- mainExecutor.runAllReady()
-
- verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- }
-
- /** Regression test for b/212467440. */
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun chipClicked_activityStarterTriggeredWithUnmodifiedIntent() {
- val notifEntry = createOngoingCallNotifEntry()
- val pendingIntent = notifEntry.sbn.notification.contentIntent
- notifCollectionListener.onEntryUpdated(notifEntry)
-
- chipView.performClick()
-
- // Ensure that the sysui didn't modify the notification's intent -- see b/212467440.
- verify(mockActivityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
- }
-
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun callNotificationAdded_chipIsClickable() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- assertThat(chipView.hasOnClickListeners()).isTrue()
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun callNotificationAdded_newChipsEnabled_chipNotClickable() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- assertThat(chipView.hasOnClickListeners()).isFalse()
- }
-
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun fullscreenIsTrue_chipStillClickable() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
- testScope.runCurrent()
-
- assertThat(chipView.hasOnClickListeners()).isTrue()
- }
-
- // Swipe gesture tests
-
- @Test
- fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
- testScope.runCurrent()
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- verify(mockSwipeStatusBarAwayGestureHandler)
- .addOnGestureDetectedCallback(anyString(), any())
- }
-
- @Test
- fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() {
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
- testScope.runCurrent()
-
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- verify(mockSwipeStatusBarAwayGestureHandler, never())
- .addOnGestureDetectedCallback(anyString(), any())
- }
-
- @Test
- fun transitionToImmersiveMode_swipeGestureCallbackAdded() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
- testScope.runCurrent()
-
- verify(mockSwipeStatusBarAwayGestureHandler)
- .addOnGestureDetectedCallback(anyString(), any())
- }
-
- @Test
- fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() {
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- reset(mockSwipeStatusBarAwayGestureHandler)
-
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
- testScope.runCurrent()
-
- verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
- }
-
- @Test
- fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() {
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
- testScope.runCurrent()
- val ongoingCallNotifEntry = createOngoingCallNotifEntry()
- notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
- reset(mockSwipeStatusBarAwayGestureHandler)
-
- notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
-
- verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
- }
-
- // TODO(b/195839150): Add test
- // swipeGesturedTriggeredPreviously_entersImmersiveModeAgain_callbackNotAdded(). That's
- // difficult to add now because we have no way to trigger [SwipeStatusBarAwayGestureHandler]'s
- // callbacks in test.
-
- // END swipe gesture tests
-
- private fun createOngoingCallNotifEntry() = createCallNotifEntry(ongoingCallStyle)
-
- private fun createScreeningCallNotifEntry() = createCallNotifEntry(screeningCallStyle)
-
- private fun createCallNotifEntry(
- callStyle: Notification.CallStyle,
- nullContentIntent: Boolean = false,
- ): NotificationEntry {
- val notificationEntryBuilder = NotificationEntryBuilder()
- notificationEntryBuilder.modifyNotification(context).style = callStyle
- notificationEntryBuilder.setUid(CALL_UID)
-
- if (nullContentIntent) {
- notificationEntryBuilder.modifyNotification(context).setContentIntent(null)
- } else {
- val contentIntent = mock(PendingIntent::class.java)
- notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent)
- }
-
- return notificationEntryBuilder.build()
- }
-
- private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
-}
-
-private val person = Person.Builder().setName("name").build()
-private val hangUpIntent = mock(PendingIntent::class.java)
-
-private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(person, hangUpIntent)
-private val screeningCallStyle =
- Notification.CallStyle.forScreeningCall(
- person,
- hangUpIntent,
- /* answerIntent= */ mock(PendingIntent::class.java),
- )
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 129a6bb..2ed0671 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -23,13 +23,7 @@
import com.android.server.notification.Flags.politeNotifications
import com.android.server.notification.Flags.vibrateWhileUnlocked
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
-import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
import com.android.systemui.Flags.communalHub
-import com.android.systemui.Flags.statusBarCallChipNotificationIcon
-import com.android.systemui.Flags.statusBarScreenSharingChips
-import com.android.systemui.Flags.statusBarUseReposForCallChip
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.shared.flag.DualShade
@@ -63,10 +57,6 @@
// DualShade dependencies
DualShade.token dependsOn SceneContainerFlag.getMainAconfigFlag()
-
- // Status bar chip dependencies
- statusBarCallChipNotificationIconToken dependsOn statusBarUseReposForCallChipToken
- statusBarCallChipNotificationIconToken dependsOn statusBarScreenSharingChipsToken
}
private inline val politeNotifications
@@ -86,17 +76,4 @@
private inline val communalHub
get() = FlagToken(FLAG_COMMUNAL_HUB, communalHub())
-
- private inline val statusBarCallChipNotificationIconToken
- get() =
- FlagToken(
- FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON,
- statusBarCallChipNotificationIcon(),
- )
-
- private inline val statusBarScreenSharingChipsToken
- get() = FlagToken(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, statusBarScreenSharingChips())
-
- private inline val statusBarUseReposForCallChipToken
- get() = FlagToken(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP, statusBarUseReposForCallChip())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index de08e38..86954d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -18,7 +18,6 @@
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.Flags
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -64,18 +63,12 @@
is OngoingCallModel.InCallWithVisibleApp -> OngoingActivityChipModel.Hidden()
is OngoingCallModel.InCall -> {
val icon =
- if (
- Flags.statusBarCallChipNotificationIcon() &&
- state.notificationIconView != null
- ) {
+ if (state.notificationIconView != null) {
StatusBarConnectedDisplays.assertInLegacyMode()
OngoingActivityChipModel.ChipIcon.StatusBarView(
state.notificationIconView
)
- } else if (
- StatusBarConnectedDisplays.isEnabled &&
- Flags.statusBarCallChipNotificationIcon()
- ) {
+ } else if (StatusBarConnectedDisplays.isEnabled) {
OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(
state.notificationKey
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 2f6431b..ec3a5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.domain.model.TopPinnedState
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import javax.inject.Inject
@@ -60,7 +61,7 @@
/** Converts the notification to the [OngoingActivityChipModel] object. */
private fun NotificationChipModel.toActivityChipModel(
- headsUpState: PinnedStatus
+ headsUpState: TopPinnedState
): OngoingActivityChipModel.Shown {
StatusBarNotifChips.assertInNewMode()
val icon =
@@ -87,8 +88,12 @@
}
}
- if (headsUpState == PinnedStatus.PinnedByUser) {
- // If the user tapped the chip to show the HUN, we want to just show the icon because
+ val isShowingHeadsUpFromChipTap =
+ headsUpState is TopPinnedState.Pinned &&
+ headsUpState.status == PinnedStatus.PinnedByUser &&
+ headsUpState.key == this.key
+ if (isShowingHeadsUpFromChipTap) {
+ // If the user tapped this chip to show the HUN, we want to just show the icon because
// the HUN will show the rest of the information.
return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index c81e8e2..956d99e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.chips.ui.model
import android.view.View
-import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
@@ -130,10 +129,6 @@
*/
data class StatusBarView(val impl: StatusBarIconView) : ChipIcon {
init {
- check(Flags.statusBarCallChipNotificationIcon()) {
- "OngoingActivityChipModel.ChipIcon.StatusBarView created even though " +
- "Flags.statusBarCallChipNotificationIcon is not enabled"
- }
StatusBarConnectedDisplays.assertInLegacyMode()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index c7535ec..eb5a370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -112,20 +112,20 @@
if (StatusBarNotifChips.isEnabled) {
applicationScope.launch {
statusBarNotificationChipsInteractor.promotedNotificationChipTapEvent.collect {
- showPromotedNotificationHeadsUp(it)
+ onPromotedNotificationChipTapEvent(it)
}
}
}
}
/**
- * Shows the promoted notification with the given [key] as heads-up.
+ * Updates the heads-up state based on which promoted notification with the given [key] was
+ * tapped.
*
* Must be run on the main thread.
*/
- private fun showPromotedNotificationHeadsUp(key: String) {
+ private fun onPromotedNotificationChipTapEvent(key: String) {
StatusBarNotifChips.assertInNewMode()
- mLogger.logShowPromotedNotificationHeadsUp(key)
val entry = notifCollection.getEntry(key)
if (entry == null) {
@@ -135,22 +135,29 @@
// TODO(b/364653005): Validate that the given key indeed matches a promoted notification,
// not just any notification.
+ val isCurrentlyHeadsUp = mHeadsUpManager.isHeadsUpEntry(entry.key)
val posted =
PostedEntry(
entry,
wasAdded = false,
wasUpdated = false,
- // Force-set this notification to show heads-up.
- shouldHeadsUpEver = true,
- shouldHeadsUpAgain = true,
+ // We want the chip to act as a toggle, so if the chip's notification is currently
+ // showing as heads up, then we should stop showing it.
+ shouldHeadsUpEver = !isCurrentlyHeadsUp,
+ shouldHeadsUpAgain = !isCurrentlyHeadsUp,
isPinnedByUser = true,
- isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key),
+ isHeadsUpEntry = isCurrentlyHeadsUp,
isBinding = isEntryBinding(entry),
)
+ if (isCurrentlyHeadsUp) {
+ mLogger.logHidePromotedNotificationHeadsUp(key)
+ } else {
+ mLogger.logShowPromotedNotificationHeadsUp(key)
+ }
mExecutor.execute {
mPostedEntries[entry.key] = posted
- mNotifPromoter.invalidateList("showPromotedNotificationHeadsUp: ${entry.logKey}")
+ mNotifPromoter.invalidateList("onPromotedNotificationChipTapEvent: ${entry.logKey}")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
index e443a04..5141aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
@@ -148,6 +148,15 @@
)
}
+ fun logHidePromotedNotificationHeadsUp(key: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = key },
+ { "requesting promoted entry to hide heads up: $str1" },
+ )
+ }
+
fun logPromotedNotificationForHeadsUpNotFound(key: String) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index 75c7d2d..6140c92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
+import com.android.systemui.statusbar.notification.domain.model.TopPinnedState
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
import javax.inject.Inject
@@ -98,21 +99,39 @@
}
}
- /** What [PinnedStatus] does the top row have? */
- private val topPinnedStatus: Flow<PinnedStatus> =
+ /** What [PinnedStatus] and key does the top row have? */
+ private val topPinnedState: Flow<TopPinnedState> =
headsUpRepository.activeHeadsUpRows.flatMapLatest { rows ->
if (rows.isNotEmpty()) {
- combine(rows.map { it.pinnedStatus }) { pinnedStatus ->
- pinnedStatus.firstOrNull { it.isPinned } ?: PinnedStatus.NotPinned
+ // For each row, emits a (key, pinnedStatus) pair each time any row's
+ // `pinnedStatus` changes
+ val toCombine: List<Flow<Pair<String, PinnedStatus>>> =
+ rows.map { row -> row.pinnedStatus.map { status -> row.key to status } }
+ combine(toCombine) { pairs ->
+ val topPinnedRow: Pair<String, PinnedStatus>? =
+ pairs.firstOrNull { it.second.isPinned }
+ if (topPinnedRow != null) {
+ TopPinnedState.Pinned(
+ key = topPinnedRow.first,
+ status = topPinnedRow.second,
+ )
+ } else {
+ TopPinnedState.NothingPinned
+ }
}
} else {
- // if the set is empty, there are no flows to combine
- flowOf(PinnedStatus.NotPinned)
+ flowOf(TopPinnedState.NothingPinned)
}
}
/** Are there any pinned heads up rows to display? */
- val hasPinnedRows: Flow<Boolean> = topPinnedStatus.map { it.isPinned }
+ val hasPinnedRows: Flow<Boolean> =
+ topPinnedState.map {
+ when (it) {
+ is TopPinnedState.Pinned -> it.status.isPinned
+ is TopPinnedState.NothingPinned -> false
+ }
+ }
val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
@@ -142,13 +161,25 @@
}
}
- /** Emits the pinned notification state as it relates to the status bar. */
- val statusBarHeadsUpState: Flow<PinnedStatus> =
- combine(topPinnedStatus, canShowHeadsUp) { topPinnedStatus, canShowHeadsUp ->
+ /**
+ * Emits the pinned notification state as it relates to the status bar. Includes both the pinned
+ * status and key of the notification that's pinned (if there is a pinned notification).
+ */
+ val statusBarHeadsUpState: Flow<TopPinnedState> =
+ combine(topPinnedState, canShowHeadsUp) { topPinnedState, canShowHeadsUp ->
if (canShowHeadsUp) {
- topPinnedStatus
+ topPinnedState
} else {
- PinnedStatus.NotPinned
+ TopPinnedState.NothingPinned
+ }
+ }
+
+ /** Emits the pinned notification status as it relates to the status bar. */
+ val statusBarHeadsUpStatus: Flow<PinnedStatus> =
+ statusBarHeadsUpState.map {
+ when (it) {
+ is TopPinnedState.Pinned -> it.status
+ is TopPinnedState.NothingPinned -> PinnedStatus.NotPinned
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 042389f..fd5973e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -25,7 +25,6 @@
import android.service.notification.StatusBarNotification
import android.util.ArrayMap
import com.android.app.tracing.traceSection
-import com.android.systemui.Flags
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -132,12 +131,6 @@
}
private fun NotificationEntry.toModel(): ActiveNotificationModel {
- val statusBarChipIcon =
- if (Flags.statusBarCallChipNotificationIcon()) {
- icons.statusBarChipIcon
- } else {
- null
- }
val promotedContent =
if (PromotedNotificationContentModel.featureFlagEnabled()) {
promotedNotificationContentModel
@@ -158,7 +151,7 @@
aodIcon = icons.aodIcon?.sourceIcon,
shelfIcon = icons.shelfIcon?.sourceIcon,
statusBarIcon = icons.statusBarIcon?.sourceIcon,
- statusBarChipIconView = statusBarChipIcon,
+ statusBarChipIconView = icons.statusBarChipIcon,
uid = sbn.uid,
packageName = sbn.packageName,
contentIntent = sbn.notification.contentIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/model/TopPinnedState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/model/TopPinnedState.kt
new file mode 100644
index 0000000..51c448a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/model/TopPinnedState.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 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.domain.model
+
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+
+/** A class representing the state of the top pinned row. */
+sealed interface TopPinnedState {
+ /** Nothing is pinned. */
+ data object NothingPinned : TopPinnedState
+
+ /**
+ * The top pinned row is a notification with the given key and status.
+ *
+ * @property status must have [PinnedStatus.isPinned] as true.
+ */
+ data class Pinned(val key: String, val status: PinnedStatus) : TopPinnedState {
+ init {
+ check(status.isPinned)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index b56a838..31375cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -162,9 +162,7 @@
val sbIcon = iconBuilder.createIconView(entry)
sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
val sbChipIcon: StatusBarIconView?
- if (
- Flags.statusBarCallChipNotificationIcon() && !StatusBarConnectedDisplays.isEnabled
- ) {
+ if (!StatusBarConnectedDisplays.isEnabled) {
sbChipIcon = iconBuilder.createIconView(entry)
sbChipIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
} else {
@@ -186,7 +184,7 @@
try {
setIcon(entry, normalIconDescriptor, sbIcon)
- if (Flags.statusBarCallChipNotificationIcon() && sbChipIcon != null) {
+ if (sbChipIcon != null) {
setIcon(entry, normalIconDescriptor, sbChipIcon)
}
setIcon(entry, sensitiveIconDescriptor, shelfIcon)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index c57cede..f56c2d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -18,8 +18,6 @@
import android.app.ActivityManager
import android.app.IActivityManager
-import android.app.Notification
-import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
import android.app.PendingIntent
import android.app.UidObserver
import android.content.Context
@@ -44,9 +42,6 @@
import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
@@ -60,7 +55,9 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-/** A controller to handle the ongoing call chip in the collapsed status bar.
+/**
+ * A controller to handle the ongoing call chip in the collapsed status bar.
+ *
* @deprecated Use [OngoingCallInteractor] instead, which follows recommended architecture patterns
*/
@Deprecated("Use OngoingCallInteractor instead")
@@ -71,7 +68,6 @@
@Application private val scope: CoroutineScope,
private val context: Context,
private val ongoingCallRepository: OngoingCallRepository,
- private val notifCollection: CommonNotifCollection,
private val activeNotificationsInteractor: ActiveNotificationsInteractor,
private val systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@@ -90,105 +86,24 @@
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
private val uidObserver = CallAppUidObserver()
- private val notifListener =
- object : NotifCollectionListener {
- // Temporary workaround for b/178406514 for testing purposes.
- //
- // b/178406514 means that posting an incoming call notif then updating it to an ongoing
- // call notif does not work (SysUI never receives the update). This workaround allows us
- // to trigger the ongoing call chip when an ongoing call notif is *added* rather than
- // *updated*, allowing us to test the chip.
- //
- // TODO(b/183229367): Remove this function override when b/178406514 is fixed.
- override fun onEntryAdded(entry: NotificationEntry) {
- onEntryUpdated(entry, true)
- }
-
- override fun onEntryUpdated(entry: NotificationEntry) {
- StatusBarUseReposForCallChip.assertInLegacyMode()
- // We have a new call notification or our existing call notification has been
- // updated.
- // TODO(b/183229367): This likely won't work if you take a call from one app then
- // switch to a call from another app.
- if (
- callNotificationInfo == null && isCallNotification(entry) ||
- (entry.sbn.key == callNotificationInfo?.key)
- ) {
- val newOngoingCallInfo =
- CallNotificationInfo(
- entry.sbn.key,
- entry.sbn.notification.getWhen(),
- // In this old listener pattern, we don't have access to the
- // notification icon.
- notificationIconView = null,
- entry.sbn.notification.contentIntent,
- entry.sbn.uid,
- entry.sbn.notification.extras.getInt(
- Notification.EXTRA_CALL_TYPE,
- -1,
- ) == CALL_TYPE_ONGOING,
- statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false,
- )
- if (newOngoingCallInfo == callNotificationInfo) {
- return
- }
-
- callNotificationInfo = newOngoingCallInfo
- if (newOngoingCallInfo.isOngoing) {
- logger.log(
- TAG,
- LogLevel.DEBUG,
- { str1 = newOngoingCallInfo.key },
- { "Call notif *is* ongoing -> showing chip. key=$str1" },
- )
- updateChip()
- } else {
- logger.log(
- TAG,
- LogLevel.DEBUG,
- { str1 = newOngoingCallInfo.key },
- { "Call notif not ongoing -> hiding chip. key=$str1" },
- )
- removeChip()
- }
- }
- }
-
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- if (entry.sbn.key == callNotificationInfo?.key) {
- logger.log(
- TAG,
- LogLevel.DEBUG,
- { str1 = entry.sbn.key },
- { "Call notif removed -> hiding chip. key=$str1" },
- )
- removeChip()
- }
- }
- }
override fun start() {
- if (StatusBarChipsModernization.isEnabled)
- return
+ if (StatusBarChipsModernization.isEnabled) return
dumpManager.registerDumpable(this)
- if (Flags.statusBarUseReposForCallChip()) {
- scope.launch {
- // Listening to [ActiveNotificationsInteractor] instead of using
- // [NotifCollectionListener#onEntryUpdated] is better for two reasons:
- // 1. ActiveNotificationsInteractor automatically filters the notification list to
- // just notifications for the current user, which ensures we don't show a call chip
- // for User 1's call while User 2 is active (see b/328584859).
- // 2. ActiveNotificationsInteractor only emits notifications that are currently
- // present in the shade, which means we know we've already inflated the icon that we
- // might use for the call chip (see b/354930838).
- activeNotificationsInteractor.ongoingCallNotification.collect {
- updateInfoFromNotifModel(it)
- }
+ scope.launch {
+ // Listening to [ActiveNotificationsInteractor] instead of using
+ // [NotifCollectionListener#onEntryUpdated] is better for two reasons:
+ // 1. ActiveNotificationsInteractor automatically filters the notification list to
+ // just notifications for the current user, which ensures we don't show a call chip
+ // for User 1's call while User 2 is active (see b/328584859).
+ // 2. ActiveNotificationsInteractor only emits notifications that are currently
+ // present in the shade, which means we know we've already inflated the icon that we
+ // might use for the call chip (see b/354930838).
+ activeNotificationsInteractor.ongoingCallNotification.collect {
+ updateInfoFromNotifModel(it)
}
- } else {
- notifCollection.addCollectionListener(notifListener)
}
scope.launch {
@@ -244,21 +159,12 @@
logger.log(
TAG,
LogLevel.DEBUG,
- {
- bool1 = Flags.statusBarCallChipNotificationIcon()
- bool2 = currentInfo.notificationIconView != null
- },
- { "Creating OngoingCallModel.InCall. notifIconFlag=$bool1 hasIcon=$bool2" },
+ { bool1 = currentInfo.notificationIconView != null },
+ { "Creating OngoingCallModel.InCall. hasIcon=$bool1" },
)
- val icon =
- if (Flags.statusBarCallChipNotificationIcon()) {
- currentInfo.notificationIconView
- } else {
- null
- }
return OngoingCallModel.InCall(
startTimeMs = currentInfo.callStartTime,
- notificationIconView = icon,
+ notificationIconView = currentInfo.notificationIconView,
intent = currentInfo.intent,
notificationKey = currentInfo.key,
)
@@ -597,8 +503,4 @@
}
}
-private fun isCallNotification(entry: NotificationEntry): Boolean {
- return entry.sbn.notification.isStyle(Notification.CallStyle::class.java)
-}
-
private const val TAG = OngoingCallRepository.TAG
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarUseReposForCallChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarUseReposForCallChip.kt
deleted file mode 100644
index 4bdd90e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarUseReposForCallChip.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2024 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.phone.ongoingcall
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the status bar use repos for call chip flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object StatusBarUseReposForCallChip {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
-
- /** A token used for dependency declaration */
- val token: FlagToken
- get() = FlagToken(FLAG_NAME, isEnabled)
-
- /** Is the refactor enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.statusBarUseReposForCallChip()
-
- /**
- * Called to ensure code is only run when the flag is enabled. This protects users from the
- * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
- * build to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun isUnexpectedlyInLegacyMode() =
- RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index d731752..d9d9a29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -295,11 +295,12 @@
override val shouldShowOperatorNameView: Flow<Boolean> =
combine(
shouldHomeStatusBarBeVisible,
- headsUpNotificationInteractor.statusBarHeadsUpState,
+ headsUpNotificationInteractor.statusBarHeadsUpStatus,
homeStatusBarInteractor.visibilityViaDisableFlags,
homeStatusBarInteractor.shouldShowOperatorName,
- ) { shouldStatusBarBeVisible, headsUpState, visibilityViaDisableFlags, shouldShowOperator ->
- val hideForHeadsUp = headsUpState == PinnedStatus.PinnedBySystem
+ ) { shouldStatusBarBeVisible, headsUpStatus, visibilityViaDisableFlags, shouldShowOperator
+ ->
+ val hideForHeadsUp = headsUpStatus == PinnedStatus.PinnedBySystem
shouldStatusBarBeVisible &&
!hideForHeadsUp &&
visibilityViaDisableFlags.isSystemInfoAllowed &&
@@ -309,10 +310,10 @@
override val isClockVisible: Flow<VisibilityModel> =
combine(
shouldHomeStatusBarBeVisible,
- headsUpNotificationInteractor.statusBarHeadsUpState,
+ headsUpNotificationInteractor.statusBarHeadsUpStatus,
homeStatusBarInteractor.visibilityViaDisableFlags,
- ) { shouldStatusBarBeVisible, headsUpState, visibilityViaDisableFlags ->
- val hideClockForHeadsUp = headsUpState == PinnedStatus.PinnedBySystem
+ ) { shouldStatusBarBeVisible, headsUpStatus, visibilityViaDisableFlags ->
+ val hideClockForHeadsUp = headsUpStatus == PinnedStatus.PinnedBySystem
val showClock =
shouldStatusBarBeVisible &&
visibilityViaDisableFlags.isClockAllowed &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
index 6175ea1..a98a9e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
@@ -60,7 +60,7 @@
private val showingHeadsUpStatusBar: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
- headsUpNotificationInteractor.statusBarHeadsUpState.map { it.isPinned }
+ headsUpNotificationInteractor.statusBarHeadsUpStatus.map { it.isPinned }
} else {
flowOf(false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index 57a12df..c4ef4f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -35,7 +35,6 @@
import androidx.test.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -112,20 +111,8 @@
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
- fun testCreateIcons_chipNotifIconFlagDisabled_statusBarChipIconIsNull() {
- val entry =
- notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
- entry?.let { iconManager.createIcons(it) }
- testScope.runCurrent()
-
- assertThat(entry?.icons?.statusBarChipIcon).isNull()
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
- fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagDisabled_statusBarChipIconIsNotNull() {
+ fun testCreateIcons_cdFlagDisabled_statusBarChipIconIsNotNull() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
entry?.let { iconManager.createIcons(it) }
@@ -135,8 +122,8 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
- fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagEnabled_statusBarChipIconIsNull() {
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun testCreateIcons_cdFlagEnabled_statusBarChipIconIsNull() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
entry?.let { iconManager.createIcons(it) }
@@ -217,7 +204,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun testCreateIcons_cdFlagDisabled_sensitiveImportantConversation() {
val entry =
@@ -233,7 +219,7 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun testCreateIcons_cdFlagEnabled_sensitiveImportantConversation() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
@@ -248,7 +234,6 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun testUpdateIcons_cdFlagDisabled_sensitiveImportantConversation() {
val entry =
@@ -266,7 +251,7 @@
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun testUpdateIcons_cdFlagEnabled_sensitiveImportantConversation() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)