Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -404,7 +404,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { } fun assertIsCallChip(latest: OngoingActivityChipModel?, notificationKey: String) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) if (StatusBarConnectedDisplays.isEnabled) { assertNotificationIcon(latest, notificationKey) return Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt +154 −6 Original line number Diff line number Diff line Loading @@ -18,15 +18,20 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import android.content.DialogInterface import android.content.packageManager import android.content.res.Configuration import android.content.res.mainResources import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.mediaprojection.data.model.MediaProjectionState Loading @@ -48,21 +53,21 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsVie import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.statusbar.core.StatusBarRootModernization 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 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization 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.phone.ongoingcall.shared.model.inCallModel import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach Loading @@ -85,15 +90,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock private val commandRegistry = kosmos.commandRegistry private val screenRecordState = kosmos.screenRecordRepository.screenRecordState private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState private val callRepo = kosmos.ongoingCallRepository private val activeNotificationListRepository = kosmos.activeNotificationListRepository private val pw = PrintWriter(StringWriter()) private val mockSystemUIDialog = mock<SystemUIDialog>() private val chipBackgroundView = mock<ChipBackgroundContainer>() private val chipView = Loading Loading @@ -201,6 +203,152 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { assertIsCallChip(latest!!.secondary, callNotificationKey) } @Test fun chips_oneChip_notSquished() = testScope.runTest { callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) val latest by collectLastValue(underTest.chips) // The call chip isn't squished (squished chips would be icon only) assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoTimerChips_isSmallPortrait_andChipsModernizationDisabled_bothSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) val latest by collectLastValue(underTest.chips) // Squished chips are icon only assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_countdownChipAndTimerChip_countdownNotSquished_butTimerSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Starting(millisUntilStarted = 2000) callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) val latest by collectLastValue(underTest.chips) // The screen record countdown isn't squished to icon-only assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Countdown::class.java) // But the call chip *is* squished assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_numberOfChipsChanges_chipsGetSquishedAndUnsquished() = testScope.runTest { val latest by collectLastValue(underTest.chips) // WHEN there's only one chip screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(OngoingCallModel.NoCall) // The screen record isn't squished because it's the only one assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) // WHEN there's 2 chips callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) // THEN they both become squished assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) // But the call chip *is* squished assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) // WHEN we go back down to 1 chip screenRecordState.value = ScreenRecordModel.DoingNothing // THEN the remaining chip unsquishes assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoChips_isLandscape_notSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) // WHEN we're in landscape val config = Configuration(kosmos.mainResources.configuration).apply { orientation = Configuration.ORIENTATION_LANDSCAPE } kosmos.fakeConfigurationRepository.onConfigurationChange(config) val latest by collectLastValue(underTest.chips) // THEN the chips aren't squished (squished chips would be icon only) assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoChips_isLargeScreen_notSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) // WHEN we're on a large screen kosmos.displayStateRepository.setIsLargeScreen(true) val latest by collectLastValue(underTest.chips) // THEN the chips aren't squished (squished chips would be icon only) assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test @EnableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoChips_chipsModernizationEnabled_notSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording setNotifs( listOf( activeNotificationModel( key = "call", statusBarChipIcon = createStatusBarIconViewOrNull(), callType = CallType.Ongoing, whenTime = 499, ) ) ) val latest by collectLastValue(underTest.chips) // Squished chips would be icon only assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test fun primaryChip_screenRecordShowAndShareToAppShow_screenRecordShown() = testScope.runTest { Loading packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +92 −11 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import android.content.res.Configuration import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer Loading @@ -30,6 +33,7 @@ import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenReco import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope Loading @@ -37,7 +41,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn /** Loading @@ -56,8 +62,30 @@ constructor( castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, notifChipsViewModel: NotifChipsViewModel, displayStateInteractor: DisplayStateInteractor, configurationInteractor: ConfigurationInteractor, @StatusBarChipsLog private val logger: LogBuffer, ) { private val isLandscape: Flow<Boolean> = configurationInteractor.configurationValues .map { it.isLandscape } .stateIn(scope, SharingStarted.WhileSubscribed(), false) private val isScreenReasonablyLarge: Flow<Boolean> = combine(isLandscape, displayStateInteractor.isLargeScreen) { isLandscape, isLargeScreen -> isLandscape || isLargeScreen } .distinctUntilChanged() .onEach { logger.log( TAG, LogLevel.DEBUG, { bool1 = it }, { "isScreenReasonablyLarge: $bool1" }, ) } .stateIn(scope, SharingStarted.WhileSubscribed(), false) private enum class ChipType { ScreenRecord, ShareToApp, Loading Loading @@ -165,21 +193,71 @@ constructor( ) private val internalChips: Flow<InternalMultipleOngoingActivityChipsModel> = incomingChipBundle.map { bundle -> combine(incomingChipBundle, isScreenReasonablyLarge) { bundle, isScreenReasonablyLarge -> // First: Find the most important chip. val primaryChipResult = pickMostImportantChip(bundle) val primaryChip = primaryChipResult.mostImportantChip if (primaryChip is InternalChipModel.Hidden) { // If the primary chip is hidden, the secondary chip will also be hidden, so just // pass the same Hidden model for both. when (val primaryChip = primaryChipResult.mostImportantChip) { is InternalChipModel.Hidden -> { // If the primary chip is hidden, the secondary chip will also be hidden, so // just pass the same Hidden model for both. InternalMultipleOngoingActivityChipsModel(primaryChip, primaryChip) } else { // Then: Find the next most important chip. } is InternalChipModel.Shown -> { // Otherwise: Find the next most important chip. val secondaryChip = pickMostImportantChip(primaryChipResult.remainingChips).mostImportantChip if ( secondaryChip is InternalChipModel.Shown && StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled && !isScreenReasonablyLarge ) { // If we have two showing chips and we don't have a ton of room // (!isScreenReasonablyLarge), then we want to make both of them as small as // possible so that we have the highest chance of showing both chips (as // opposed to showing the primary chip with a lot of text and completely // hiding the secondary chip). // Also: If StatusBarChipsModernization is enabled, then we'll do the // squishing in Compose instead. InternalMultipleOngoingActivityChipsModel( primaryChip.squish(), secondaryChip.squish(), ) } else { InternalMultipleOngoingActivityChipsModel(primaryChip, secondaryChip) } } } } /** Squishes the chip down to the smallest content possible. */ private fun InternalChipModel.Shown.squish(): InternalChipModel.Shown { return when (model) { // Icon-only is already maximum squished is OngoingActivityChipModel.Shown.IconOnly -> this // Countdown shows just a single digit, so already maximum squished is OngoingActivityChipModel.Shown.Countdown -> this // The other chips have icon+text, so we should hide the text is OngoingActivityChipModel.Shown.Timer, is OngoingActivityChipModel.Shown.ShortTimeDelta, is OngoingActivityChipModel.Shown.Text -> InternalChipModel.Shown(this.type, this.model.toIconOnly()) } } private fun OngoingActivityChipModel.Shown.toIconOnly(): OngoingActivityChipModel.Shown { // If this chip doesn't have an icon, then it only has text and we should continue showing // its text. (This is theoretically impossible because // [OngoingActivityChipModel.Shown.Countdown] is the only chip without an icon, but protect // against it just in case.) val currentIcon = icon ?: return this return OngoingActivityChipModel.Shown.IconOnly( currentIcon, colors, onClickListenerLegacy, clickBehavior, ) } /** * A flow modeling the primary chip that should be shown in the status bar after accounting for Loading Loading @@ -327,6 +405,9 @@ constructor( } } private val Configuration.isLandscape: Boolean get() = orientation == Configuration.ORIENTATION_LANDSCAPE companion object { private val TAG = "ChipsViewModel".pad() Loading packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import com.android.systemui.biometrics.domain.interactor.displayStateInteractor import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel Loading @@ -34,6 +36,8 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel, callChipViewModel = callChipViewModel, notifChipsViewModel = notifChipsViewModel, displayStateInteractor = displayStateInteractor, configurationInteractor = configurationInteractor, logger = statusBarChipsLogger, ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -404,7 +404,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { } fun assertIsCallChip(latest: OngoingActivityChipModel?, notificationKey: String) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) if (StatusBarConnectedDisplays.isEnabled) { assertNotificationIcon(latest, notificationKey) return Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt +154 −6 Original line number Diff line number Diff line Loading @@ -18,15 +18,20 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import android.content.DialogInterface import android.content.packageManager import android.content.res.Configuration import android.content.res.mainResources import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.mediaprojection.data.model.MediaProjectionState Loading @@ -48,21 +53,21 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsVie import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.statusbar.core.StatusBarRootModernization 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 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization 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.phone.ongoingcall.shared.model.inCallModel import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach Loading @@ -85,15 +90,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock private val commandRegistry = kosmos.commandRegistry private val screenRecordState = kosmos.screenRecordRepository.screenRecordState private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState private val callRepo = kosmos.ongoingCallRepository private val activeNotificationListRepository = kosmos.activeNotificationListRepository private val pw = PrintWriter(StringWriter()) private val mockSystemUIDialog = mock<SystemUIDialog>() private val chipBackgroundView = mock<ChipBackgroundContainer>() private val chipView = Loading Loading @@ -201,6 +203,152 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { assertIsCallChip(latest!!.secondary, callNotificationKey) } @Test fun chips_oneChip_notSquished() = testScope.runTest { callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) val latest by collectLastValue(underTest.chips) // The call chip isn't squished (squished chips would be icon only) assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoTimerChips_isSmallPortrait_andChipsModernizationDisabled_bothSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) val latest by collectLastValue(underTest.chips) // Squished chips are icon only assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_countdownChipAndTimerChip_countdownNotSquished_butTimerSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Starting(millisUntilStarted = 2000) callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) val latest by collectLastValue(underTest.chips) // The screen record countdown isn't squished to icon-only assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Countdown::class.java) // But the call chip *is* squished assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_numberOfChipsChanges_chipsGetSquishedAndUnsquished() = testScope.runTest { val latest by collectLastValue(underTest.chips) // WHEN there's only one chip screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(OngoingCallModel.NoCall) // The screen record isn't squished because it's the only one assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) // WHEN there's 2 chips callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) // THEN they both become squished assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) // But the call chip *is* squished assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) // WHEN we go back down to 1 chip screenRecordState.value = ScreenRecordModel.DoingNothing // THEN the remaining chip unsquishes assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoChips_isLandscape_notSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) // WHEN we're in landscape val config = Configuration(kosmos.mainResources.configuration).apply { orientation = Configuration.ORIENTATION_LANDSCAPE } kosmos.fakeConfigurationRepository.onConfigurationChange(config) val latest by collectLastValue(underTest.chips) // THEN the chips aren't squished (squished chips would be icon only) assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test @DisableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoChips_isLargeScreen_notSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34, notificationKey = "call")) // WHEN we're on a large screen kosmos.displayStateRepository.setIsLargeScreen(true) val latest by collectLastValue(underTest.chips) // THEN the chips aren't squished (squished chips would be icon only) assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test @EnableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun chips_twoChips_chipsModernizationEnabled_notSquished() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording setNotifs( listOf( activeNotificationModel( key = "call", statusBarChipIcon = createStatusBarIconViewOrNull(), callType = CallType.Ongoing, whenTime = 499, ) ) ) val latest by collectLastValue(underTest.chips) // Squished chips would be icon only assertThat(latest!!.primary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) assertThat(latest!!.secondary) .isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @Test fun primaryChip_screenRecordShowAndShareToAppShow_screenRecordShown() = testScope.runTest { Loading
packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +92 −11 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import android.content.res.Configuration import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer Loading @@ -30,6 +33,7 @@ import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenReco import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope Loading @@ -37,7 +41,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn /** Loading @@ -56,8 +62,30 @@ constructor( castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, notifChipsViewModel: NotifChipsViewModel, displayStateInteractor: DisplayStateInteractor, configurationInteractor: ConfigurationInteractor, @StatusBarChipsLog private val logger: LogBuffer, ) { private val isLandscape: Flow<Boolean> = configurationInteractor.configurationValues .map { it.isLandscape } .stateIn(scope, SharingStarted.WhileSubscribed(), false) private val isScreenReasonablyLarge: Flow<Boolean> = combine(isLandscape, displayStateInteractor.isLargeScreen) { isLandscape, isLargeScreen -> isLandscape || isLargeScreen } .distinctUntilChanged() .onEach { logger.log( TAG, LogLevel.DEBUG, { bool1 = it }, { "isScreenReasonablyLarge: $bool1" }, ) } .stateIn(scope, SharingStarted.WhileSubscribed(), false) private enum class ChipType { ScreenRecord, ShareToApp, Loading Loading @@ -165,21 +193,71 @@ constructor( ) private val internalChips: Flow<InternalMultipleOngoingActivityChipsModel> = incomingChipBundle.map { bundle -> combine(incomingChipBundle, isScreenReasonablyLarge) { bundle, isScreenReasonablyLarge -> // First: Find the most important chip. val primaryChipResult = pickMostImportantChip(bundle) val primaryChip = primaryChipResult.mostImportantChip if (primaryChip is InternalChipModel.Hidden) { // If the primary chip is hidden, the secondary chip will also be hidden, so just // pass the same Hidden model for both. when (val primaryChip = primaryChipResult.mostImportantChip) { is InternalChipModel.Hidden -> { // If the primary chip is hidden, the secondary chip will also be hidden, so // just pass the same Hidden model for both. InternalMultipleOngoingActivityChipsModel(primaryChip, primaryChip) } else { // Then: Find the next most important chip. } is InternalChipModel.Shown -> { // Otherwise: Find the next most important chip. val secondaryChip = pickMostImportantChip(primaryChipResult.remainingChips).mostImportantChip if ( secondaryChip is InternalChipModel.Shown && StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled && !isScreenReasonablyLarge ) { // If we have two showing chips and we don't have a ton of room // (!isScreenReasonablyLarge), then we want to make both of them as small as // possible so that we have the highest chance of showing both chips (as // opposed to showing the primary chip with a lot of text and completely // hiding the secondary chip). // Also: If StatusBarChipsModernization is enabled, then we'll do the // squishing in Compose instead. InternalMultipleOngoingActivityChipsModel( primaryChip.squish(), secondaryChip.squish(), ) } else { InternalMultipleOngoingActivityChipsModel(primaryChip, secondaryChip) } } } } /** Squishes the chip down to the smallest content possible. */ private fun InternalChipModel.Shown.squish(): InternalChipModel.Shown { return when (model) { // Icon-only is already maximum squished is OngoingActivityChipModel.Shown.IconOnly -> this // Countdown shows just a single digit, so already maximum squished is OngoingActivityChipModel.Shown.Countdown -> this // The other chips have icon+text, so we should hide the text is OngoingActivityChipModel.Shown.Timer, is OngoingActivityChipModel.Shown.ShortTimeDelta, is OngoingActivityChipModel.Shown.Text -> InternalChipModel.Shown(this.type, this.model.toIconOnly()) } } private fun OngoingActivityChipModel.Shown.toIconOnly(): OngoingActivityChipModel.Shown { // If this chip doesn't have an icon, then it only has text and we should continue showing // its text. (This is theoretically impossible because // [OngoingActivityChipModel.Shown.Countdown] is the only chip without an icon, but protect // against it just in case.) val currentIcon = icon ?: return this return OngoingActivityChipModel.Shown.IconOnly( currentIcon, colors, onClickListenerLegacy, clickBehavior, ) } /** * A flow modeling the primary chip that should be shown in the status bar after accounting for Loading Loading @@ -327,6 +405,9 @@ constructor( } } private val Configuration.isLandscape: Boolean get() = orientation == Configuration.ORIENTATION_LANDSCAPE companion object { private val TAG = "ChipsViewModel".pad() Loading
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import com.android.systemui.biometrics.domain.interactor.displayStateInteractor import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel Loading @@ -34,6 +36,8 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel, callChipViewModel = callChipViewModel, notifChipsViewModel = notifChipsViewModel, displayStateInteractor = displayStateInteractor, configurationInteractor = configurationInteractor, logger = statusBarChipsLogger, ) }